【驗證碼逆向專欄】數美驗證碼全家桶逆向分析以及 AST 獲取動態參數

来源:https://www.cnblogs.com/ikdl/archive/2023/05/08/17382244.html
-Advertisement-
Play Games

聲明 本文章中所有內容僅供學習交流使用,不用於其他任何目的,不提供完整代碼,抓包內容、敏感網址、數據介面等均已做脫敏處理,嚴禁用於商業用途和非法用途,否則由此產生的一切後果均與作者無關! 本文章未經許可禁止轉載,禁止任何修改後二次傳播,擅自使用本文講解的技術而導致的任何意外,作者均不負責,若有侵權, ...


聲明

本文章中所有內容僅供學習交流使用,不用於其他任何目的,不提供完整代碼,抓包內容、敏感網址、數據介面等均已做脫敏處理,嚴禁用於商業用途和非法用途,否則由此產生的一切後果均與作者無關!

本文章未經許可禁止轉載,禁止任何修改後二次傳播,擅自使用本文講解的技術而導致的任何意外,作者均不負責,若有侵權,請在公眾號【K哥爬蟲】聯繫作者立即刪除!

目標

  • 目標:數美全家桶,包括:滑塊、文字點選、圖標點選、語序點選、空間推理、無感驗證
  • 地址:
// 官網體驗地址
aHR0cHM6Ly93d3cuaXNodW1laS5jb20vdHJpYWwvY2FwdGNoYS5odG1s
// 官方隱藏地址
aHR0cHM6Ly9jYXN0YXRpYy5mZW5na29uZ2Nsb3VkLmNuL3ByL3YxLjAuNC9kZW1vLmh0bWw=
// 某紅書驗證頁面
aHR0cHM6Ly93d3cueGlhb2hvbmdzaHUuY29tL3dlYi1sb2dpbi9jYXB0Y2hh

數美不同類型驗證碼核心的 JS 都是一樣的,只是個別參數有微小差別,主要以滑塊為例來分析,通過 JS 代碼以及官方文檔可以看出數美是有無感驗證的,但是官網體驗地址里並沒有放出來,官方有一個隱藏地址,裡面的 demo 是最全的,包括無感,可以去上面給出的第二個地址里查看;數美的加密參數包含了 DES 加密演算法,參數名以及 DES Key 不定時會變化,本文也會分析如何利用 AST 來獲取動態的參數。

01

抓包分析

conf 介面,獲取配置,主要是獲取核心的 captcha-sdk.min.js 的地址,請求參數解釋:

參數 含義
organization 數美分配的公司標識,一般是每個網站唯一,寫死即可
appId 應用標識,區分不同應用,數美後臺可以管理
callback 回調參數
lang 語言,zh-cn 簡體中文、zh-tw 繁體中文、en 英文
model 模式,slide 滑塊、auto_slide 無感驗證、select 文字點選、icon_select 圖標點選、seq_select 語序點選、spatial_select 空間推理
sdkver 這個 sdk 版本是 captcha-sdk.min.js 內部寫死的
channel 推廣渠道,數美後臺可以管理
captchaUuid 32位隨機字元串,與業務方自身埋點數據配合,便於後續定位問題或進行數據統計
rversion captcha-sdk.min.js 版本號

02

返回結果重點看 captcha-sdk.min.js 文件地址,如下圖所示有個 v1.0.4-171,本文中我們稱 v1.0.4 為大版本,171 為小版本,小版本不定時會更新,版本號不斷升高。

03

然後就是 register 介面,不同類型,返回的數據都大同小異,其中 bg 是背景圖片,fg 是滑塊,文字點選、空間推理中 order 是提示信息,klrid 三個參數後續會用到。

04

05

06

最後就是 fverify 驗證介面,有類似下圖紅框中的 12 個參數,都是通過 JS 生成的,其參數名會根據 captcha-sdk.min.js 的變化而變化,其中有個最長的類似於下圖的 ep 值,包含了軌跡加密。返回值里參數解釋:

