October 31, 2026
Behind the Kerberos Attacks Part 1 — Kerberos Authentication Flow
Introduction

By Pawut Jingjit
6 min read
Introduction
- ถ้าเพื่อนๆ เคยซ้อมโจทย์
Active Directory Labsมาบ้าง น่าจะเคยใช้AS-REP Roasting,Kerberoastingมาบ้าง แต่เพื่อนๆ เคยสงสัยไหมว่า เบื้องหลังการโจมตีพวกนี้ มันทำงานอย่างไร - อย่าง
Kerberoastingทำไมถึงสามารถดึงcrack offline passwordได้แค่บางaccount, ทำไม ได้hash passwordมา เราสามารถloginด้วยhashนั้นได้เลย โดยไม่ต้องผ่านการcrackก่อน (Pass-the-Hash) หรือ ทำไม "เวลาไม่ตรงกัน" เป็นปัญหาใหญ่ในAD - จึงได้เป็นบทความชุด Behind the Kerberos Attacks ครับ
- เริ่มจาก Part1 — Kerberos Authentication Flow เป็นการปูว่า จริงๆ Kerberos ทำงานอย่างไรครับ
alice ต้องการใช้ File Share (cifs) ใน domain โดยมีเงื่อนไขว่า Network ไม่ปลอดภัย ถูกดักฟังได้ตลอด
alice จะพิสูจน์ตัวตนได้อย่างไร โดยไม่ต้องส่ง password ออกไปเลยสักครั้ง
{data}Kหมายถึง "data" ที่ถูก encrypt ด้วย keyKใครที่จะอ่านข้างในได้ ต้องมี keyKเท่านั้น
Kerberos Authentication Flow
- AS-REQ / AS-REP alice พิสูจน์ตัวตนกับ KDC เพื่อขอ TGT
- TGS-REQ / TGS-REP alice ใช้ TGT ขอ Service Ticket สำหรับ service ที่ต้องการ
- AP-REQ / AP-REP alice ยื่น Service Ticket ให้ service ปลายทาง
AS-Request (Authentication Server Request)
AS-REQ: alice encrypt Timestamp ด้วย Kc แล้วส่งให้ KDC ถ้า KDC ไขได้ แสดงว่าเป็น alice จริง
AS-REQ
├─ cname : alice
├─ sname : krbtgt/CORP.LOCAL
├─ nonce : 8812 (random on client)
└─ padata : {timestamp}KcAS-REQ
├─ cname : alice
├─ sname : krbtgt/CORP.LOCAL
├─ nonce : 8812 (random on client)
└─ padata : {timestamp}Kccname: ชื่อ user ที่ขอTGTkrbtgt:Kerberos Ticket-Granting Ticketเป็น service ที่ออก TGTpadata:pre-authentication dataTimestamp ที่เข้ารหัส (Encrypt) ด้วยKcKc:client long-term keyได้มาจากการนำ password ของ user ผ่านhash function- แน่นอนว่า การขอ
Ticketต้องระบุว่า user ที่ขอคือใคร - ท้ายที่สุดแล้ว
TGSก็เป็น service หนึ่ง จึงต้องแจ้งชื่อ service เป็นkrbtgt - ที่น่าสนใจคือ
padataว่าทำไมต้องใช้ Timestamp? แล้วจริงๆKcคืออะไร
Password in KDC
ก่อนที่จะไปถึง Kc อยากจะให้เพื่อนๆ เข้าใจระบบ Password ของ KDC ก่อนนะครับ
- แน่นอนว่า
Kerberosจะไม่เก็บ Password เป็นplain textแต่จะเก็บuser passwordที่ผ่านhash functionแทน — ในกรณีทั่วไปจะเป็นMD4 - เราจะเรียก
user passwordที่ผ่านMD4 hash functionว่าNT hash - ว่ากันตามตรง
MD4 hash functionไม่ถือเป็นมาตรฐานที่ปลอดภัยแล้ว แต่หลายๆ ระบบ รวมถึง Labs ที่เพื่อนๆ มักซ้อมกัน ยังใช้เป็นNT hashอยู่ - ปัจจุบันใช้เป็น
PBKDF2 hash functionโดยเรียก user password ที่ผ่านhash functionนี้ว่าAES key
NT hash(รวมไปถึงAES key) คือKcที่เอาไปใช้ encrypt
password → MD4 → NT hash ─┐
├─► Kc
password → PBKDF2 → AES key ─┘password → MD4 → NT hash ─┐
├─► Kc
password → PBKDF2 → AES key ─┘เกร็ด ระวังสับสน
NT hashกับNTLMซึ่งเป็นprotocolการauthenticateก่อนมีการใช้Kerberos
AS-Request (Authentication Server Request) (continue)
- เราทราบแล้วว่า
Kcคือ Key ที่ได้จาก user password ที่ผ่านhash functionอย่างMD4 - แล้วทำไมต้องใช้ Timestamp ที่ encrypt ด้วย Kc? เป็นค่าอื่นไม่ได้หรือ?
- เมื่อเพื่อนๆ พิจารณาจาก AS-Req จะเห็นว่า เราส่ง
aliceซึ่งเป็นค่าที่แก้ไขได้KDCเองจะไม่รู้เลยว่าaliceนั้น มาจากaliceจริงหรือไม่ aliceต้องพิสูจน์ได้ว่าเป็นตัวจริง โดยการบอกสิ่งที่มีเพียงaliceตัวจริงเท่านั้นที่รู้ คือpassword- เพื่อนๆ ที่มาจากสาย Developer อาจสงสัยว่า แล้วส่ง
passwordไปยืนยันตรงๆ ไม่ได้หรือ?Login formปกติก็ส่งpasswordกันตรงๆ - เมื่อเพื่อนๆ ส่ง
passwordผ่านbrowserจะมีHTTPS(TLS)ป้องกันไม่ให้สามารถ "ดัก"passwordกลางทางได้ Kerberosออกแบบตั้งแต่ยุคที่ยังไม่มีHTTPSจึงต้องสันนิษฐานไว้ก่อนว่าNetworkไม่ปลอดภัย ถูก "ดักฟัง" ได้เสมอ
แทนที่จะส่ง password(รวมถึง hash) ไปตรงๆ KDCจะ "ทดสอบ" ว่า
ลองล็อกกุญแจ(Encrypt) ด้วยกุญแจ(Password) ให้ดูหน่อย ถ้าเป็น Alice ตัวจริง ต้อง "ล็อกกุญแจ" ได้
- ถ้า
KDCสามารถ Decrypt ด้วยNT Hashที่KDCเก็บไว้KDCก็มั่นใจได้ว่า alice ใช้ "กุญแจดอกเดียวกัน" หมายถึงเป็น Alice ตัวจริง
ทำไมต้อง Timestamp
- เราทราบว่า ทำไมต้อง
Encryptด้วยNT hashแต่ทำไมKDCต้องให้เราEncryptTimestamp - ถ้าหากว่า
KDCให้ลองEncryptusernameแทน สิ่งที่เกิดขึ้นคือ "ผู้โจมตี สามารถดักเอาAS-Request" ไปส่งได้ โดยไม่ต้องEncryptเองเลย (Replay Attack) - สิ่งที่
Encryptต้องมีคุณสมบัติสำคัญคือ "แยกใหม่-เก่าได้" ถ้าจะพิสูจน์ตัวตนว่ารู้Kcต้องEncryptใหม่เท่านั้น - Timestamp เป็นสิ่งที่ตอบโจทย์พอดี ถ้าถูกดัก
AS-RequestKDCจะทราบว่า "Timestamp นั้น ห่างจากปัจจุบันมากไหม(มาตรฐานคือ 5 นาที)" - จึงเป็นเหตุผลว่า ทำไมถ้าเพื่อนๆ ทำ Labs AD แล้วเวลาเครื่องไม่ตรง มักจะเจอปัญหา
ถ้าเพื่อนๆ เข้าใจว่า Password ใน
KDCไม่ได้ใช้เทียบตรงๆ แต่ใช้เพื่อพิสูจน์ว่าaliceสามารถEncryptได้จริงไหม ถือว่าเข้าใจ Key concept ของ AS-Request แล้วครับ
AS-Response (Authentication Server Response)
AS-Response: KDC ส่ง {alice, SK_tgs}Kkrbtgt (TGT) กลับมาคู่กับ {Sk_tgs}Kc
User เก็บ TGT ไว้โดย decryptไม่ได้ และ decrypt {Sk_tgs}Kc แล้วเก็บ Sk_tgs ไว้
AS-REP
├─ cname : alice
├─ ticket : {alice, SK_tgs, endtime, PAC}Kkrbtgt ← TGT
└─ enc-part : {SK_tgs, endtime, nonce}KcAS-REP
├─ cname : alice
├─ ticket : {alice, SK_tgs, endtime, PAC}Kkrbtgt ← TGT
└─ enc-part : {SK_tgs, endtime, nonce}Kccname: ชื่อ user ที่ได้รับTGTticket:TGTที่เข้ารหัส (Encrypt) ด้วยKkrbtgtuser ถือไว้ได้แต่อ่านหรือแก้ไขข้างในไม่ได้TGT: ย่อมาจากTicket Granting Ticketenc-part:encrypted partส่วนที่เข้ารหัส (Encrypt) ด้วยKcมีเพียงuserตัวจริงเท่านั้นที่ถอดได้Kkrbtgt:krbtgt long-term keyได้มาจาก password ของaccountkrbtgtมีเพียงKDCเท่านั้นที่รู้SK_tgs:session key ticket granting serviceที่KDCสุ่มขึ้นมาใหม่ ใช้แทนKcในขั้นตอนต่อไปendtime: เวลาหมดอายุของTGT(default ใน AD คือ 10 ชั่วโมง)
krbtgt คืออะไร
krbtgt(Kerberos Ticket-Granting Ticket) ถือเป็น Service ซึ่ง Service ในADถือว่าเสมือนaccountหนึ่ง ซึ่งมีทั้งusername,passwordKkrbtgtจึงเป็นlong-term keyของ userkrbtgtที่ derive มาจาก password ของkrbtgtkrbtgt accountถูกสร้างมาพร้อมdomainและถูกสุ่มpasswordฉะนั้นKkrbtgtจึงมีเพียงKDCที่รู้
SK_tgs คืออะไร
SK_tgssession key ticket granting serviceคือ key ชั่วคราว ที่ KDC สุ่มขึ้นใหม่ ทุกครั้งที่ออก TGT ใช้แทนKcในขั้นตอนต่อไป เพื่อไม่ต้องใช้ key ที่มาจาก password ของ user ซ้ำบ่อย ๆ- หมายความว่า แทนที่
userจะพิมพ์passwordซ้ำ จะใช้SK_tgsแทน - เก็บที่
clientเท่านั้น โดยเก็บคู่กับTGT
เกร็ด — nonce กันไม่ให้ client ถูกหลอกให้ใช้ reply เก่าที่ session key อาจรั่วไปแล้ว ซึ่งสำคัญมากในยุค DES ที่ key crack ได้ แต่ในยุค AES แทบไม่ใช่ช่องทางโจมตีในทางปฏิบัติแล้ว
AS-Response (Authentication Server Response) (ต่อ)
userไม่ทราบKkrbtgtจึงไม่สามารถอ่านTGTได้ จึงเก็บTGTไว้โดยไม่decryptenc-partuserสามารถdecryptได้ เพราะมีKcจึงเก็บSK_tgsไว้คู่กับTGT- สังเกตว่า ต่อให้ดัก AS-Response มา ก็ไม่สามารถ
decryptSK_tgsมาใช้ได้
TGT มีเพียง KDC ที่อ่านได้ ใช้เพื่อขอ Service Ticket SK_tgs ใช้พิสูจน์ตัวตน User แทน Kc
TGS-Request (Ticket-Granting Service Request)
TGS-Request: user ส่ง authenticator ({alice, timestamp}Sk_tgs) คู่กับ TGT
KDC decrypt TGT ด้วย Kkrbtgt และได้ Sk_tgs มา ถ้าสามารถ decrypt authenticator ได้ แสดงว่าเป็น alice ตัวจริง
TGS-REQ
├─ sname : cifs
├─ ticket : {alice, SK_tgs, endtime}Kkrbtgt ← TGT
├─ nonce : 5307
└─ authenticator : {alice, timestamp}SK_tgs
TGS-REQ
├─ sname : cifs
├─ ticket : {alice, SK_tgs, endtime}Kkrbtgt ← TGT
├─ nonce : 5307
└─ authenticator : {alice, timestamp}SK_tgs
sname:service nameที่ user ต้องการใช้บริการ ในที่นี้คือcifs(file share)ticket:TGTที่ได้จากAS-Responseauthenticator:usernameและ timestamp ที่Encryptด้วยSK_tgsSK_tgs:userได้มาจากenc-partของAS-Response- สังเกตว่า
SK_tgsมีเพียงuserตัวจริงที่decryptได้ เพราะทราบKc - หลังจากนี้ user ไม่จำเป็นต้องพิมพ์
passwordใหม่ เพื่อที่จะขอใช้serviceใดๆแล้ว เพราะใช้SK_tgsยืนยันตัวตนแทน (จนกระทั่งTGTexpire)
KDC ตรวจ TGS-Request อย่างไร
KDCเริ่มจากdecryptTGTด้วยKkrbtgtที่มีเพียงKDCรู้ —KDCจะได้SK_tgs,usernameของ user ที่ต้องการใช้บริการserviceKDCนำSK_tgsจากขั้นตอนที่แล้ว มา decryptauthenticatorเพื่อตรวจสอบว่าuserในTGTเป็นuserเดียวกับuserในauthenticatorหรือไม่
TGS-Reply (Ticket-Granting Service Reply)
KDC ออก Service Ticket คู่กับ enc-part ({SK_svc, endtime}Sk_tgs)
alice เก็บ Service Ticket ไว้ และ decrypt enc-part ด้วย Sk_tgs และเก็บ SK_svc ไว้
TGS-REP
├─ cname : alice
├─ ticket : {alice, SK_svc, PAC, endtime}Ks ← Service Ticket
└─ enc-part : {SK_svc, endtime, nonce}SK_tgsTGS-REP
├─ cname : alice
├─ ticket : {alice, SK_svc, PAC, endtime}Ks ← Service Ticket
└─ enc-part : {SK_svc, endtime, nonce}SK_tgscname: ชื่อ user ที่ได้รับ service ticketticket: Service Ticket ที่เข้ารหัส (Encrypt) ด้วยKsuser ถือไว้ได้แต่อ่านหรือแก้ไขข้างในไม่ได้ มีเพียง service ปลายทางที่ถอดได้enc-part: encrypted part ที่เข้ารหัสด้วยSK_tgs—userที่มีSK_tgsถอดได้Ks:service long-term keyได้มาจากpasswordของaccountที่เป็นเจ้าของSPN(Service principle name)มีเพียงKDCและserviceนั้นที่รู้SK_svc:session keyตัวใหม่ที่KDCสุ่มขึ้น ใช้ระหว่างuserกับserviceปลายทางPAC:Privilege Attribute Certificateข้อมูลสิทธิ์ของuserเช่นSIDและgroup- ทำแค่ ครั้งแรกต่อ service หลังจากนั้น client ใช้ตั๋วใน cache ได้จนกว่าตั๋วจะหมดอายุ
Ks คืออะไร
- เช่นเดียวกับ
krbtgt— ทุกserviceถือว่าเป็นaccountหนึ่ง จึงมีpasswordของตัวเอง ซึ่งมีเพียงKDCและserviceนั้น ที่รู้ - ในเมื่อมีเพียง
KDCและserviceที่รู้passwordหมายความว่า มีเพียงserviceปลายทางเท่านั้น ที่เปิดService Ticketได้ - และเป็นการรับประกันว่า
Service TicketถูกออกโดยKDCจริง
PAC คืออะไร
- เป็นข้อมูลสิทธิของ
userเช่นSID,group - เพราะว่า
TGSจะออกService Ticketให้เสมอ โดยไม่สนใจว่าuserนั้นมีสิทธิใน Service นั้นหรือไม่ PACเป็นข้อมูลให้Serviceพิจารณาว่าuserนั้น มีสิทธิในServiceนั้นจริงไหม
Sk_svc คืออะไร
session keyตัวใหม่ที่KDCสุ่มขึ้น ใช้ระหว่างuserกับserviceปลายทาง- ใช้เพื่อพิสูจน์กับ
Serviceว่า เป็น user ตัวจริง ที่มีSk_tgsถึงจะสามารถdecryptมาอ่านได้ Sk_tgs,SK_svcทำหน้าที่เดียวกัน คือ พิสูจน์ว่า เป็น key ที่นำมาใช้แทนKc,Ks- ต่างกันที่
Sk_tgsจริงๆ สามารถใช้Kcแทนได้เสมอ แต่อาจทำให้ user จำเป็นต้องพิมพ์passwordบ่อยๆ - เพื่อนๆ คงสงสัยว่า ใช้
Ksเพื่อพิสูจน์กับ Service แทนไม่ได้หรือ? - สามารถทำได้ในทางทฤษฎ ีแต่จะทำให้
userรู้Ks - ถ้า "user รู้
Ks" หมายความว่า user สามารถ "ออก Ticket ปลอม" เป็นชื่อใครก็ได้ สิทธิเท่าไหร่ก็ได้ เสมอ
AP-Request (Application Request)
AP-REQ:_ alice ส่ง Service Ticket คู่กับ authenticator ที่ encrypt ด้วย SK_svc_
service จะ decrypt ticket ด้วย Ks ของตัวเองได้ Sk_svc
service นำ Sk_svc ไป decrypt authenticator ถ้าได้ แสดงว่าเป็น alice ตัวจริง
service ตรวจสอบ PAC ว่า alice มีสิทธิใน Service นี้จริงหรือไม่
AP-REQ
├─ ticket : {alice, Sk_svc, PAC, endtime}Ks ← Service Ticket
└─ authenticator : {alice, timestamp}Sk_svcAP-REQ
├─ ticket : {alice, Sk_svc, PAC, endtime}Ks ← Service Ticket
└─ authenticator : {alice, timestamp}Sk_svcticket:Service Ticketที่ได้จากTGS-Responseส่งต่อให้serviceauthenticator:usernameและ timestamp(เวลาฝั่ง client) ที่Encryptด้วยSk_svcSk_svc:userได้มาจากenc-partของTGS-Response
AP-Response (Application Reply)
AP-REP:_ service ส่ง {_timestamp}Sk_svc ที่ได้จาก authenticator กลับไป
alice decrypt enc-part ด้วย Sk_svc และตรวจว่า timestamp ตรงกันกับ timestamp ที่ alice เก็บไว้ไหม
หลังจากนี้ alice, service จะใช้ Sk_svc ในการ encrypt, decrypt message หากัน
AP-REP
└─ enc-part : {timestamp}SK_svcAP-REP
└─ enc-part : {timestamp}SK_svcเกร็ด — ทำไมต้องส่ง
timestampกลับมา จริงๆ ในขา AP-Response timestamp ใช้แทนnonce
Epilogue
- ก่อนนี้ ช่วงที่ทำโจทย์
ADเจ้าของบทความชอบrun commandอย่างเดียวเลย ซึ่งว่ากันตามตรง มันก็พอที่จะทำHTB-Labระดับeasyได้นั่นละครับ - แต่เจ้าของบทความคิดว่า การทำโจทย์โดยที่ไม่เข้าใจ Mechanic เลย อาจจะเป็นการเรียนที่ผิดวิธีไปนิดหนึ่ง
- แล้วเจ้าของบทความมีโอกาสได้อ่าน web novel ยุค 80 เรื่องหนึ่งมา ชื่อว่า "Designing an Authentication System: a Dialogue in Four Scenes"
- ซึ่งประทับใจมากครับ เป็น web novel ที่ Athena และ Euripides ถกเถียงกัน เรื่อง "ระบบยืนยันตัวตนบน network ตั้งแต่ 0 จนเป็น Charon(Kerberos)ที่ใช้กันถึงทุกวันนี้"
- ตอนแรก บทความนี้จะเป็นสรุปเรื่องที่ Athena คุยกับ Euripides นั่นละครับ แต่รู้สึกว่า "อรรถรส" น่าจะหายไปเยอะเลย ก็แนะนำให้เพื่อนๆ ลองอ่านดูนะครับ ส่วนตัวชอบที่ "Euripides นั่งหาปัญหามาให้ Athena แก้เรื่อยๆ" มากครับ
- ขอบคุณเพื่อนๆ ที่อ่านมาจนถึงตรงนี้นะครับ เจอกันใหม่ในบทความหน้าครับ
Reference
- Bill Bryant (1988), Designing an Authentication System: a Dialogue in Four Scenes