首页
社区
课程
招聘
[原创]挖洞遇到验证码那些事
发表于: 2023-8-11 22:20 2215

[原创]挖洞遇到验证码那些事

2023-8-11 22:20
2215

平台:aHR0cHM6Ly93d3cudnVsYm94LmNvbS8=公益SRC

厂商:zay7qMuz

链接:aHR0cHM6Ly91cGFzcy4xMGpxa2EuY29tLmNuL2xvZ2lu

文章搭配视频地址:挖洞遇到验证码那些事

输入手机号码与账号密码登录的接口都有滑块。区别在于账号密码登录的时候有个加盐密码。流程比手机号码的接口复杂一下,为了省时间。此文章便是根据手机号码的接口去写的,毕竟咱的目的是过滑块嘛。

每次刷新验证码,都会发四个数据包下来。第一个数据包用于请求图片。图片链接的拼接就是urlParams加上imgs的值。

第三个数据包是背景图

第四个数据包是缺口图片

正确拼接验证码,短信验证码发送成功

captcha_signature在刷新验证码发下来的数据包里面的sign参数。

captcha_phrase和captcha_ticket在getTicket接口从服务器返回回来的,captcha_phrase是缺口,如果缺口对齐失败的话captcha_ticket是获取不到的。所以得先把getTicket接口的参数补齐拿到ticket。

getTicket的前五个参数直接从getPreHandle接口返回的urlParams获取。

phrase直接用搜索大法就能搜到关键位置

x是缺口识别的x轴,opt.width和opt.height是固定的值

inity也是直接在js文件一搜就能拿到他的关键代码。data.data.inity在getPreHandle接口里面的inity。这段代码就是取那个inity相除相乘再拼接到phrase里头

x直接就是缺口识别然后减去15(因为图片实际大小跟他在网页上面的大小不一样),懒得缩小了。因为通过训练相同的图片跟缺口其x就相差15,直接减更方便(懒人做法)

图片缺口识别代码:

mobile加密就是一个rsa加密,没有任何混淆跟难点。

验证码校验接口还有个hexin-v参数

校验验证码的数据包里面有个加密的header参数hexin-v,他跟cookie的v是一模一样的。有两种方法可以找到关键加密位置,第一种是headers hook,第二种是cookie hook。

cookie hook代码:

在控制台输入hook的代码

断点断了下来。回溯堆栈找到关键代码加密的地方

w函数便是设置cookie的函数,r就是v加密生成返回的值。那么qn.update()就是v生成的函数了。把断点下到qn.update()那一行然后刷新。由于这个网站的js文件是动态生成的,所以需要重新进入新的js文件在qn.update()那一行重新下个断点刷新。

断点断下之后进入qn.update()看看里面写了啥

进入到qn.update()函数内部可以看出他返回了另外一个函数的返回值。

把断点断到R函数这一行进入到R函数内部。

R函数往E对象里面压入一堆值。找找E在哪里生成的。

E对象是在b函数里面被赋值的,但因作用域的原因,他并不是在这里声明的,E声明的地方还得往上面去找。

在945行找到E声明的地方,同时也发现了这一段大的函数是一个自执行的函数。

开始扣代码,就只扣需要的代码,R()是v加密后的值,R函数里面有E对象,E对象是在b函数里面被实例化的。b函数也扣下来,b函数里面调用了C函数。C函数也一并扣下来。因为E对象是全局作用域,其声明的代码在几个函数的外面。

代码运行报错,r未定义。那么e,t,n是否也是未定义。在浏览器运行一下看看是什么呢

在控制台打印r,e,t,n。发现他们都是大数组。直接扣下来就行了

运行报window,document未定义。在开头定义一下为空对象

运行报Kn未定义

把断点下到报错代码那一行,进去Kn函数内部

运行报错Y未定义。

浏览器查看Y是字符串“1”,直接在代码中写固定就行

报这个错的原因是因为没有调用b函数,在R函数的开头调用运行一下b函数。

对象实例化Un函数是未定义的。去浏览器扣。

浏览器扣下来的Un函数

运行报Xn未定义,分析代码的作用是把函数赋给对象,那么i后面跟着的肯定是字符串,把函数赋给某某对象原型链下面的某个属性。

