[验证码逆向]关于极验4验证通过却无法登录的问题
本文内容仅限于安全研究和学习,不公开具体源码。维护网络安全,人人有责。
在这个人手极验4的时代,我就不详细介绍极验的逆向了,但是我曾经遇到过一个问题,感觉挺有意思,便分享一下。
前言
在某个网站登录中,当我使用python生成的极验4验证参数去登录时,却返回验证码错误。起初我以为是登录接口的问题但是我使用网站生成的验证参数去登录却可以成功登录,我就明白是协议代码的问题了。
后来翻遍网上也没发现什么有用的信息,直到我看到极验4发布的一篇博客: https://blog.geetest.com/article/fbb906f609a4dd9773c7609ef48bf664
博客内容
其中"异常标记"功能引起我的注意,简单地说就是极验会在js里生成一个固定的键值对,极验服务器会校验该键值对,如果不相同则会返回一个"不正确"的验证参数。
键值对分析
该键值对在生成w值的e变量里。
e变量的值
直觉告诉我就是这个键值对的问题,当我把该键值对塞入代码里,重新登录,不出所料登录成功了。
深入研究发现该键值对与gcaptcha4.js
的版本有关。
这是该键值对在gcaptcha4.js
中的位置(v1.7.8)
键值对位置
于是我就将版本号和该键值对存在本地,并每次请求load
接口都会检查返回的版本,当于本地不相同时获取新的gcaptcha4.js
内容,并且用正则来查找该键值对(有条件的可以使用AST,我用正则是为了方便)
load返回的版本号
一下是获取键值对的部分代码
import re
# 最新的gcaptcha4.js
gt4_js = ""
# 获取键值对的主要内容
content = re.findall(r"!=typeof global\?global:this(.*?)\(\)", gt4_js)[0]
content = re.findall(r"{(.*?)}", content)[0]
# 获取键
key = re.findall(r"\"(.*?)\"", content)[0]
key = key.encode('utf-8').decode('unicode_escape')
# 获取混淆的值
n = re.findall(r"\(.*?\)", content)[0][1:-1]
# 解混淆函数,其实就是开头的那4个函数中的第3个
value = decrypt(n)
# 构造键值对
data = {key: value}
获取好键值对后可以保存在本地,下次就不需要重新获取了。
关于其他键值对
ep
该值在最近个版本中都是123
,当然,如果不放心也可以获取。
def get_ep(gt4_js):
"""获取ep值"""
# 使用正则表达式查找"\u0065\u0070": $_开头,)结尾的字符串
js = re.findall(r"\"\\u0065\\u0070\":\$_.*?\)", gt4_js)[0]
key = int(re.findall(r"\(.*?\)", js)[0][1:-1])
ep = decrypt(key)
return ep
biht: "1426265548"
该字符串较为固定,我已经几个月没见它变过了,其实这是在gct.js
里生成的并且和ep
有关,gct.js
也是有版本号的,这个网上已经有人研究过了,我就不过多赘述了。
def get_gct(ep, gct_js) -> dict:
"""在gct.js中获取e值中的随机值键对"""
function_name = re.findall(r"\)\)\{return (.*?)\(", gct_js)[0]
break_position = gct_js.find("return function(t){")
gct_js_new = gct_js[:break_position] + "window.gct=" + function_name + ";" + gct_js[break_position:]
gct_js_new = "window = global;" + gct_js_new + 'function getGct(){var e = {"lang": "zh", "ep": "' + ep + '"};window.gct(e);delete e["lang"];delete e["ep"];return e;}'
gct = execjs.compile(gct_js_new).call("getGct")
return gct
最后的运行结果
结果