參數 含義
code 1100:成功;1901:QPS超限;1902:參數不合法;1903:服務失敗;9101:無許可權操作
riskLevel 處置建議,PASS:正常,建議直接放行;REJECT:違規,建議直接攔截

07

08

逆向分析

跟棧會發現核心邏輯在 captcha-sdk.min.js 里,這個 JS 類似於 OB 混淆(以前的文章介紹過,此處不再細說):

09

這裡可以自己寫 AST 還原一下,為了方便我們直接使用 v_jstools 解混淆:

10

然後替換掉原來的 captcha-sdk.min.js,如果你測試的是官網的體驗頁面,使用 Fiddler 替換時要註意可能有跨域問題,需要利用 Filters 功能,設置響應頭 Access-Control-Allow-Origin 欄位值為當前功能變數名稱:

11

12

如果你沒註意到這個跨域問題,可能會替換之後發現沒替換成功,原因是數美的資源有四個功能變數名稱,其中一個宕了便會啟用另一個,你替換其中一個報錯了就會自動跳轉另一個,所以看起來你並沒有替換成功:

13

PS:若替換的 JS 格式化了,那麼你在網頁上滑動也是校驗失敗的,因為 JS 里檢測了格式化,將 JS 壓縮成一行再替換即可,具體檢測的位置後文會講到。

captchaUuid

直接搜索關鍵詞下斷點,經過多次調試會發現第一個出現 captchaUuid 的地方是在 smcp.min.js,如下圖所示:

14

這裡的棧並不多,來回跟棧也沒發現是哪裡生成的,此時可以從初始位置也就是 embed.html 初始化驗證碼的地方開始單步跟:

15

單步跟進去會發現一個 getCaptchaUuid() 的方法,將此方法扣出來即可。

16

function generateTimeFormat() {
    var e = new Date()
    , t = function(n) {
        return +n < 10 ? "0" + n : n.toString();
    };
    return ((e.getFullYear().toString() + t(e.getMonth() + 1)) + t(e.getDate()) + t(e.getHours()) + t(e.getMinutes())) + t(e.getSeconds());
}

function getCaptchaUuid() {
    var c = "";
    var o = "ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678";
    var s = o.length;
    for (var a = 0; a < 18; a++) {
        c += o.charAt(Math.floor(Math.random() * s));
    }
    return generateTimeFormat() + c;
}

12 個加密參數

直接跟棧就很容易找到,如下圖所示的位置,D 就是生成的所有參數,此外,也可以通過搜索關鍵字 getEncryptContent 或者直接搜索參數名稱來定位。

17

可以發現上圖裡就有四個加密參數,都用到了 getEncryptContent 這個加密方法,加密方法傳入兩個參數,一個是待加密參數,一個是 DES Key,這四個待加密參數分別為 appId 值、channel 值、lang 值和一個 getSafeParams 方法。

18

重點跟進 getEncryptContent 方法看看,一個控制流,挑幾個重點的講一下,第一步是獲取一個 key,這個 key 是在前面設置的,後續會講到,實際上這個 key 沒啥用。

19

然後會有一個 isJsFormat 的格式化檢測函數,正常應該是 false 的,如果你格式化了就為 true,也就會導致 f 的值為時間戳加數美的功能變數名稱,這個 f 值後續是 DES 的 Key,不對的話自然怎麼滑都不會通過。

20

然後就是 DES 加密了,這個 DES 是標準的加密演算法,下圖中傳入的 1 和 0 表示的是加密,0 和 0 則表示解密,解密的情況也有,後續會遇到,modeECBpaddingZeroPadding,不需要 iv,可以直接扣代碼,或者直接引庫即可。

21

var CryptoJS = require("crypto-js")

function DESEncrypt(key, word) {
    var key_ = CryptoJS.enc.Utf8.parse(key);
    var srcs = CryptoJS.enc.Utf8.parse(word);
    var encrypted = CryptoJS.DES.encrypt(srcs, key_, {
        mode: CryptoJS.mode.ECB,
        padding: CryptoJS.pad.ZeroPadding
    });
    return encrypted.toString();
}

