CGGC Reverse Medium Kazmansomware Writeup

kazma 成大資安社 創辦人/社長

Intro

這次出了一題勒索軟體當作 CGGC 2024 決賽的 reverse medium,開發上其實花了不少時間,因為一直在調整勒索的範圍以及在思考考點要怎麼拿捏比較適合,最後也花了點時間反覆測試題目在解題上是可行的才放心去做別的事,希望大家會喜歡,雖然最後題目難度偏簡單有點可惜,但也許之後會出個 revenge 也說不定。
題解的部分會直接用出題者的角度去介紹,所以大家也可以參考看看跟自己的做法有哪些差異。

Writeup

這次的題目會給一個 flag.zip 如下:
flag.png
然後我們解壓縮之後會看到 flag.png,但其實他是一個 flag.png.exe,只是因為 windows 預設會隱藏副檔名的顯示,所以如果我們執行這張假圖片的話他就會執行到裡面藏的勒索軟體 rans.exe,像是下面這樣:
exe
可以明顯看到他幫我們換桌布並且跳出一個類似勒索軟體的 gui,並且如果我們嘗試關閉的話會看到需要 termination key:
termination
那這邊原本其實是想懲罰在主機解題的人,所以原本是會把檔案都加密的,但是評估了一下還是只幫各位換桌布而已,有點可惜QQ
總之如果各位把他丟進去逆向工具會發現他其實是一個 Self-Extracting Archive 的壓縮檔:
sfx
這可以直接透過 WinRAR 或是 binwalk 之類的工具拿到 rans.exe 如下:
rans
那把 rans.exe 丟去逆向應該會發現它是由 python 編譯成的 exe:
pyi
那實際上我確實是用 auto-py-to-exe 編譯成的,所以我們這邊可以先用 pyinstxtractor 幫我們解出 pyc:
https://github.com/extremecoders-re/pyinstxtractor.git

然後再用 decompyle3 把 rans.pyc 解回 rans.py,如果用 uncompyle6 可能會因為不支援最新版的 python 所以反編譯失敗。
接著拿到 rans.py 後就應該就沒什麼問題了,可以注意到勒索軟體它會去生成一組隨機的 machine id 跟 key 然後用 receive_key 的 api 把資料送給遠端的 server,然後又可以注意到勒索軟體使用者嘗試輸入 key 之後解開的時候會去拿 machine id 跟 key 請求 get_flag api。
那這邊其實解法很簡單,我們其實只要自己生一組 machine id 跟 key 之後請求 receive_key api 就可以在遠端存下這組 id 跟 key,然後我們再用同一組 id 跟 key 去請求 get_flag 就可以拿到 flag 了,因為資料庫裡存在我們剛剛放進去的那組 key。

不過一開始沒想到的是,後來看 dashboard 好像有人存了一組空白的 id 跟 key 導致後來請求空白的 id 跟 key 就能拿到 flag 了 QQ
有點可惜 receive_key api 應該加一下限制,不然大家隨便送的 id 跟 key 都會收。
space

Exploit 參考

GPT 生的解題腳本:

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# python pyinstxtractor.py rans.exe
# decompyle3 ./rans.pyc > rans.py
import requests
import base64
import uuid
from Crypto.Protocol.KDF import PBKDF2
from Crypto.Random import get_random_bytes

# Server endpoints
DASHBOARD_URL = 'https://rans.kazma.tw/prjrans/includes/api/receive_key.php'
GET_FLAG_URL = 'https://rans.kazma.tw/prjrans/includes/api/get_flag.php'

# Key generation function (matches the logic in EncryptionTool)
def generate_key(password, salt=None):
if salt is None:
salt = get_random_bytes(16) # Generate a random salt
return PBKDF2(password.encode(), salt, dkLen=32, count=1000000), salt

# Exploit logic
def exploit():
# Step 1: Generate a fake machine ID
fake_machine_id = str(uuid.uuid4()) # Generate a valid UUID
print(f"[+] Fake Machine ID: {fake_machine_id}")

# Step 2: Generate a fake encryption key
password = "PleaseGiveMeMoney" # Password used in the code
key, salt = generate_key(password)
encoded_key = base64.b64encode(key).decode('utf-8') # Encode the key in base64
print(f"[+] Generated Encryption Key: {encoded_key}")

# Step 3: Send the machine_id and key to the server
payload = {
'machine_id': fake_machine_id,
'encryption_key': encoded_key
}
headers = {'Content-Type': 'application/json'}
response = requests.post(DASHBOARD_URL, json=payload, headers=headers, verify=False)

if response.status_code == 200:
print(f"[+] Successfully submitted machine ID and key to the server!")
else:
print(f"[-] Failed to submit key. Server response: {response.text}")
return

# Step 4: Use the fake machine ID and key to request the flag
payload = {
'machine_id': fake_machine_id,
'decryption_key': encoded_key
}
response = requests.post(GET_FLAG_URL, json=payload, headers=headers, verify=False)

if response.status_code == 200:
data = response.json()
if data.get("success"):
flag = data.get("flag", "No flag returned.")
print(f"[+] Successfully retrieved the flag: {flag}")
else:
print(f"[-] Failed to retrieve the flag: {data.get('message')}")
else:
print(f"[-] Failed to fetch flag. Server response: {response.text}")

# Run the exploit
if __name__ == "__main__":
exploit()

執行結果:

1
2
3
4
5
6
7
8
9
╰─ python solve.py                                                                                                  ─╯
[+] Fake Machine ID: 43e88d41-67fe-4af4-95af-11a0ff5731e2
[+] Generated Encryption Key: xs0zV0FG4V3f8NBwdO/k2GjiMEg6ptqJ46aSf2rbaEM=
/Users/kingkazma/anaconda3/lib/python3.10/site-packages/urllib3/connectionpool.py:1064: InsecureRequestWarning: Unverified HTTPS request is being made to host 'rans.kazma.tw'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/1.26.x/advanced-usage.html#ssl-warnings
warnings.warn(
[+] Successfully submitted machine ID and key to the server!
/Users/kingkazma/anaconda3/lib/python3.10/site-packages/urllib3/connectionpool.py:1064: InsecureRequestWarning: Unverified HTTPS request is being made to host 'rans.kazma.tw'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/1.26.x/advanced-usage.html#ssl-warnings
warnings.warn(
[+] Successfully retrieved the flag: CGGC{Nn3lr*WppJ%RkdTsGRAR}
  • Title: CGGC Reverse Medium Kazmansomware Writeup
  • Author: kazma
  • Created at : 2024-12-07 14:22:39
  • Updated at : 2024-12-07 16:14:18
  • Link: https://kazma.tw/2024/12/07/CGGC-2024-Reverse-Medium-Kazmansomware-Writeup/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments
On this page
CGGC Reverse Medium Kazmansomware Writeup