替换即可。

运行报Gn未定义,直接进入Gn.serverTimeNow()函数内部查看写了什么。

浏览器扣下来的代码,看看这段代码有什么作用。

cn函数有一个三元表达式,TOKEN_SERVER_TIME是数值,x + T + C是"undefined",TOKEN_SERVER_TIME == x + T + C 是false

所以执行(time = parseInt(TOKEN_SERVER_TIME),time)

这段函数的整体意思是将TOKEN_SERVER_TIME取整赋值给time,再把time值返回。

E[i] = Gn.serverTimeNow()直接改写成E[i] = parseInt(TOKEN_SERVER_TIME)

运行代码报错TOKEN_SERVER_TIME未定义,在浏览器搜索TOKEN_SERVER_TIME。

在第一行就可以看到TOKEN_SERVER_TIME。直接扣下来

运行报错Wn未定义。这段C函数代码要是直接去扣就入坑了,先看看这个C函数有什么作用。

第一行代码就是取cookie名为v的值,如果没取到cookie的v值就去取headers的"hexin-v"值。

一般情况下都不是第一次运行,那么他就会取得到前面cookie的v值。那么下面的if判断就会成立,就会执行。但是我们总不可能每次运行代码都去取他前面的cookie,那实在是太复杂了。假设,我是说假设。假设我每一次进去这个网站都是第一次,都取不到cookie的v值和headers的hexin-v。那么这段代码又是有什么样的作用呢。

cookie的v和headers的hexin-v都取不到值的话那么if里面的就是null。就直接执行后面的代码

最后C函数的代码就是直接改写成E[o] = Gn.random()

运行报错,直接扣Gn.random()

C函数可以值改写成

Dn报错

Dn是数值0

xn未定义

xn是数值3

报了Gn的错。分析这段代码就是把浏览器指纹ua传进了strhash函数。可以直接取最后的值写固定。

后面的报错都是这几个值未定义。

getBrowserFeature—>获得浏览器特性

getPlatform—>获取浏览器平台

getBrowserIndex—>获取当前网页所在浏览器的索引

getPluginNum—>获取当前网页或浏览器环境中已安装或可用的插件数量。

至于改写的话直接取最后的值就行了

b函数都改完之后到R函数报错了。Gn.serverTimeNow()在b函数就已经扣过了

Gn.serverTimeNow()—>parseInt(TOKEN_SERVER_TIME)

Gn.timeNow()未定义

进入Gn.timeNow()函数内部是个on函数

on函数的作用就是取当前时间戳除以1000再无符号右移0位

改写后的代码

后面的代码都是未定义的

Dn是数值0,n是字符串"DOMMous"

getMouseMove—>获取鼠标当前的横坐标和纵坐标

getMouseClick—>获取鼠标点击时的横坐标和纵坐标,以及所点击的按钮

getMouseWhell—>获取滚轮事件的信息

getKeyDown—>获取按键事件的信息

getClickPos—>获取鼠标点击的坐标,然后进行相应的操作,比如记录点击位置或者根据点击位置执行特定的逻辑。

直接取最后运行完的值

运行Qn.encode未定义

进入函数内部Qn对象总共有4个方法,直接把Qn对象补齐.

运行报错

在浏览器打印o+C是字符串"length",直接替换

在浏览器打印Vn(t[65],r[56])是字符串"100",直接替换

后面几个都是未定义的,在浏览器找到对应的值替换

直接拿运行之后的结果进行替换

Xn(q, t[66], In)—>"ff"

G—>"w"

Xn(e[66], r[58], S)—>"3f"

B + i—>"3f"

Vn(r[26], n[56])—>""

代码再次执行,值便出来了。

带上值去请求一遍。手机验证码发送成功

在网上看到一堆文章都又说直接用js-dom包补document和window两个环境。

直接把整个js文件复制下来

定义一个全局变量接受v的值,方便后面导出。

运行代码报window未定义

导包

运行代码报document未定义

运行代码报navigator未定义

打印值出来了,报错不管他。直接用py调用。也是成功的!

本文章仅供学习交流使用,如有侵权,联系我删除。

