เรื่องที่ผมทำวันหยุดพังด้วยการรีเวิร์สเอนจิเนียร์ WSC
(blog.es3n1n.eu)- defendnot ที่สร้างขึ้นระหว่างทริปโซลเป็นเครื่องมือที่พยายามปิดใช้งาน Windows Defender ด้วยการเรียกใช้ API ของบริการ Windows Security Center (WSC) โดยตรง และนำไปสู่การรีเวิร์สเอนจิเนียร์อยู่หลายวัน
- เครื่องมือก่อนหน้าอย่าง no-defender ใช้วิธีนำโค้ดของแอนติไวรัสที่มีอยู่มาลงทะเบียนกับ WSC ว่ามีแอนติไวรัสตัวอื่นอยู่ หลังจากได้ดาวบน GitHub ราว 1.5k ก็ถูกลบออกตามคำขอ DMCA จากผู้พัฒนาแอนติไวรัส
- ตอนแรกสามารถทำซ้ำการเรียก COM API ของ WSC ได้ในเวลาประมาณ 1 ชั่วโมง แต่เมื่อ WSC ตรวจสอบโปรเซสที่เรียกใช้ โปรเซสทั่วไปจึงถูกปฏิเสธการเข้าถึง
- จากการวิเคราะห์พบว่า WSC ตรวจสอบ WinDefend SID, สิทธิ์ผู้ดูแลระบบ, ลายเซ็น และแฟล็ก
ForceIntegrityในDllCharacteristicsของ PE และการใช้งานขั้นสุดท้ายใช้Taskmgr.exe - ในสภาพแวดล้อมที่ยุ่งยากทั้ง MacBook arm64, พีซีรีโมตในสหรัฐฯ, ความหน่วง Parsec 210ms และ VM ของ Shadow.tech ผู้เขียนแก้ปัญหา path ของ
ctx.binและเงื่อนไขพลังงานของ autorun จนทำงานสำเร็จ
ที่มาของการสร้าง defendnot
- defendnot เป็นเครื่องมือที่ใช้ API ของบริการ Windows Security Center โดยตรงเพื่อ ปิดใช้งาน Windows Defender
- แก่นของกระบวนการพัฒนาไม่ได้อยู่ที่การเรียก WSC เอง แต่อยู่ที่การค่อย ๆ อ้อมผ่านเงื่อนไขตรวจสอบและสภาพแวดล้อมการทำงานที่ไม่สะดวก
- เอกสารทางเทคนิคเกี่ยวกับ WSC ที่ละเอียดกว่านี้จะมีคนอื่นเผยแพร่ในภายหลัง
no-defender และ DMCA เมื่อ 1 ปีก่อน
- no-defender ที่เผยแพร่เมื่อประมาณ 1 ปีก่อน ใช้ Windows API สำหรับแอนติไวรัสเพื่อปิด Windows Defender
- API นี้มีไว้สำหรับแจ้งระบบว่า “มีแอนติไวรัสตัวอื่นอยู่แล้ว จึงไม่จำเป็นต้องรันการสแกนของ Defender”
- คอมโพเนนต์ของระบบที่ดูแลส่วนนี้คือ Windows Security Center (WSC)
- no-defender ทำงานโดยใช้โค้ด third-party ของแอนติไวรัสที่มีอยู่ เพื่อบังคับให้แอนติไวรัสนั้นถูกลงทะเบียนกับ WSC
- หลังเผยแพร่ได้ไม่กี่สัปดาห์ก็ได้ประมาณ 1.5k stars และเมื่อผู้พัฒนาแอนติไวรัสที่ถูกใช้ยื่นคำขอ DMCA takedown ก็ลบเนื้อหาใน repository แล้วจบเรื่อง
การวิเคราะห์ WSC ที่เริ่มขึ้นอีกครั้งระหว่างทริปโซล
- ระหว่างพักอยู่ใน Airbnb ที่โซล MrBruh ส่งข้อความมาว่าเขากำลังดู no-defender และสำรวจว่าจะทำ implementation แบบ “สะอาด” โดยไม่ใช้ไบนารีของแอนติไวรัสได้หรือไม่
- ปกติผู้เขียนจะพกโน้ตบุ๊ก x86 แยกไว้สำหรับ CTF หรือการ reverse x86 แต่ทริปนี้พกมาแค่ M4Pro MacBook
- แม้อยู่ในสภาพเสียเปรียบเพราะไม่มีเครื่องสำหรับ reverse x86 ก็เริ่มวิเคราะห์ WSC โดยใช้ช่วงเวลาก่อนเพื่อน ๆ ตื่น
Day 1: ทำซ้ำ WSC COM API และการถูกปฏิเสธการเข้าถึงครั้งแรก
- MrBruh ส่งไบนารี WSC เวอร์ชันล่าสุดมาให้ และใช้ implementation การลงทะเบียน WSC ของแอนติไวรัสที่เคยใช้ก่อนหน้านี้เป็นตัวอย่างอ้างอิง
- WSC มี COM API ที่แอนติไวรัสต่าง ๆ ใช้ และสามารถทำซ้ำการเรียกแบบที่แอนติไวรัสเดิมทำได้ในเวลาประมาณ 1 ชั่วโมง
- ทดสอบโดยบูต Windows arm64 บน Parallels แต่ผลคือ access denied
- จากประสบการณ์เดิมรู้ว่า WSC ตรวจสอบโปรเซสที่เรียก API และตอนแรกสงสัยว่าอาจเป็นการตรวจสอบลายเซ็น
- เมื่อ inject โค้ดเข้าไปในโปรเซสที่แอนติไวรัสเดิมใช้สำหรับงาน WSC การลงทะเบียนแอนติไวรัสใหม่และการเรียก COM เพื่ออัปเดตสถานะก็ทำงานได้ปกติ
ความพยายามลบไบนารีของแอนติไวรัสออก
- เพื่อไม่ให้โปรเจกต์ใหม่ถูกผู้ผลิตแอนติไวรัสรายเดิมร้องเรียนอีก จึงพยายามใช้ไบนารีที่ระบบมีให้แทนไบนารีของแอนติไวรัส
- เลือก
cmd.exeเป็นโปรเซสเป้าหมายแรก แต่การเรียก WSC API ถูกปฏิเสธ - เมื่อดู
wscsvc.dllพบโค้ดที่ตรวจสอบว่าโปรเซสที่เรียกเป็น PPL หรือไม่ - โปรเซสที่สร้างด้วย
CreateProcessAธรรมดาไม่ใช่โปรเซสที่ได้รับการป้องกันแบบ PPL และคืนนั้นก็ไปต่อไม่ได้
Day 2: ตั้งค่าสภาพแวดล้อมรีโมตดีบักที่ไม่สะดวก
- บน MacBook arm64 จัดการ Windows x86 ได้ยาก และไม่อยากต้องทำงานในสภาพแวดล้อม arm64 หรือใช้งาน x64dbg ไม่ได้
- เพื่อนชื่อ pindos อนุญาตให้เข้าถึงพีซี และรีโมตเข้าไปผ่าน Parsec
- เนื่องจากเชื่อมต่อจากเกาหลีไปยังพีซีในสหรัฐฯ ความหน่วงเฉลี่ยอยู่ที่ประมาณ 210ms
- workflow ตอนนั้นยุ่งยากมาก
- build โมดูลด้วย MSVC บน Windows arm64 ใน Parallels
- แชร์ผลลัพธ์การ build กับ host ผ่าน shared folder
- คัดลอกผลลัพธ์ไปยัง VM บนพีซีของ pindos ผ่าน AnyDesk
- ดีบักบริการ WSC ในสภาพแวดล้อม Parsec ที่มีความหน่วง 210ms
- สภาพแวดล้อมไม่สะดวกจนทำให้ความเร็วในการพัฒนาและวิเคราะห์ลดลงอย่างมาก
ดีบักบริการ WSC และ WinDefend SID
- บริการ WSC เป็น DLL ที่รันโดย
svchostและปัจจัยหลักที่ขัดขวางการเชื่อมต่อ debugger คือ การป้องกันแบบ PPL - เปิด test mode ใน VM และใช้ไดรเวอร์ที่มีโค้ดไม่กี่บรรทัดใน kernel mode เพื่อลบ PPL ของโปรเซสเป้าหมาย
- จุดที่
cmd.exeล้มเหลวเมื่อขอลงทะเบียนแอนติไวรัสกับ WSC คือWscServiceUtils::CreateExternalBaseFromCaller - ฟังก์ชันนี้ impersonate เป็น RPC client แล้วตรวจสอบว่า token ของโปรเซสที่เรียกมี
WinDefendSID หรือไม่ - ตอนนั้นผู้เขียนยังไม่คุ้นกับพฤติกรรมของ Windows token จึงคิดว่าถ้า impersonate เป็น
WinDefendก็น่าจะผ่านการตรวจสอบได้ - ในสภาพอดนอน ยังตีความผิดว่าไบนารีของแอนติไวรัสเดิมก็ผ่านการตรวจ
IsMemberนี้เช่นกัน
ความพยายาม impersonate เป็น WinDefend และความล้มเหลว
- หลังศึกษา Windows token อยู่หลายชั่วโมง จึง implement แนวทางให้รันด้วย token ที่มี WinDefend SID เพื่อให้ผ่านการตรวจของ WSC
- ลองรันโค้ดโดยแนบ
WinDefendSID เข้ากับcmdแล้ว แม้ COM call จะคืนค่าSTATUS_SUCCESSแต่การลงทะเบียนแอนติไวรัสใหม่จริง ๆ ไม่เกิดขึ้น - แนวทางนี้สุดท้ายไม่เป็นประโยชน์ และต้องกลับมาตรวจสอบอีกครั้งในวันถัดไป
Day 3: สร้างอัลกอริทึมตรวจสอบจริงขึ้นใหม่
- เมื่อตรวจสอบอีกครั้งพบว่าไบนารีของแอนติไวรัสเดิม ไม่ผ่านการตรวจ WinDefend SID
- หากผ่านการตรวจนั้น จะได้จัดการ object ของ WSC ของ Windows Defender แต่ไม่สามารถปิด Defender ได้ทันทีด้วยการเรียก WSC เพียงอย่างเดียว จึงไม่มีประโยชน์
- ลบโค้ด impersonate เป็น WinDefend ออก แล้ววิเคราะห์ branch อื่นของเงื่อนไข
- branch นั้นตรวจสอบสิ่งต่อไปนี้กับไบนารีที่เรียกใช้
- โปรเซสอยู่ในสถานะ elevated หรือไม่
- ลายเซ็นของไบนารีถูกต้องหรือไม่
- มีแฟล็กเฉพาะใน
DllCharacteristicsของ PE หรือไม่
- แฟล็ก
DllCharacteristicsที่ตรวจในCSecurityVerificationManager::CreateExternalBaseFromPESettingsคือ ForceIntegrity - สร้างโค้ดที่ทำซ้ำการตรวจไบนารีแบบที่ WSC ทำไว้ในโฟลเดอร์
wsc-binary-checkของ repository defendnot และทดสอบกับไบนารีใน System32
การใช้ Taskmgr.exe และบั๊ก ctx.bin
- เมื่อเพื่อนต้องกลับไปใช้พีซี จึงเชื่อมต่อเข้า VM โดยตรงผ่าน Parsec แต่พอมี software encoding ด้วย สภาพแวดล้อมก็ยิ่งช้าลง
- ใช้
Taskmgr.exeแทนcmd.exeแต่ยังเกิด RPC error อยู่ - เพราะความหน่วงและปัญหาการพิมพ์ ทำให้ดีบักใน VM เดิมได้ยากขึ้น และหลังได้รับคำแนะนำจึงจ่าย $30 สมัคร shadow.tech
- VM ของ Shadow.tech ใช้ Windows เวอร์ชันเก่ากว่า ทำให้โค้ด
wscsvcไม่ได้ถูก inline รวมไว้ในฟังก์ชันเดียวมากเหมือนเวอร์ชันล่าสุด และผล decompile ก็อ่านง่ายกว่า - สาเหตุจริงของ error คือ ชื่อ AV ที่ส่งให้ WSC ผิด
defendnot-loaderใช้ctx.binที่บรรจุพารามิเตอร์แบบ serialized เพื่อส่งข้อมูลไปยังdefendnot.dll- แม้จะ implement IPC แยกไว้สำหรับติดตามสถานะ แต่การส่งค่า config ยังเหลือวิธี
ctx.binซึ่งเป็นซากจากโค้ด no-defender เดิม - ฟังก์ชันที่หา path ของ
ctx.binใช้โฟลเดอร์พื้นฐานของโมดูลTaskmgr.exeเป็นฐาน ไม่ใช่nodefend.dll - ผลคือส่ง null byte เป็นชื่อ AV และ WSC ปฏิเสธ buffer นั้น
- หลังแก้ปัญหา path แล้ว การทดสอบก็สำเร็จ
เก็บโค้ดให้เรียบร้อยและปัญหา autorun
- เพื่อให้เสร็จภายในวันเดียวกัน จึงเก็บโค้ดและ implement ฟีเจอร์เพิ่มเติมจนถึง 8 โมงเช้า
- ฟีเจอร์เพิ่มเติมรวมถึงความสามารถในการเพิ่มตัวเองเข้า autorun
- ตอน 8 โมงเช้าเหลือแค่ autorun ที่ยังทำงานไม่ถูกต้อง หลังทดสอบหลายวิธีแต่ล้มเหลวจึงไปนอน
- วันถัดมาตรวจสอบอีกครั้ง พบว่าสาเหตุคือ checkbox เกี่ยวกับพลังงานสองรายการตอนสร้าง task ใน Task Scheduler
- เนื่องจากโน้ตบุ๊กไม่ได้เสียบไฟ AC task จึงไม่ถูกเรียกใช้ และเมื่อปิดแฟล็กดังกล่าว autorun ก็ทำงานได้
- จากนั้นเก็บโค้ดต่ออีกไม่กี่ชั่วโมงและจบงาน
สรุป
- งานนี้สนุกก็จริง แต่ปัญหาสภาพแวดล้อมและ workflow รีโมตดีบักที่เจอซ้ำ ๆ ตลอดหลายวันเป็นประสบการณ์ที่ไม่อยากเจออีก
- โดยเฉพาะ MacBook arm64, VM x86 ระยะไกล, ความหน่วงสูง, encoding ที่ช้า และบั๊ก path ของไฟล์ที่เพิ่งมาโผล่ทีหลัง ทำให้ความยากในการ implement เพิ่มขึ้นมาก
- เอกสาร WSC เชิงเทคนิคเพิ่มเติมจะเผยแพร่แยกต่างหากในภายหลัง
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
วิธีที่รุกรานระบบที่สุดแต่ได้ผลที่สุดในการปิด Defender คือบูตด้วย live Linux USB แล้วเปลี่ยนชื่อ
C:\ProgramData\Microsoft\Windows Defenderจากนั้นสร้างไฟล์เปล่าไว้แทนที่ตรงนั้นเผื่ออ้างอิง WSC ย่อมาจาก Windows Security Center
ผมค่อนข้างสับสนเพราะสำนวน
the antivirus I was usingไม่เข้าใจว่าทำไมบริษัทแอนติไวรัสนั้นถึงมีเหตุผลที่จะส่ง คำขอลบตาม DMCA ไปให้ผู้เขียนน่าจะหมายถึงผู้เขียนทำ reverse engineering แอนติไวรัสตัวอื่นแล้วเอาบางส่วนไปใส่ในโปรเจกต์โอเพนซอร์ส แต่ก็เห็นชื่ออย่าง
Impersonating WinDefendด้วย เลยสงสัยว่าท้ายที่สุดแล้วผู้เขียนละเมิดกฎหมายลิขสิทธิ์ในรูปแบบใดรูปแบบหนึ่งหรือเปล่าย่อหน้าก่อนหน้าที่อ้างถึงอธิบายไว้ว่า “โปรเจกต์ใช้โค้ด third-party ของแอนติไวรัสที่มีอยู่แล้ว และบังคับให้แอนติไวรัสนั้นลงทะเบียนกับ WSC ในฐานะแอนติไวรัส”
โค้ดนี้ให้ความรู้สึกต้องสาป:
https://github.com/es3n1n/defendnot/blob/master/defendnot-lo...
ถ้าอยากรู้ว่าจริง ๆ แล้วเกิดอะไรขึ้น ดูตรงนี้:
https://github.com/es3n1n/defendnot/blob/master/cxx-shared/s...
ตัวอย่างเช่น ภาษา D มีแนวคิดของ statement ที่รันเมื่อจบ scope ติดมากับภาษาอยู่แล้ว
สรุปคือไม่ใช่สิ่งที่ AI เขียน โค้ดนี้หน่วงการเรียกฟังก์ชันไว้จนกว่า object จะหลุดออกจาก scope การ implement ใช้ C macro เพื่อสร้าง syntax ที่สั้นกว่า โดยละบางส่วนของการนิยาม C lambda/anonymous function ที่จำเป็น และสร้างชื่อตัวแปรไม่ซ้ำเพื่อจัดการ deferred call
แต่ syntax ที่ได้หลีกเลี่ยงการเขียนตัวพิมพ์ใหญ่ซึ่งเป็นธรรมเนียมทั่วไปในการสื่อว่าเป็น C macro และมองเผิน ๆ เหมือนการเรียกฟังก์ชันของ object pointer ถ้าไม่คุ้นกับ pattern นี้ หรือคาดหวังว่า macro จะแสดงต่างออกไป ก็อาจทำให้งงได้
สำหรับบางคน มันพบได้บ่อยและมีประโยชน์พอที่ในบางบริบทแทบจะมองเป็น idiom ได้ คำอธิบายเชิงเทคนิคใน https://news.ycombinator.com/item?id=43959403#43960905 อธิบายการทำงานของ macro ได้ดี
ช่วงวันหยุดผมไป reverse engineering virtual desktop ของ Windows แล้วทำให้วันหยุดดีขึ้นมาก ความทรงจำที่ดีที่สุดของปีที่แล้วคือการได้รู้สึกว่า reverse engineering สนุกจริง ๆ
ยังได้เรียนรู้เรื่องน่าสนใจเยอะด้วย โดยใต้ RPC ของ Windows มีเลเยอร์ messaging ที่ไม่ได้ทำเอกสารไว้: https://csandker.io/2022/05/24/Offensive-Windows-IPC-3-ALPC....
ทุกครั้งผมจะรู้เลยว่าถ้ารูปโปรไฟล์เป็นตัวละครอนิเมะ บทความน่าจะดี เซฟไว้เผื่อตอนที่ต้องกลับไปใช้ สภาพแวดล้อม Windows แย่ ๆ ในภายหลัง
สำหรับคนที่สงสัย: WSC ย่อมาจาก Windows Security Center ผมเองก็ต้องไปค้นเหมือนกัน
ทำไมถึงอยากปิด WSC กันนะ?
ถ้าเป็นผู้ขาย EDR นี่คือ API call แบบพรางตัวที่ใช้กดหรือปิดใช้งาน Windows Firewall ได้ เช่น CrowdStrike น่าจะใช้ได้ทั้ง Windows Firewall หรือ implementation ของตัวเอง
netcat.exeไม่ได้เมื่อไม่นานมานี้ผมอ่าน https://nostarch.com/windows-security-internals แล้วบทความนี้ทำให้เข้าใจได้ลึกซึ้งขึ้นมาก
ผมพอรู้อยู่บ้างว่าการทำงานเบื้องหลังแบบนี้ใน Windows โดยคร่าว ๆ เป็นอย่างไร แต่จังหวะเหมาะมาก บทสุดท้ายของหนังสือเล่มนั้นก็เจาะลึก token และ SID ค่อนข้างละเอียด เหมือนที่ผู้เขียนบทความนี้พูดถึง