-
-
[原创]挖洞遇到验证码那些事
-
发表于: 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期)
赞赏
- [原创]挖洞遇到验证码那些事 2216
- 攻防世界simple_js 3388
- [原创]挖洞遇到验证码那些事 2364