function DESDecrypt(key, word) {
    var key_ = CryptoJS.enc.Utf8.parse(key);
    var decrypt = CryptoJS.DES.decrypt(word, key_, {
        mode: CryptoJS.mode.ECB,
        padding: CryptoJS.pad.ZeroPadding
    });
    return decrypt.toString(CryptoJS.enc.Utf8);
}

這裡的四個值就分析完了,還有八個值是在前面生成的,如下圖所示 x 的值即為其他八個值,往前看是一個函數生成的,往裡面跟即可。

22

跟進來是一個 getMouseAction 方法,裡面先是挨個取值,後續會對這些值進行 DES 加密,下圖中的 a、c 參數就是 register 介面返回的 k、l 值,s 參數是對 register 介面返回的 k 值進行解密操作:

23

上圖中 u = this._data 裡面的值,根據滑塊、點選、無感模式的不同,也有所差異,以下代碼中,以 baseData 來表示 this._data 的值,根據模式的不同,可分為三類,大致構成如下:

滑塊(slide):

/* 
track:滑動軌跡(x, y, t),distance:滑動距離,randomNum:生成兩數之間的隨機值,示例:
var track = [[0, -2, 0], [62, 1, 98], [73, 4, 205], [91, 3, 303], [123, -3, 397], [136, 8, 502], [160, 0, 599], [184, 0, 697], [169, 0, 797]]
var distance = 169
 */

var baseData = {}
baseData.mouseData = track
baseData.startTime = 0
baseData.endTime = track[track.length - 1][2] + randomNum(100, 500)
baseData.mouseEndX = distance
baseData.trueWidth = 300
baseData.trueHeight = 150
baseData.selectData = []
baseData.blockWidth = 40

滑塊軌跡生成代碼:

def get_sm_track(distance):
    track_length = random.randint(4, 10)
    track = [[0, -2, 0]]
    m = distance % track_length
    e = int(distance / track_length)
    for i in range(track_length):
        x = (i + 1) * e + m + random.randint(20, 40)
        y = -2 + (random.randint(-1, 10))
        t = (i + 1) * 100 + random.randint(-3, 5)
        if i == track_length - 1:
            x = distance
            track.append([x, y, t])
        else:
            track.append([x, y, t])
    logger.info("track: %s" % track)
    return track

點選類(文字點選 select、圖標點選 icon_select、語序點選 seq_select、空間推理 spatial_select):

/*
coordinate:點選坐標(x, y),randomNum:生成兩數之間的隨機值,示例:
var coordinate = [[171, 101], [88, 102], [138, 109], [225, 100]]
 */

var baseData = {}
var time_ = new Date().getTime()
coordinate.forEach(function(co) {
    co[0] = co[0] / 300
    co[1] = co[1] / 150
    co[2] = time_
    time_ += randomNum(100, 500)
})
baseData.mouseData = coordinate
baseData.startTime = time_ - randomNum(800, 20000)
baseData.endTime = coordinate[coordinate.length - 1][2]
baseData.mouseEndX = 0
baseData.trueWidth = 300
baseData.trueHeight = 150
baseData.selectData = coordinate
baseData.blockWidth = undefined

無感(auto_slide):

/*
randomNum:生成兩數之間的隨機值
*/

var baseData = {}
baseData.mouseData = [[0, 0, 0]]
baseData.startTime = 0
baseData.endTime = randomNum(100, 500)
baseData.mouseEndX = 260
baseData.trueWidth = 300
baseData.trueHeight = 150
baseData.selectData = []
baseData.blockWidth = 40

這些值生成完了之後,就是挨個通過 getEncryptContent 進行加密,前面已經分析過,實際上就是 DES 加密,可以看到分為點選、滑塊和無感三類,其中 DES Key 也是會每隔一段時間變化的:

24

再往下走還有三個加密參數,待加密值是定值,然後將 s 的值(也就是前面 register 介面返回的 k 經過 DES 解密後的值賦值給了 this._data.__key)。

25

至此所有加密參數就搞完了。

結果驗證

26

27

28

29

30

31

AST 獲取動態參數