phrase: (x) + ";" + inity + ";" + opt.width + ";" + opt.height
phrase: (x) + ";" + inity + ";" + opt.width + ";" + opt.height
inity = (data.data.inity / 195 * opt.height);
inity = (data.data.inity / 195 * opt.height);
def detect_captcha_gap(bg, tp): 
    ''' 
    bg: 背景图片 
    tp: 缺口图片 
    return:空缺距背景图左边的距离 
    '''    # 读取背景图片和缺口图片 
    bg_img = cv2.imread(bg) # 背景图片 
    tp_img = cv2.imread(tp) # 缺口图片 
    # 识别图片边缘 
    bg_edge = cv2.Canny(bg_img, 100, 200
    tp_edge = cv2.Canny(tp_img, 100, 200
    # 转换图片格式 
    bg_pic = cv2.cvtColor(bg_edge, cv2.COLOR_GRAY2RGB) 
    tp_pic = cv2.cvtColor(tp_edge, cv2.COLOR_GRAY2RGB) 
    # cv2.imwrite("bg_style.png",bg_pic) # 保存背景轮廓提取 
    # cv2.imwrite("slide_style.png",tp_pic) # 保存滑块背景提取 
    # 缺口匹配 
    res = cv2.matchTemplate(bg_pic, tp_pic, cv2.TM_CCOEFF_NORMED) 
    min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)  # 寻找最优匹配 
    th, tw = tp_pic.shape[:2
    tl = max_loc  # 左上角点的坐标 
    # 返回缺口的左上角X坐标 
    br = (tl[0] + tw, tl[1] + th)  # 右下角点的坐标 
    cv2.rectangle(bg_img, tl, br, (0, 0, 255), 2# 绘制矩形 
    cv2.imwrite("result_new.png", bg_img)  # 保存在本地 
    # 返回缺口的左上角X坐标 
    return tl[0
x = detect_captcha_gap("bg.jpg","tp.jpg")-15
def detect_captcha_gap(bg, tp): 
    ''' 
    bg: 背景图片 
    tp: 缺口图片 
    return:空缺距背景图左边的距离 
    '''    # 读取背景图片和缺口图片 
    bg_img = cv2.imread(bg) # 背景图片 
    tp_img = cv2.imread(tp) # 缺口图片 
    # 识别图片边缘 
    bg_edge = cv2.Canny(bg_img, 100, 200
    tp_edge = cv2.Canny(tp_img, 100, 200
    # 转换图片格式 
    bg_pic = cv2.cvtColor(bg_edge, cv2.COLOR_GRAY2RGB) 
    tp_pic = cv2.cvtColor(tp_edge, cv2.COLOR_GRAY2RGB) 
    # cv2.imwrite("bg_style.png",bg_pic) # 保存背景轮廓提取 
    # cv2.imwrite("slide_style.png",tp_pic) # 保存滑块背景提取 
    # 缺口匹配 
    res = cv2.matchTemplate(bg_pic, tp_pic, cv2.TM_CCOEFF_NORMED) 
    min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)  # 寻找最优匹配 
    th, tw = tp_pic.shape[:2
    tl = max_loc  # 左上角点的坐标 
    # 返回缺口的左上角X坐标 
    br = (tl[0] + tw, tl[1] + th)  # 右下角点的坐标 
    cv2.rectangle(bg_img, tl, br, (0, 0, 255), 2# 绘制矩形 
    cv2.imwrite("result_new.png", bg_img)  # 保存在本地 
    # 返回缺口的左上角X坐标 
    return tl[0
x = detect_captcha_gap("bg.jpg","tp.jpg")-15
(function () {
    'use strict';
    var cookieTemp = "";
    Object.defineProperty(document, 'cookie', {
        set: function (val) {
            console.log('Hook捕获到cookie设置->', val);
            if (val.indexOf('v') != -1) {
                debugger;
            }
            cookieTemp = val;
            return val;
        },
        get: function () {
            return cookieTemp;
        }
    });
})();
(function () {
    'use strict';
    var cookieTemp = "";
    Object.defineProperty(document, 'cookie', {
        set: function (val) {
            console.log('Hook捕获到cookie设置->', val);
            if (val.indexOf('v') != -1) {
                debugger;
            }
            cookieTemp = val;
            return val;
        },
        get: function () {

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 5
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//