【验证码逆向专栏】V5验证码逆向分析

【验证码逆向专栏】V5验证码逆向分析

在这里插入图片描述

声明

本文章中所有内容仅供学习交流使用,不用于其他任何目的,不提供完整代码,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关!

本文章未经许可禁止转载,禁止任何修改后二次传播,擅自使用本文讲解的技术而导致的任何意外,作者均不负责,若有侵权,请在公众号【K哥爬虫】联系作者立即删除!

前言

最近有粉丝反馈关于 v5 验证的相关问题,不知道 wss 协议的滑块应该如何下手,该网站通过 wss 协议传输进行验证码的校验,本文就来针对这个demo站进行逆向研究。

逆向目标

  • 目标:V5 验证Demo,滑块逆向分析
  • 地址:aHR0cHM6Ly93d3cudmVyaWZ5NS5jb20vZGVtbw==

逆向过程

抓包分析

打开 demo 地址,发现有智能和滑块俩种形式。本文对滑块验证进行分析。

7tML4j.jpg

点击按钮进行验证,我们发现它进行WS协议传输,总共有 6 条数据交互,其中3条发送到服务器之后,我们得到1条响应,之后又向服务器发送了一次,再次得到了响应。

7tMm0c.jpg

很明显发送和接收的消息都被加密了,拖动滑块进行校验,发现它又上传了一段密文,同时也对应接收到了消息,很显然这部分必然是校验的过程。

7tMp53.jpg

所以,接收和发送总共是8个ws数据交互,我们只需要将这8个分析完毕就可以解决。

逆向分析

三段上传包分析

看过往期 WS 协议文章的话,我们肯定知道 WS 首先找onopen ,我们从堆栈第一个进入。7tM3k5.jpg

我们发现它实例化了一个 WebSocket 对象,那它的入口必然就是这里,我们在这里下个断点,刷新页面,发现成功在这里断了下来。

7tMlbm.jpg

我们的首要目的就是找到onopen,既然它实例化了,那么 onopen 大概率就是在附近,果不其然在下面我们找到了这个方法。

7tM7P4.jpg
那么顺藤摸瓜,我们看看他内部消息加密是如何生成的,我们发现它内部只调用了 e 函数,那么我们进去。发现它又调用了 t 函数

7tMEFh.jpg

再次进入 t 函数,发现有参数构造且传参过程,如下:

7tMNq9.jpg

我们进入该函数中,看看是不是我们最终要找的消息加密的部分,进入后发现这个函数就是我们要找的消息加密主体,7tMqaY.jpg

通过 n = Cb(i) 获取加密明文,接着通过 e = f(i, o) 获取加密的 key ,发现 i=0 且是一串哈希值,通过得到的 key 对明文进行加密。接着进行进一步分析,i,o 来源自哪里,通过搜索可知,i 与 o 由页面html渲染,如下:

7jLRzB.jpg

明文由 requestId,token 以及一些浏览器参数构成,大致如下:

{"requestId":"Req_17483265394231","command":"7051CA8BF3E64DEDAA9334620DA8F5F1","data":{"l":"9dbf055d86******","f":"eab39387d6a2acf6a1d71c379a5ba746b61fc759","m":"c225b29a*******","j":"ES5","tl":5,"o":{"spm":"c225b29****","v5lid":"OrOeKo62lbu*******","userAgent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) ******","language":"zh-CN","colorDepth":24,"deviceMemory":8,"pixelRatio":1.25,"hardwareConcurrency":8,"screenResolution":[1536,864],"availableScreenResolution":[824,1536],"timezoneOffset":-480,"timezone":"Asia/Shanghai","platform":"Win32","canvas":"e1547d5eeb5c65***********","webgl":"164641c74782e718**********","touchSupport":[0,false,false],"audio":"124.04347527516074","j":"ES5"},"exfp":{"fpa":"902f0fe98719b779ea*******","fphc":"e1547d5eeb5c6506*******","fphg":"164641c74*****","fphf":"eab39387d6a2acf6a1d*******","fprt":"77c2955771*************"},"aux":{}}

l 值为页面token值,requestId由时间戳构成,其余参数可以自己进行伪造,最终通过AES进行加密,然后拼接分成3段进行WS 协议传输。

e= encryptData(n, e)
a = [];
a["push"]("7051CA8BF3E64DEDAA9334620DA8F5F1")
a["push"](token)
a["push"](e)

var c = a.join(""); // Join the array into a string
// console.log(c);

var C = 0;
var res = [];
while (c) {
    var e = null;
    // If the string is longer than 1024 characters, slice it
    if (c.length > 1024) {
        e = c.substr(0, 1024);
        c = c.substring(1024); 
    } else {
        e = c;
        c = null; 
    }
    // Format and push the chunk into the result array
    e = "0" + "|" + "3" + "|" + C + "|" + e;
    C++;
    res.push(e); 
    }

至此3段ws发送就分析完毕,经过我们最开始分析可知,发送之后会得到一段加密的密文返回,我们尝试 python 封装果真得到一段加密的密文返回。了解WS的知道,返回值的处理一般都在 onmessage 中,所以我们找 onmessage 事件处理器。

7jLtHb.jpg

单步跟进,就进入到了解密的关键地方,如下图:

7jLzUe.jpg

 a[ca](ob, function(e) {
                        var t = e[oa], n = a[wa], r = a[xa];
                        e = t;
                        r || (r = t.substring(0, 32), e = t.substring(32), a[xa] = r);
                        t = f(r, n);
                        e = h(e, t);
                        e = Bb(e);
                        t = e[fb] || qb;
                        t == qb ? (t = e[lb], n = a[S][t]) && (a[S][t] = null, delete a[S][t], (t = n[Gc]) && t(e)) : a[N](t, [ e ]);
                    });

解密的密钥是由返回的密文截取32位与token 通过熟悉的 f 方法来生成,接着进行常规的AES解密即可得到,python将操作进行复现:

7jL42P.jpg

解密后它给我们返回了一个 u 值,我们暂且不知道这个u有什么作用,大概率需要参与后面的计算。

获取图片

获取图片的上传接口,加密位置与三段ws传输位置相同,不同的是,加密key 的生成由返回的 u 值与token通过 f 方法生成。

7jLJQw.jpg

紧接着还是通过拼接token进行生成密文,需要注意 e 的数字索引不能写错,否则会报错。

e= encryptData(n, e)
a = [];
a["push"]("E97CE473AE1A46A8BF4A88FD73636D7E")
//a["push"](token)
a["push"](e)

var c = a.join("");
// console.log(c);

var C = 0;
var res = [];
while (c) {
    var e = null;
    if (c.length > 1024) {
        e = c.substr(0, 1024);
        c = c.substring(1024); 
    } else {
        e = c;
        c = null; 
    }
    e = "1" + "|" + "1" + "|" + C + "|" + e;
    C++;
    res.push(e);

}

最终得到图片密文,再次通过相同的解密方法进行密文的解密,不同的就是解密的 key 仍然是使用 u 值与token进行生成,最终成功得到图片数据。

7jLXc6.jpg

接口验证

拖动进行验证,同样还是在相同地方断住,轨迹明文如下:

7jLjnO.jpg

"1748328830478,90,15,-17,90,15,-17,105,16,-16,112,22,-15,120,32,-14,126,44,-14,134,55,-13,141,66,-13,149,74,-13,156,82,-13,164,86,-13,172,89,-13,179,91,-13,188,91,-13,205,91,-13,247,92,-13,247,94,-13,262,95,-13,262,97,-13,269,100,-13,277,103,-13,291,106,-13,299,108,-13,306,111,-13,314,113,-13,322,115,-13,329,118,-13,336,119,-13,344,123,-13,351,125,-13,359,127,-13,366,129,-13,374,131,-13,381,132,-13,404,134,-13,405,135,-13,426,137,-13,427,139,-13,434,142,-13,442,147,-14,449,151,-14,457,156,-15,472,162,-15,472,164,-15,547,167,-15,1748328831257"

初步分析,轨迹由时间戳,时间增量,x偏移,y偏移组成,经过可知时间增量不能与实际时间增量变化太大,轨迹生成如下:

import random
import time


def generate_slider_track(x_distance):
    # 生成开始和结束时间戳(当前时间)
    start_time = str(int(time.time() * 1000))
    end_time = str(int(time.time() * 1000) + 1000)
    track = [start_time]

    current_x = 0
    remaining_distance = x_distance
    y_base = -24  # 基础y坐标
    time_increment = 0

    while remaining_distance > 0:
        step = min(max(1, int(remaining_distance * random.uniform(0.1, 0.3))), remaining_distance)
        current_x += step
        remaining_distance -= step

        # 时间增量(随机但逐渐增加)
        time_increment += random.randint(30, 100)

        y_offset = random.randint(-3, 0)
        y = y_base + y_offset

        track.extend([str(time_increment), str(current_x), str(y)])

    if int(track[-2]) != x_distance:
        time_increment += random.randint(10, 50)
        track.extend([str(time_increment), str(x_distance), str(y_base)])

    track.append(end_time)

    track_str = ",".join(track)

    return track_str


# x_distance = 72  # 你想要移动的距离
# track_str = generate_slider_track(x_distance)
# print(track_str)

结果验证

7jLILQ.jpg

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值