Hacking, Coding and Gaming | @[email protected]

After attending a great talk by @marcoslaviero about AWS cloud security and persisting your access through AWS Lambda and EC2, I realised there was another nasty way to "inject" malicious code in to an account and execute it on demand (or have it executed for you)... by inserting it as a "version" of an existing Lambda function, and restoring that Lambda's functionality. Again, this assumes you have already compromised the AWS account.

Given an existing Lambda function (eg: "hypn-lambda-mischief" below) :

Step 1 - getting the current/original code: We can fetch the current Lambda code through AWS CLI with the command:

aws lambda get-function --function-name=_hypn-lambda-mischief_ --qualifier=\$LATEST

While we could just copy and paste the code above from the web interface, more complex Lambda functions with dependencies would likely have been uploaded as a .zip file and wont be viewable in the web interface.

The response (if the command is successful) will contain a "Location" property which is a link to download the code (as a .zip file). The CLI response should look something like this:

{
    "Code": {
        "RepositoryType": "S3",
        "Location": "https://awslambda-eu-west-1-tasks.s3-eu-west-1.amazonaws.com/snapshots/REDACTED/hypn-lambda-mischief-REDACTED-REDACTED-REDACTED-REDACTED-REDACTED?X-Amz-Security-Token=REDACTED-REDACTED-REDACTED-&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20170823T212605Z&X-Amz-SignedHeaders=host&X-Amz-Expires=600&X-Amz-Credential=REDACTED&X-Amz-Signature=REDACTED"
    },
    "Configuration": {
        "TracingConfig": {
            "Mode": "PassThrough"
        },
        "Version": "$LATEST",
        "CodeSha256": "REDACTED-REDACTED-REDACTED-REDACTED=",
        "FunctionName": "hypn-lambda-mischief",
        "MemorySize": 128,
        "CodeSize": 216,
        "FunctionArn": "arn:aws:lambda:eu-west-1:REDACTED:function:hypn-lambda-mischief:$LATEST",
        "Handler": "index.handler",
        "Role": "arn:aws:iam::REDACTED:role/REDACTED",
        "Timeout": 3,
        "LastModified": "2017-08-23T21:19:03.735+0000",
        "Runtime": "nodejs6.10",
        "Description": ""
    }
}

Download the "Location" URL  (a zip file).

Step 2 - injecting malicious code: Now that we have a copy of the "real" code, we can replace the Lambda function with our own malicious code (either using the editor in the web interface or choosing to upload a .zip file containing it) :

or:

Click Save/Upload.

Step 3 - persisting and hiding the injected coded: With the malicious code in place, click on "Actions" then "Publish new version" which will make a snapshot of our code (which we will use later) :

You'll be prompted for a description for the version, which you can leave empty... or if there are other versions with descriptions you could use them as inspiration for a description that will blend in.

Step 4 - restoring original functionality: Replace the Lambda function code with the "real" code (either copying+pasting the code from the .zip back in to the web-interface or uploading the zip file as per step 2 above - if your malicious code was uploaded as a .zip file you'll likely only have this option).

And after saving you should see 2 (or more) version under the "Qualifiers" -> "Versions" drop down list... $LATEST always refers to the latest code.

Step 5 - execution: We can now run our malicious code through the AWS CLI by specifying the function name and version ("qualifier") :

# aws lambda invoke --function-name=hypn-lambda-mischief --qualifier=1 /dev/stdout
"The Call of Cthulhu"

Leaving out our version number, the original code should be executed:

# aws lambda invoke --function-name=hypn-lambda-mischief /dev/stdout
"Hello from Lambda"

Depending on the permissions that are in place, it should be possible to create an "alias" for the malicious function version and modify (or create) triggers - such as an AWS Gateway endpoint - to call the malicious lambda version, which could then call the $LATEST version of the same lambda function (or a "dev" or "prod" alias that might exist)... which would cause Lambda and trigger to behave as expected, and our malicious code. Some work (and permission granting) is required for this (at least in the case of AWS Gateway).

Bonus fun: It's also possible to upload up to 50mb of binary data as a .zip to a Lambda function (though it obviously wont be executable), version it, and retrieve it elsewhere.