iT邦幫忙

2025 iThome 鐵人賽

DAY 26
0
Security

走進資安現場: JavaScript資安逆向工程超實戰系列 第 26

Day 26 逆向實戰 - URL 驗證參數 (困難)

  • 分享至 

  • xImage
  •  

https://ithelp.ithome.com.tw/upload/images/20250926/20169775qz3N36mujp.jpg

本系列文章所討論的 JavaScript 資安與逆向工程技術,旨在分享知識、探討防禦之道,並促進技術交流。
所有內容僅供學術研究與學習,請勿用於任何非法或不道德的行為。
讀者應對自己的行為負完全責任。尊重法律與道德規範是所有技術人員應共同遵守的準則。

本文同步發佈:https://nicklabs.cc/javascript-reverse-engineering-url-difficult

挑戰網址

aHR0cHM6Ly93d3cubWFzaGFuZ3BhLmNvbS9wcm9ibGVtLWRldGFpbC8xNC8=

解題過程

觀察 Network 請求

在 Chrome DevTools 打開 「 Network 」 分頁,並篩選 「 XHR / Fetch 」 請求。

可以看到 /data/?page=3&m=... 的 API 呼叫。

「 m 」這個就是驗證參數。

https://ithelp.ithome.com.tw/upload/images/20250926/20169775lu5N9G9lIF.jpg

檢查 Payload

切換到 Payload 標籤,這裡看到送出的 Form Data 僅包含 { "page": 2 },沒有其他驗證參數。

https://ithelp.ithome.com.tw/upload/images/20250926/201697751odiJp1UW6.jpg

追蹤請求來源

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

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

https://ithelp.ithome.com.tw/upload/images/20250926/201697757nqtXqA3w0.jpg

檢查 loadPage 函式

打開 pagination14.js 找到 loadPage。

這裡只是透過 $.ajax 發起請求並沒有驗證參數生成的邏輯。

https://ithelp.ithome.com.tw/upload/images/20250926/20169775qMzI7uEMOi.jpg

利用 Console 追蹤 $.ajax

在 Console 中輸入 $.ajax 並點擊跳轉到 jQuery 原始程式碼中定義的 AJAX 函式。

https://ithelp.ithome.com.tw/upload/images/20250926/20169775KzGz1RCTIN.jpg

檢查 $.ajax 定義

在 jquery-3.6.0.min.js 中看到 $.ajax 的原始碼沒有被 hook,代表請求仍走 jQuery 的原流程。

https://ithelp.ithome.com.tw/upload/images/20250926/20169775ERf4Bgz3KH.jpg

$.ajax 設斷點,逐步追蹤

在 pagination14.js 中的 $.ajax 下斷點並逐步執行,會發現呼叫流程跳進另一個檔案 xhr.js。

在 xhr.js 內找到混淆過的函式呼叫 x(f),進一步觀察發現這就是驗證參數生成的入口。

https://ithelp.ithome.com.tw/upload/images/20250926/20169775fQ8xqxkD6r.jpg

定位驗證參數生成邏輯

將滑鼠放上 x 會顯示該函式資訊並點擊跳轉到該函式。

https://ithelp.ithome.com.tw/upload/images/20250926/20169775WcXkNu99nF.jpg

解混淆取得生成規則

將 xhr.js 內部的混淆程式碼解讀後,就能完整理解驗證參數的生成邏輯,掌握請求驗證的核心機制。

https://ithelp.ithome.com.tw/upload/images/20250926/20169775BH2BwU2Gy1.jpg

完整程式碼

function getUrlM() {
    const ts = new Date().getTime()

    function t1(str) {
        let n = 0;
        for (let i = 0; i < str.length; i++) {
            var charCode = str.charCodeAt(i);
            for (let j = 0; j < 20; j++)
                switch (j % 3) {
                    case 0:
                        n = n ^ (charCode << j % 8);
                        break;
                    case 1:
                        n = (n ^ charCode >> j % 8)
                        break;
                    case 2:
                        n = n ^ charCode;
                }
        }
        return n.toString(16);
    }

    function t2(N) {
        var W = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
        let O = '', L, y, k, H, D, K, S, I = 0;
        while(I < N.length){
            L = N.charCodeAt(I++)
            y = I < N.length ? N.charCodeAt(I++) : 0
            k = I < N.length ? N.charCodeAt(I++) : 0
            H = L >> 2
            D = (3 & L) << 4 | (y >> 4)
            K = (((15 & y) << 2) | (k >> 6))
            S = 63 & k
            isNaN(y) ? K = S = 64 : isNaN(k) && (S = 64)
            O = (O + W.charAt(H)) + W.charAt(D) + W.charAt(K) + W.charAt(S)
        }
        return O;
    }

    let p = t1('dasdasdarqwdasdasqwdasda' + ts)
    return t2(`${p}${ts}`)
}


const getPage = async(page) => {

    const urlM = getUrlM()

    const response = await fetch(`https://www.mashangpa.com/api/problem-detail/14/data/?m=${urlM}`, {
        "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=h378reowcqlena73nfa49mqfc4g26i6f",
            "Referer": "https://www.mashangpa.com/problem-detail/14/",
            "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"
    });

    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()

Github 原始碼

https://github.com/mrnick6886/ScrapingChallenges/blob/main/mashangpa/14.js


上一篇
Day 25 逆向實戰 - URL 驗證參數 (中等)
系列文
走進資安現場: JavaScript資安逆向工程超實戰26
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言