3 min read

Bugku CTF - game1 writeup (zh-TW)

Bugku CTF - game1 writeup (zh-TW)

題目資訊

解題過程

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 等)來驗證假設