# SWSEC LAB2 Writeup
###### tags: `swsec` `writeup`
<style>
p:has(img) {
text-align: center;
}
.markdown-body img {
/* max-width: 80%; */
}
</style>
## HelloRevWorld
雖然 Flag 是 wide char,但透過 ghidra 的 Defined Strings window,可以簡單的將 wide char string 提取出來。

FLAG: `FLAG{h311O_revers1ng_3ngineer5}`
## AssemblyDev
`arithmetic.asm` - 照著算數學
```assembly
; a + b
mov eax, [rsp]
add eax, [rsp+0x4]
; a - b
mov ebx, [rsp]
sub ebx, [rsp+0x4]
; -c
mov ecx, [rsp+0x8]
neg ecx
; 9*a + 7
mov edx, [rsp]
lea edx, [edx+edx*8+7]
```
`[reg]` 可以用 reg 中的值作為 address 做存取
`mov` 無法直接傳兩個 address,故先放到 `r14`/`r15` 暫存。
`data_movement.asm`
```assembly
add rax, 0x87 ; RAX += 0x87
sub rbx, 0x63 ; RBX += 0x63
; swap RCX, RDX
mov r15, rcx
mov rcx, rdx
mov rdx, r15
; modify memory
add dword [rsp], 0xdeadbeef
sub dword [rsp+0x4], 0xfaceb00c
; swap
mov r14, [rsp+0x8]
mov r15, [rsp+0xc]
mov [rsp+0x8], r15
mov [rsp+0xc], r14
```
cmp1、cmp2 中會先做條件成立的行為,若條件成立,就跳過條件不成立時的行為 (`jge`, `jb`)
檢查是否是偶數,可以用 `test reg, 1` 配合 `jz` 跳到對應行為 (`test` 中實際上做了 AND)
在 odd 中加上 `jmp end` 以跳過 even 的 instruction
`condition.asm`
```assembly
cmp1:
mov eax, [rsp] ; a
mov ebx, [rsp+0x4] ; b
cmp eax, ebx
jge cmp2
mov eax, ebx ; a < b
cmp2:
mov ebx, [rsp+0x8] ; c
mov edx, [rsp+0xc] ; d
cmp ebx, edx
jb cmp3
mov ebx, edx ; c >= d
cmp3:
mov ecx, [rsp+0x8]
test ecx, 1
jz even ; ecx & 1 == 0
odd:
shr ecx, 3
jmp end
even:
shl ecx, 2
end: nop
```

FLAG: `FLAG{c0d1Ng_1n_a5s3mB1y_i5_sO_fun!}`
## Clipboard Stealer 1 -- sub_140001C80
查閱 `GetModuleFileNameA` 文件,若 `hModule` 為 NULL,則回傳目前執行檔的路徑。
另其在 `GetUserNameA` 後,將其傳入 `sprinf` 造成完整路徑,並透過 `CopyFileA` 將目前執行檔複製到該路徑,且使用 `SetFileAttributeA` 設該檔為 `FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM` 以隱藏該檔案。

該路徑位於 `Start Menu\Programs\Startup` 下,會於開機時自動啟動,故推論此 function 之行為為 `Boot or Logon Autostart Execution: Registry Run Keys / Startup Folder`,其 MITRE ATT&CK technique ID 為 `T1547.001`,推得 flag。

FLAG: `FLAG{T1547.001}`
## Clipboard Stealer 2 -- sub_140001030
此 function 建立了一個 timer,會於 2023/11/18 後才啟動。

其可用來讓此惡意程式只在環境符合特定條件時執行,以針對特定對象進行攻擊。
也並未有繞過 VM 的意圖,故推測為 `Defense Evasion — Execution Guardrails`,其 MITRE ATT&CK technique ID 為 `T1480`,推得 flag。

FLAG: `FLAG{T1480}`
## Clipboard Stealer 3 -- sub_140001120
xor_key 原本為 `int[2]`,將其 retype 成 `char[4]`。
重新命名變數後可以發現為簡單的 XOR 加密。