前面說了,/v1.0.4-171/captcha-sdk.min.js 文件地址,我們稱 v1.0.4 為大版本,171 為小版本,小版本每隔一段時間會更新,版本號會不斷升高,具體更新周期是多少?這裡推薦一個方法 document.lastModified,該方法記錄的是物理網頁的最後修改時間,我們直接訪問 JS 地址,就可以直接查看不同版本的 JS 是啥時候更新的了,多對比幾個版本,發現更新間隔時間並沒有太明顯的規律,如下圖所示:

32

33

34

不同版本裡面的 12 個加密參數的名稱和 DES 加密的 Key 都不一樣,我們可以利用 AST 來動態獲取這 12 個參數,經過測試,以下版本均可正常提取:

  • v1.0.4-148 ~ v1.0.4-171
  • v1.0.3-147 ~ v1.0.3-171
  • v1.0.1-147 ~ v1.0.1-171

截止本文發佈,小版本 171 為最新,v1.0.4 小版本從 148 開始,v1.0.3v1.0.1147 以前沒有混淆,可自行正則匹配,暫未發現其他大版本,如有遇到不能適配的,可聯繫我瞅瞅,完整的代碼在公眾號 k哥爬蟲 中,有需要的可以點擊下方鏈接。
【驗證碼逆向專欄】數美驗證碼全家桶逆向分析以及 AST 獲取動態參數

PS:此 AST 代碼僅實現對動態參數的提取,並非還原所有的混淆,提取出來的結果是有序、未去重的,後續按索引取就行。

35


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 做業務的時候經常忘記@RequestParam註解參數,記錄一下 首先,我們要清楚@RequestParam是乾什麼的 @RequestParam:將請求參數綁定到你控制器的方法參數上,路徑上有個參數+? @RequestParam註解參數: 語法:@RequestParam(value=”參數名” ...
  • 本文首發於公眾號:Hunter後端 原文鏈接:Django筆記三十八之發送郵件 這一篇筆記介紹如何在 Django 中發送郵件。 在 Python 中,提供了 smtplib 的郵件模塊,而 Django 在這個基礎上對其進行了封裝,我們可以通過 django.core.mail 來調用。 以下是本 ...
  • 不要跳過這部分知識,對瞭解 NodeManager 本地目錄結構,和熟悉 Container 啟動流程有幫助。 一、分散式緩存介紹 主要作用就是將用戶應用程式執行時,所需的外部文件資源下載緩存到各個節點。 YARN 分散式緩存工作流程如下: 客戶端將應用程式所需的文件資源 (外部字典、JAR 包、二 ...
  • ​ 本文分享一個給力的Java後端面試題網站:面試梯。 網址:https://offer.skyofit.com 這套題真實、高頻、全面、有詳細答案、保你穩過面試,讓你成為offer收割機。題目包括:Java基礎、多線程、JVM、資料庫、Redis、Shiro、Spring、SpringBoot、M ...
  • 3.1一個簡單的Java語言程式 這是程式雖然很簡單,但是所有的Java程式都具有這種結構,因此還是值得花一些時間來研究的。首先,Java區分大小寫。如果出現了大小寫拼寫錯誤(例如:將main拼寫成Main),程式將無法運行。 下麵逐行的查看這段源代碼。關鍵字pubilc稱為訪問修飾符(access ...
  • 基於Java的簡單圖書館管理系統實現,圖書租借管理系統,租借系統,springboot圖書館管理系統,大學圖書管理系統,圖書借閱系統,圖書館借閱歸還系統。 ...
  • 主用python做項目有一段時間,這次簡單總結學習下。為後面的項目編寫,進行一次基礎知識的查缺補漏、 1、變數名和數據類型 """ 變數名,只能由" 數字、大小寫字母、_ " 組成,且不能以數字開頭 """ # 整數 int # hashable,不可變對象 a = 5 # 浮點數 float # ...
  • Python有一個全球社區:https://pypi.org/,在這裡我們可以搜索任何主題的Python第三方庫。PyPI全稱是Python Package Index,指的是Python包的索引,它由PSF(Python Software Foundation)來維護,並且展示全球Python計算 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...