鐵人賽 Day 24 逆向實戰 - Post 驗證參數 (中等)

鐵人賽 Day 24 逆向實戰 - Post 驗證參數 (中等)
鐵人賽 Day 24 逆向實戰 - Post 驗證參數 (中等)

本系列文章所討論的 JavaScript 資安與逆向工程技術,旨在分享知識、探討防禦之道,並促進技術交流。

所有內容僅供學術研究與學習,請勿用於任何非法或不道德的行為。

讀者應對自己的行為負完全責任。尊重法律與道德規範是所有技術人員應共同遵守的準則。

 

aHR0cHM6Ly93d3cubWFzaGFuZ3BhLmNvbS9wcm9ibGVtLWRldGFpbC85Lw==

打開 DevTools 後會直接停在debugger需要往上追蹤 Call Stack 並且 Override 排除。

1.jpg

選擇請求 data/ 在 Headers 標籤下可以確認請求方法為 POST。

2.jpg

在 Payload 標籤下觀察請求內容,看到參數 m 和 tt,為傳送的驗證參數。

3.jpg

在 Initiator 分頁檢查呼叫堆疊追蹤到 window.loadPage 函式來自 pagination9.js。

點擊 pagination9.js 並進入該對應的函式。

4.jpg

程式碼中呼叫 $[r(1414)] 經過混淆不太容易觀察到實際內容。

5.jpg

在 Console 輸入 r(1414) 得到 ajax,所以$[r(1414)] 等同於 $.ajax。

在 Console 輸入 $.ajax 並點擊函式可以跳到該函式位置。

6.jpg

繼續追蹤 n[r(1414)] = function(u){...},發現這裡重新包裝了 $.ajax。

7.jpg

先下斷點觀察,可以觀察到呼叫鏈中使用 t[e(613)],透過 n(r) 方式轉換並呼叫函式。

所以實際上需要觀察的是n的函式,將滑鼠放在n上會顯示函式資訊,並且點擊進入該函式。

8.jpg

這個函式也是經過混淆的。

9.jpg

透過 r(1034) 取出的字串 "1|6|7|2|0|3|4|5" 控制 switch-case 的執行順序。

10.jpg

所以實際上的執行邏輯如下,但這還是混淆過的程式碼,需要進一步解混淆。

var f = (new Date)[r(2244)]();
n[r(516)] = n.headers || {};
n[r(516)][r(481)] = t.fFUZS;
n[r(1939)] = n[r(1939)] || {};
n.data.m = t.MqmaW(c, t[r(1881)](t[r(489)], f));
n[r(1939)].tt = btoa(f);
n[r(1939)] = JSON[r(827)](n.data);

解混淆後的程式碼如下

var f = (new Date).getTime();
n.headers = n.headers || {};
n.headers['Content-Type'] = 'application/json';
n.data = n.data || {};
n.data.m = c('9527' + f);
n.data.tt = btoa(f);
n.data = JSON.stringify(n.data);

解完混淆可以得知兩個參數實際邏輯。

m 是透過 c('9527' + 時間戳) 產生的。

tt 則是將時間戳使用base64編碼。

滑鼠移到變數 c 可以顯示該函式資訊,並可直接跳轉到程式碼定義位置。

11.jpg

最後可以定位到產生簽名的函式,看到使用 HmacSHA1 並結合特定字串運算,這裡就是最終生成驗證參數的關鍵位置。

12.jpg
const CryptoJS = require('crypto-js')

function generatorPostM(time) {
    const text = `9527${time}`
    return CryptoJS.HmacSHA1(text, "xxxooo").toString();
}

function generatorPostTt(time) {
    return btoa(`${time}`)
}

const getPage = async(page) => {
    const time = new Date().getTime();

    const postM = generatorPostM(time);
    const postT = generatorPostTt(time)

    const response = await fetch(`https://xxxxxxxxxx/api/problem-detail/9/data/`, {
        "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/9/",
            "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": JSON.stringify({
            "page": page,
            "m": postM,
            "tt": postT,
        }),
        "method": "POST",
    });

    let 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()
https://github.com/mrnick6886/ScrapingChallenges/blob/main/mashangpa/9.js

 

作者頭像
Nick

擅長從前端的互動設計到後端的資料處理,都能親自規劃與實作。對我來說開發網站不只是完成功能,而是打造一個能被真正使用、體驗順暢的平台。我喜歡把複雜的技術轉化成簡單好懂的成果,並在這個過程中持續學習與挑戰自己。