透過 Export data 把 mutex_name 的 data 複製出來,寫個 script 解密。
`Clipboard Stealer 3 -- sub_140001120/solve.py`
```python
xor_key = bytes.fromhex("6463627A")[::-1] # little endian
mutex_name = [
0x0E, 0x0A, 0x52, 0x51, 0x25, 0x2B, 0x57, 0x3B, 0x4E, 0x3D,
0x0E, 0x11, 0x0E, 0x51, 0x1B, 0x3B, 0x11, 0x53, 0x2F, 0x28,
0x25, 0x31, 0x14, 0x0D, 0x0E, 0x01, 0x2B, 0x64
]
for i in range(len(mutex_name)):
print(chr(mutex_name[i] ^ xor_key[i % len(xor_key)]), end="")
```
FLAG: `FLAG{th15_I4_4_mut3x_k1LL_SwitcH}`
## Clipboard Stealer 4 -- Extract Next Stage Payload
觀察 `sub_140001BF0`,裡面 `sub_140001870` 之參數為 pointer of pointer 與 pointer of size,看起來像在讀東西。

查看 `sub_140001870` 實作,並參考其訊息,可推論 pe_data 為內嵌之 dll。
將此 function 重命名為 `getEmbeddedPE`。

透過 Shift+E 將 `pe_data` export 為 `exported.dll`。

將 `exported.dll` 提取出來後,可以透過 `certutil -hashfile exported.dll MD5` 來計算其 MD5 hash。

FLAG: `FLAG{462fe0007f86957f59824e113f78947c}`
## Clipboard Stealer 5 -- Exfiltrate Data
該 dll 忘記拔掉 debug info,function name 都還留著。
從 `_DllMainCRTStartUp` -> `DllMain` 會看到兩個 function call,其中一個叫做 `collect_and_exfiltrate`。
跟進去之後會看到 `collect_data` 與 `exfiltrate`,而 `exfiltrate` 中又有`connect_to_c2` 與 `send_collected_data_to_c2`。
`connect_to_c2` 中能看到以下程式碼:

其行為為 TCP 連線到 `192.168.10.10:11187`,故可知流量中何者為回傳 C2 的封包。
簡單觀察 `send_collected_data_to_c2` 後,可以回推其封包格式。
每個 packet 都是 0x4C 的大小,而有個欄位會固定為 `0x11877811`,另外 switch 所判斷的值看起來會決定行為,故猜設為 `cmd`,整個封包後 24 個 byte 則為 `data`。
建立 `C2_PACKET` struct:

最後標註如下:

`cmd = 1` 會從 C2 取得 key 並將資料做加密,`cmd = 2` 則是將加密的資料回傳。
查看 `encrypt_data`,其使用了 `RC4` 作為加密演算法,並取 8 bytes 作為 key。

寫一個腳本從封包撈取資料並解密。
`Clipboard Stealer 5 -- Exfiltrate Data/solve.py`
```python
from scapy.all import *
from Crypto.Cipher import ARC4
C2 = "192.168.10.10"
MAGIC = b"\x11\x87\x78\x11"[::-1]
pkts = rdpcap("capture.pcapng")
key = None
for pkt in pkts:
if TCP not in pkt: continue
payload = bytes(pkt[TCP].payload)
if not payload.startswith(MAGIC): continue
cmd = int.from_bytes(payload[0x8:0x8+4], 'little')
data = payload[0xC:0xC+24]
if pkt[IP].src == C2 and cmd == 1:
key = data[:8]
print(f"key={key.hex()}")
elif pkt[IP].dst == C2 and cmd == 2:
cipher = ARC4.new(key)
print(f"cipher={data.hex()}")
print(cipher.decrypt(data).decode())
```
FLAG: `FLAG{C2_cU540m_Pr0t0C01}`
## Clipboard Stealer 6 -- Dynamic API Resolution
用 Ctrl+E 跳到 `my_start`。
先將 `p_InLoadOrderModuleList` retype 成 `_LDR_DATA_TABLE_ENTRY *`,讓 `j` 的型別也正確,`j[6].Flink` 此時變成 `j->BaseDllName.Buffer`,最後將 `j` rename 成 `module `。
將底下針對 `dll_name` 的判斷常數按 R 轉成 char,可以發現他在找 `dll_name == kernel32`,也就是 `kernel32.dll`。
<img src=https://hackmd.io/_uploads/ByXR_zaVT.png style="width: 55%">
<img src=https://hackmd.io/_uploads/BJvyKf6N6.png style="width: 45%">
左: Before; 右: After

