写在前面
验证码是我们做攻防对抗过程中经常会遇到的东西,在大部分核心场景,包括用户登陆,支付等环节,简单的会做个无感验证,但是如果发现异常用户,则会弹出图形验证码等形式。最近因为工作需要,对GeeTest验证码做了分析,今天抽空把整套流程再重新梳理一遍,供有兴趣的同学参考。
整体流程
XX验证码验证流程如下图:
暂时无法在飞书文档外展示此内容
- 发出注册请求Register.php,由服务端生成cid,challenge等参数,其中challenge是主要id,用于后续验证。
- 拉取验证码信息(get.php),根据注册时获取到的cid,challenge等信息,加密生成参数w,再将w作为固定请求的body数据,验证码服务器在接收到注册请求时,解密w数据,得到cid以及challenge,并生成验证码数据,再将验证码数据返回到客户端。抓包如下图,上面内容为request信息,下面内容为response信息。
- 验证码结果验证(ajax.php),客户端将用户处理的验证结果以及challenge,cid等信息一起加密生成参数w,再将参数w作为固定的body数据,返回到验证码服务器进行验证。
- 客户端会保存好challenge(具备有一定时效性,超过一定时间将会失效),后续的业务请求中会将challenge值一起携带到后端,后端再根据challenge到验证码服务器进行查询,确认第三部中验证是否合法。如果合法,则正常返回;否则,将返回相应错误码。
w参数计算
java层逆向,通过逐步调试分析,发现最终在调用com.geetest.sdk.a.a
函数后,生成的值会作为参数"w"。然而该函数是个native函数,因此需要继续分析so层。(sdk版本:4.0.8)
直接hook native函数com.geetest.sdk.a.a
可以发现,get.php请求时的w参数内容为:
json
{"challenge":"ffac10aa5b0937bae3cbd895b3ec39a4","cid":"0c326bd5edd071ba9b9dd10df201013f","client_type":"android","lang":"zh-CN"}
无感验证模式下,ajax.php请求时w参数内容为:
python
{"cid":"0c326bd5edd071ba9b9dd10df201013f","challenge":"ffac10aa5b0937bae3cbd895b3ec39a4","client_type":"android","light":"","serial":"xxxxxxxx","insight":"{"build":"1","release":"1.0","br":"100.0","bs":{"br":"1.0","bs":"5","plugState":"1","health":"2"},"coun":"CN","dh":"2203","dm":"google","dw":"1080","dn":"Pixel6","lang":"zh","mems":"7.90GB","ostype":"android","osver":"13","py":"1","ts":"1706768800000","vendor":"xxxxxx","app":"xxxx","gt3":"4.0.8","uuid":"xxxxxx"}","accessibility":{"voice":0}}
通过frida hook函数RegisterNatives
可以知道对应的函数地址,偏移为0x54f4(不同版本sdk的偏移有所不同)。
yaml
[INFO][fridaRegstNtv]: name: a, signature: (Ljava/lang/String;)Ljava/lang/String;, fnPtr: 0x78ae9114f4, modulename: libgee-lib.so -> base: 0x78ae90c000, offset: 0x54f4
ida逆向分析,发现可以直接F5,不过代码看着是做了一些简单的混淆,但是大体上逻辑还是能够猜得出来,入口函数如下:
- 随机生成16字节密钥。进入61行的函数,可以看出来
gen_rand_array16
函数是随机生成16字节大小的字符。
- 明文数据填充16字节对齐,对于长度少于16个字节,需要补满16个字节,补(16-len)个(16-len),从这特征可以看出来大概率是AES加密。
- 上图76行发现对前面随机生成的
aes_key
做加密处理,并且返回结果为128字节,由于RSA加密结果就是128字节,所以大概率就是用的RSA加密,之后结合动态hook方式获取RSA关键密钥即可。 - 字符串拼接,将加密后的数据以及RSA的128位密钥拼一起,再做base64运算。