ใน Windows Domain Environment เมื่อเครื่องที่อยู่ใน domain ต้องการสื่อสารกันจะใช้ Kerberos เป็น Protocol หลักในการทำ Authentication หากไม่สามารถใช้ได้จะหนีไปใช้ NTLMv2 แทน (สามารถเรียกอีกอย่างได้ว่า Net-NTLMv2*)
สำหรับ Kerberos เองเป็น Network Authentication Protocol ที่อาศัยการใช้งานของ “ticket” เป็นหลัก มีหน้าที่พิสูจน์ตัวตนว่าต่างฝ่ายต่างเป็นคนคนนั้น หรือ entity ที่จะสื่อสารด้วยตัวจริง สามารถใช้งานผ่าน channel ที่ไม่ secure ก็ได้ แต่มีข้อแม้ว่า entity ทั้งสองที่จะคุยกันต้องเชื่อตัวกลาง (trust party) ซึ่งก็คือ Kerberos Server นั่นเอง โดยเจ้า trust party ในโลกของ Windows Domain ก็คือเครื่อง Domain Controller Server นั่นเอง
Component ที่ต้องรู้จักใน Kerberos ประกอบด้วย
อ่านมาถึงนี่แล้วขอสรุปสั้น ๆ ก่อนที่จะไปต่อ
1) Kerberos เป็น Authentication Protocol ไม่ได้ทำหน้าที่ Authorisation
2) มี 3 party คือ KDC, client และ service
3) ใช้งานบน insecure network ก็ได้ เหนือกว่า NTLMv2 เพราะถูก capture ไปก็ทำอะไรไม่ได้ ไม่เหมือน NTLMv2 ที่อาจโดน capture ค่า hash ที่คุยระหว่างกัน โดยใน Windows environment นั้น Kerberos ใช้งานผ่าน TCP/88 และ UDP/88
ลำดับต่อไปจะอธิบายถึง Authentication Flow ซึ่งข้อมูลที่รับส่งระหว่าง entity จะขออธิบายเฉพาะค่าที่จำเป็นหรือค่าที่อยากให้สนใจเท่านั้น แน่นอนว่ามีค่าหรือตัวแปรอื่น ๆ อีกพอสมควรซึ่งไม่ขอกล่าวถึง ในช่วงท้ายของบทความจะทิ้งท้ายด้วย Reference ที่สามารถไปศึกษาต่อได้
เมื่อ client ต้องการ authenticate เข้าไปยัง domain จะต้องส่ง request ไปยัง Authentication Server ก่อน (KDC หรือ Domain Controller รับหน้าที่เป็น authentication server หรือ AS ด้วย) โดยจะส่ง username และ cipher text ที่ได้จากการนำ timestamp ไป encrypt ด้วยค่า hash ของ password ของผู้ใช้งาน ตามรูปคือ
username + enc(timestamp,hash_of_password) |
เพิ่มเติมอีกนิดเพื่อความเข้าใจ
ใน message จะมีการส่ง SPN ของ krbtgt (ชี้ Service Principal Name ไปยัง krbtgt ที่เป็น account ที่รับผิดชอบเรื่อง ticket ทั้งหลายใน Kerberos Ecosystem โดย 1 domain จะมี 1 krbtgt account) และ nonce ฝั่ง client ด้วย
ที่ต้อง encrypt ค่า timestamp เพราะเป็นกระบวนการของ pre-authentication ซึ่งโดย default แล้วจะบังคับให้ทำ
เมื่อ AS ได้รับ message มาแล้ว (Domain Controller ได้รับ) จะไปดึงข้อมูล password ที่เก็บในรูปแบบ hash ของ username คนนั้น ที่ส่งมา นำมา decrypt ค่า cipher text หากทำได้สำเร็จ client จะได้รับ TGT (ticket-granting ticket)
โดย message ที่ตอบกลับประกอบด้วย
username + enc([username,period,session_key,PAC], krbtgt_key) + enc([session_key, period, nonce], hash_of_password) |
มาถึง step นี้หลังจาก client ได้รับค่า message ชุดนี้ แสดงว่า client ผ่าน authentication process แล้ว จากนี้จะไปแงะเอาค่า session_key ไปใช้คุยกับ KDC ในห้วงเวลาถัดไป
ขยายความค่า PAC
สำหรับค่า PAC มีความสำคัญมากเพราะเป็นค่าที่กำหนด privilege ของผู้ใช้งาน โดยถ้ามองโครงสร้างข้างในจะขออธิบายด้วย slide จากหัวข้อ “Abusing Microsoft Kerberos sorry you guys don’t get it” by Alva `Skip` DUCKWALL & Benjamin DELPY (น่าจะคุ้นหูคุ้นตากันมาบ้างนะครับเพราะ Benjamin DELPY คือผู้สร้าง mimikatz นั่นเอง)
1) ใน PAC จะมีข้อมูลสำหรับ account นั้น ๆ ซึ่งค่านี้มีความสำคัญสุด ๆ จึงถูก sign ด้วย target key และ KDC key สำหรับ target key ใน step ของการ request TGT นั้นจะใช้ krbtgt NT hash ซึ่งก็คือ KDC key ตัวเดียวกัน (target หมายถึง entity ที่กำลังคุยด้วย)
2) client ไม่สามารถแงะเพื่อดูของใน TGT ได้ เอาไว้ส่งให้ KDC ดูในอนาคตว่า client มี TGT แล้วนะ
เมื่อ client ต้องการใช้บริการของ service ที่ต้องการ ก็จะทำการร้องขอ TGS (ticket-granting service) จาก KDC โดยจะส่ง
TGT + SPN of target service + enc([username,timestamp],session_key) .. มี nonce ด้วยนะ |
KDC จะเปรียบเทียบ content ใน authenticator กับของที่แงะออกมาจาก TGT ถ้าสอดคล้องกัน แสดงว่า client คือคนที่เคยผ่าน authentication มาแล้ว จากนั้นจะส่ง message กลับ 2 ส่วนคือ
client portion: enc( [service_session_key, TGS period, nonce], session_key) + server portion: TGS:enc([username,service_session_key,TGS period,PAC],target_key) |
เมื่อ client รับ message ก็จะแงะเอา service_session_key มาใช้ และได้ TGS เอาไปแสดงให้กับ service ที่กำลังจะร้องขอต่อไป
เมื่อ client พร้อมใช้บริการ service ก็จะส่ง
TGS + authenticator:enc(username+timestamp,service_session_key) |
ไปหา target service
service ทำการเปรียบเทียบ content ใน TGS และ authenticator ว่าสอดคล้องกันหรือไม่ ถ้าใช่ก็จะตอบ message กลับ
enc([timestamp],service_session_key) |
โดยการนำ timestamp ที่แงะออกมาได้ เอามา encrypt ด้วย service_session_key หลังจาก client ได้รับก็จะแงะและเปรียบเทียบ timestamp ว่าตรงกันหรือไม่ ถ้าตรงกัน client ก็จะมั่นใจว่า service ที่กำลังคุยอยู่คือตัวจริง แล้ว connection ก็เริ่มต้นขึ้นหลังจากนี้
Remark
*Kerberos เจ๋งกว่า NTLMv2 เพราะไม่ต้องส่งหรือเก็บ password อีกทั้ง performance ดีกว่า ในแง่ของความยืดหยุ่น ถ้าเราใช้ Kerberos เราจะมี concept ของ Delegation ที่ impersonate เป็น entity อื่นได้
*ขอเคลียให้ชัดก่อนทุกคนจะได้เข้าใจ ใน Windows Environment จะพบเรื่องพวกนี้ตลอด
ดังนั้นความหมายและ Notation ที่เขียนออกมาอาจจะทำให้สับสนได้ Professional Penetration Tester ที่ดีควรต้องเข้าใจและสามารถอธิบายให้คนอื่นเข้าใจถึงความแตกต่างได้ด้วย