前言
Hacktheon Sejong 據說是韓國版的金盾獎,看到前年很多人飛韓國參加就一直蠻想打的
因為限制只有大學生能夠參賽,所以是第一次參加,也就代表 Cake 的好隊友們不能陪我打了 QQ
這次跟 albert、呂老闆、slash(他去打黑客松了,嚴厲斥責) 一起打,最後是第 42 名。註:前 10 名可以去韓國打決賽
順帶一提,國籍可以填 Taiwan,填 ROC 的話國旗會是 China
防雷寫在前面:這篇 Write-up 都是 20+ solve 的水題,且大量使用 LLM,想看更完整的解答可以等 牛肉湯 / I’m down QQ / 吉娃娃 的 Write-up
賽制
- Jeopardy
- 分數線性降低、無首殺分
- 分為 Advanced 組和 Beginner 組,題目似乎相同
- 題目分階段解鎖,解掉當前階段任一題目就會開放下一階段的題目
Reverse
Barcode
ELF 會讀入一個 hex-string 並根據 hex-string 輸出 ’*’ 和 ’ ’ 組成的圖形,給輸出檔案要求回推原始輸入
範例:
❯ ./barcode 0x1111111122222222
* *
* *
* *
* *
* *
* *
* *
* *
乖乖打開 IDA 逆,總之關鍵在這 輸入的 hex-string 會被切成每 8 個 bytes 一組,分別代表 8x8 的 Block,除了第 0 個 Block 以外,對每個 Block 都先 XOR -1 (就是 NOT) 再和前一個 Block 做一次 xor,最後根據每個 bit 輸出結果
逆完把邏輯丟給 ChatGPT 再修一下 Spec 就能生出解題腳本了
import sys
MASK = 0xFFFFFFFFFFFFFFFF
def slurp_blocks(stdin_lines):
rows = [ln.rstrip('\n') for ln in stdin_lines if len(ln.rstrip('\n')) == 8]
return [rows[i:i + 8] for i in range(0, len(rows), 8)]
def block_to_int(block):
v = 0
for r in range(8):
for c in range(8):
if block[r][c] == '*':
v |= 1 << (r * 8 + c)
return v
def recover_orig(out_blocks):
orig = [out_blocks[0]]
for i in range(1, len(out_blocks)):
orig.append(((~out_blocks[i]) & MASK) ^ orig[i-1])
return orig
def main():
out_blocks = [block_to_int(b) for b in slurp_blocks(sys.stdin)]
orig = recover_orig(out_blocks)
hex_string = '0x' + ''.join(f'{v:016x}' for v in orig)
print(hex_string)
if __name__ == '__main__':
main()
Bridge
apk 裡有 encode 跟 decode 能 call,給 encode 過的結果,flag 是 decode 的結果
看到 apk 就拿出 android 死人三件套
- jadx
- apktool
- frida
這次只用到 jadx 跟 frida 而已
從 Mainactivity 往上追
追到最後會發現 load 一個 Library
要逆這個光聽就覺得麻煩了,所以還是動態解就好
把 Frida server 推到模擬器上面待機之後
Attach 並執行腳本 call decode 就好了
frida -U -p <bridge_pid> -l .\solve.js
Java.perform(function() {
var BridgeLib = Java.use('com.hacktheon.bridge.BridgeLib');
console.log('Hooked');
var enc = "4658hg76<h85eed73ihghidi8ehf<78;";
var decoded = BridgeLib.decode(enc);
console.log('Flag: ' + decoded);
});
I love reversing
Pyinstaller 打包的 exe 檔,做了甚麼不重要
自從上次打 EOF Final 被 pyc 坑慘之後就一直記著這個西方神秘網站 PyLingual 再搭配 pyinstxtractor 就能解了
先用 pyinstxteactor 拆出 .pyc 檔案,再用 PyLingual 還原回 Python 檔
根據題目說明可以知道 flag 就是 2.593627
# Decompiled with PyLingual (https://pylingual.io)
# Internal filename: infect.py
# Bytecode version: 3.12.0rc2 (3531)
# Source timestamp: 1970-01-01 00:00:00 UTC (0)
import requests
from flask import Flask, request, jsonify
app = Flask(__name__)
def infect(location_data):
location_data['latitude'] += 2.593627
location_data['longitude'] += 2.593627
return location_data
@app.route('/location_data', methods=['POST'])
def location_data():
location_data = request.json
print('Received data from attack instruction PC:', location_data)
location_data = infect(location_data)
url = 'http://192.168.101.101:4653/location_data'
response = requests.post(url, json=location_data)
print('Response from ship node:', response.text)
return jsonify({'message': 'Data forwarded to ship node successfully!'})
if __name__ == '__main__':
app.run(host='0.0.0.0', port=4653)
Forensics
Shadow of the System
給了一個 SYSTEM 檔案,要求找到可疑行為後印出的字串
用 RegExp.exe 可以在 ControlSet001 底下找到很多 Service,根本看不完
查 cmd.exe 也一堆
最後從題目敘述通靈搜 backdoor 找到了
註:有試過直接 grep 找不到,後來發現要搜 unicode
Watch
給了 Cache0000.bin 要還原出當時 RDP notepad 寫的字串
可以用 BMC-Tool
python3 bmc-tools/bmc-tools.py -s rdp/Cache0000.bin -d output -b
可以找到這個
註: -b
可以自動拼成大圖
Hidden message
給張圖,要找隱藏訊息
StegOnline 可以簡單解掉
Extract LSB 就可以看到 flag
後記
感覺下次可以報 Advanced,好像比較容易進決賽
唉真的越來越菜,要多打一點 CTF 了
拜託揪我