鐵人賽 Day 20 逆向實戰 - 解析MD5簽名機制流程 (簡單)

本系列文章所討論的 JavaScript 資安與逆向工程技術,旨在分享知識、探討防禦之道,並促進技術交流。
所有內容僅供學術研究與學習,請勿用於任何非法或不道德的行為。
讀者應對自己的行為負完全責任。尊重法律與道德規範是所有技術人員應共同遵守的準則。
挑戰網址
aHR0cHM6Ly93d3cubWFzaGFuZ3BhLmNvbS9wcm9ibGVtLWRldGFpbC80Lw==
解題過程
觀察 Network 請求
在 Chrome DevTools 打開 「 Network 」 分頁,並篩選 「 XHR / Fetch 」 請求。
可以看到 /data/?page=2&sign=...&_ts=... 的 API 呼叫。
「 sign 」 與 「 _ts 」這兩個就是驗證參數。

搜尋 sign 的來源
Search 全域搜尋 sign。
搜尋結果顯示 pagination4.js 檔案裡有 sign: window.token 的程式碼。
這代表 API 中的 sign 值,實際上是 window.token。

分析 sign 的產生邏輯
在 pagination4.js 中找到 LoadPage(pageNumber) 函式。
程式會先產生 timestamp = new Date().getTime()。
再將 "tuling" + timestamp + pageNumber 拼接成字串進行 MD5 雜湊。
最後存入 window.token,並作為 API 請求參數中的 sign。

設置斷點進行驗證
在 pagination4.js 第 87 行(window.token = window.md5(...))設定斷點。
重新觸發 API 請求,程式會在這行暫停。
將"tuling" + timestamp + pageNumber 反白後滑鼠放上去,可以馬上看到運算的結果。
如下圖所示這時候的值是 "tuling17568460861013" 然後進行MD5雜湊。

檢查 sign 的實際值
在 Debugger 暫停狀態下,打開 Console。
輸入 window.token,即可輸出運算後的MD5。
這個值與 API 請求中的 sign 一致,驗證了 sign 的生成邏輯。

檢查MD5算法是否魔改過
為了避免這是魔改MD5我們必須進行驗證
const CryptoJS = require('crypto-js')
const sign = CryptoJS.MD5("tuling17568460861013").toString();
console.log(sign);
可以得到執行結果就是 "6b6e0c21877698662f1794d9a7bebb54"。
完整程式碼
那現在即可把完整請求用Node模擬出來。
const CryptoJS = require('crypto-js')
const getPage = async(page) => {
const timestamp = new Date().getTime()
const sign = CryptoJS.MD5("tuling" + timestamp + page).toString();
const url = `https://xxxxxxxxxx/api/problem-detail/4/data/?page=${page}&sign=${sign}&_ts=${timestamp}`
const response = await fetch(url, {
"headers": {
"accept": "*/*",
"accept-language": "zh-TW,zh;q=0.9,en;q=0.8,en-US;q=0.7",
"cache-control": "no-cache",
"pragma": "no-cache",
"priority": "u=1, i",
"sec-ch-ua": "\"Not;A=Brand\";v=\"99\", \"Google Chrome\";v=\"139\", \"Chromium\";v=\"139\"",
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": "\"macOS\"",
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-origin",
"cookie": "sessionid=xxxxxxxxxx",
"Referer": "https://xxxxxxxxxx/problem-detail/4/",
"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36",
},
"body": null,
"method": "GET"
});
const json = await response.json()
return json.current_array.reduce((a, b) => a + b, 0);
}
const run = async() => {
let total = 0;
for(let i = 1 ; i<= 20 ; i++){
total += (await getPage(i))
}
console.log(`total: ${total}`)
}
run()
Github 原始碼
https://github.com/mrnick6886/ScrapingChallenges/blob/main/mashangpa/4.js