往下滑到 `user32` 的部分,並將變數 retype: `export_table_user32` -> `IMAGE_EXPORT_DIRECTORY *`、`addr_of_*` -> `_DWORD *`。
可以看到程式會遍歷 `export_table_user32` 並取其 function name (`v41`) 做 hash,當 hash 符合時,則將其 address 載入。
在 [Mr-Un1k0d3r/WindowsDllsExport](https://github.com/Mr-Un1k0d3r/WindowsDllsExport) 可找到 `user32.dll` 的 export table 中所有 function 名稱,全部掃過一次即可找到該 API 為何者。
`Clipboard Stealer 6 -- Dynamic API Resolution/solve.py`
```python
import requests
name_list_url = "https://raw.githubusercontent.com/" \
"Mr-Un1k0d3r/WindowsDllsExport/main/Win11-22000/user32.dll.txt"
res = requests.get(name_list_url)
name_list = res.text.splitlines(keepends=False)
def rol4(n, b):
bits = f"{n:032b}" # rol"4" -> 4 bytes = 32bits
return int(bits[b:] + bits[:b], 2) & ((1 << 32) - 1)
for name in name_list:
fn_hash = 0
for c in name:
fn_hash += ord(c) + rol4(fn_hash, 11) + 1187
fn_hash &= ((1 << 32) - 1)
if fn_hash == 0x416F607:
print(name)
break
```
FLAG: `FLAG{MessageBoxA}`
## Super Angry

此題會將 argv 作為 command line arg 傳入 `sub_11C9` 做轉換之後驗證。

`sub_11C9` 看起來像一個 state machine,第三個參數為長度。
可透過 angr 爆搜。
```python
import angr
import claripy
import sys
def solve(elf_binary, l):
project = angr.Project(elf_binary)
flag_chars = [claripy.BVS('flag_%d' % i, 8) for i in range(l)]
flag = claripy.Concat(*flag_chars)
initial_state = project.factory.entry_state(
args=[elf_binary, flag],
add_options=angr.options.unicorn
)
simulation = project.factory.simgr(initial_state)
simulation.explore(find=is_successful, avoid=fail)
if len(simulation.found) > 0:
for solution_state in simulation.found:
print("[>>] {!r}".format(solution_state.solver.eval(flag,cast_to=bytes)))
else:
print("[>>] no solution found :(")
def is_successful(state):
output = state.posix.dumps(sys.stdout.fileno())
return b'Correct' in output
def fail(state):
output = state.posix.dumps(sys.stdout.fileno())
return b'Incorrect' in output
solve("./super_angry", 0x20)
```
透過 `claripy.BVS('flag_%d' % i, 8)` 建立一個 8 bit char,參考 `sub_11C9` 的呼叫,flag 長度為 0x20,最後將這些 BVS concat 起來。
另外透過 `project.factory.entry_state` 建立 state 及指定輸入位置 (`args`),與 `project.factory.simgr` 建立 simulation。
`simlutaion.explore` 來設定何謂正確輸出,可傳入 function 或 address。

FLAG: `FLAG{knowing_how_2_angr!}`
## Scramble
針對每個 flag 字元,題目會隨機產生一系列操作 (add, sub, lsh) 來變動其值,並提供過程與結果。
可以透過 z3 來設定 constraint 來自動解原始值 (`x`)。BitVec 的 bits = 32 = 8 * 4 bytes。
內層迴圈會建立多個 `x{i}` 作為 intermediate state。
最後在 `solver.check()` = `sat` 後,即可透過 model 取得 `x` (flag 字元)。
這裡可以加上 `x < 256` 與 `x > 0` 來確保解出來的是 char。
外層迴圈用來指定解第幾個字元。
```python=
from z3 import *
pattern = [...]
result = [...]
flag = []
for op, r in zip(pattern, result):
s = Solver()
x = BitVec('x', 32)
s.add(x < 256)
s.add(x > 0)
xs = [x]
for i, (operator, operand) in enumerate(op):
xi = BitVec(f'x{i}', 32)
if operator == "add":
s.add(xs[-1] + operand == xi)
elif operator == "sub":
s.add(xs[-1] - operand == xi)
elif operator == "lsh":
s.add((xs[-1] << operand) == xi)
xs.append(xi)
s.add(xi == r)
if s.check() == sat:
flag.append(s.model()[x].as_long())
else:
print(s)
print(flag)
print(*map(chr, flag), sep="")
```

FLAG: `FLAG{scramble_and_using_solver_to_solve_it}`
## Unpackme
直接 strings 可發現此程式透過 upx 加殼

試著用 `upx -d` 來脫殼,卻顯示 `Checksum Error`

自行 patch upx 的 checksum 檢查,即可正常脫殼
<img src=https://hackmd.io/_uploads/SkuzGqnEa.png style="width: 36%"> <img src=https://hackmd.io/_uploads/S1YVW9hE6.png style="width: 60%">
隨意丟進 ghidra 看即可知道其 input 為 `just_a_key` 時,即會解密 flag 並輸出。


FLAG: `FLAG{just_4_simple_unpackme_challenge!}`