如何破解与研究【领域专家系列】顶象验证码?

摘要:声明 原创文章,请勿转载!1、本文内容仅限于安全研究,不公开具体源码。维护网络安全,人人有责。2、如果想更系统地了解第三代验证码、更全面体会安全产品的设计,可以结合我的另几篇文章(1)《第三代验证码研究》https:www.cnblog
声明 原创文章,请勿转载! 1、本文内容仅限于安全研究,不公开具体源码。维护网络安全,人人有责。 2、如果想更系统地了解第三代验证码、更全面体会安全产品的设计,可以结合我的另几篇文章 (1)《第三代验证码研究》https://www.cnblogs.com/boycelee/p/11363611.html(推荐) (2)《顶象验证码破解与研究》https://www.cnblogs.com/boycelee/p/14269941.html(推荐) (3)《极验验证码破解与研究》https://www.cnblogs.com/boycelee/p/14021048.html(推荐) (4)《极验无感验证破解》https://www.cnblogs.com/boycelee/p/13951819.html (5)《同盾小程序指纹破解》https://www.cnblogs.com/boycelee/p/13899956.html 3、本文主要通过破解协议的方式绕过顶象安全验证,思路与网上自动化的方式有很大的不同 一、个人心路历程二、完整流程三、实例研究1、研究目标2、案例历史研究3、官网案例分析四、请求分析1、c1请求:请求顶象服务器,获取c参数。(1)请求参数示例 :(2)请求参数信息:(3)加密算法(4)返回参数(5)指纹本地存储2、a请求:获取图片与token2(1)请求参数示例:(2)返回参数:3、v1请求:获取token3(1)请求示例:(2)返回示例:4、check五、部分服务化源码greenseer.js部分服务化代码Java服务化代码六、服务化关键1、图片还原2、随机加密算法解析(重点设计部分)(1)语法树思路(2)举例子七、成果八、顶象无感验证流程(1)顶象无感验证流程图(2)顶象风控流程图九、总结十、最后
一、个人心路历程 (1)顶象的验证码真的非常棒。从2018年12月开始到2019年5月持续与顶象验证码对抗半年。 (2)js混淆与加密算法一天更换两次、滑块验证码干扰槽等应该都是在于我对抗中升级的。 (3)一年后回头看顶象的验证码产品又多了语序点选、刮刮卡验证、空间语义验证、乱序拼图验证、旋转验证、面积验证等验证码类型,在创新方面做得非常好,应该是国内数一数二的。 (4)与顶象的对抗,使我对安全产品有了更深刻、全面的认识。 (5)以下是我与顶象的对抗过程。 顶象攻防对抗时间线 二、完整流程 访问顶象官网,注册账号后登录控制台,访问“无感验证”模块,申请开通后系统会分配一个唯一的AppId、AppSecret。 当用户滑动验证码通过后,验证码服务会生成一个token,用户的业务请求带上这个验证码token,业务系统再调用后台 SDK 验证token的有效性。 顶象请求链路如下: 顶象-使用流程 三、实例研究 1、研究目标 当用户滑动验证码通过后,验证码服务就会生成token,用户携带token上传至顶象服务进行验证。我们的目标就是生成没有被标记为异常的token。 顶象-滑块验证码 2、案例历史研究 2018年12月研究的是国航的手机注册场景。不过由于国航该场景的验证码已经不再是滑块,所以后续可能需要使用官网案例来具体分析。 顶象-国航案例 3、官网案例分析 顶象-官网案例 从请求中我们可以看出,一共是三个请求(c1、a、v1)。我们需要弄清楚这三个请求分别的含义。(从设计角度个人觉得顶象相对于极验要简洁一些。个人觉得顶象的设计非常棒,在做公司的安全产品时,我也会参考一些顶象的设计) 四、请求分析 1、c1请求:请求顶象服务器,获取c参数。 目的:此处应该是计算设备指纹,通过设备指纹能够标识唯一设备信息 (1)请求参数示例 : 1428#X8m8K4SIcMDkTOm/r2P3j5vTyaRhmW3m9aksXQ8m2r13DgOarX1TmbmTMX13/vXXC9RmXS8m2XHfDg/uV8RP/4uaMuk8/WnUNX+6Xnah2rDfmZ3YXXwR0gpt2lJu07Nozs5jXY433RaCfCO6Y8VX/6aVU2mT6cuhX14q+2v8jcQ/awvNF8X1YuuJmzf3XXbXmCvq8M8uiwXSm3vhmyyuXXOiXmo8Ya21XXGjS8ft1qT2+Al3+Ab0wXWXmyf8aCO4WrrXj6flFh9p4PnSFPokuP5vXmuFn9fM8Am+JXvlT2TiW8QL88SXjtZykz8LUtZ9kAQpkd4pn2oXmpP9upuLVr2XvVTUjyTPTXWPCy+oTofzwi2oRX41sYk1sXWARiw1w32AOrfAe1fPTXVXivd2CFHaGgNezipHxx6cciF1xORN2iUklOpQzldUBvAxwBHXSoHBAGyUXXJ99ei8lxEoj2X8sJTvz/novxbofltai6k3wmXXjtyTItyoi8QsjUMIGI0siT3XYWKG9FkI54DQdTnXm00umH0KBTIXmACOT1rFmC8XmA2rTPuVmm/Xmg1R9usmM5/6XX+vqnlSylnDXX+jGnElyi3fXXphvMw444YeNUpw0TyXuFi3HFitgAiquUM3HXtquUM3HXwt1rXSuNMs1MJfuNrniBCkEnrfYmCsYuL5Y5Cf+nCkUFCkYdCSUFrSJXonuMoXZkrpk8nf3Ue/P9F0pmjG6Q9/DgBJzVZqcg6po/NleM6PXXVE8F9oSzdt++YgMiFBYxWbOUaHPXXS+2C5jY5/ZcmEaAvR+cxxjRQ+k8CoD1xHFcxSj6ovU270Dwyik9oBPTVXYNS86LTmha3t68oQPrXs3/ZLJMPTI2QLAtomXX07drAmu4omycphXrnXYbUG9GpOSg6yts0JX2XfTXVqvE26Y/COD9OqWA5IPaQTDVxc41r6mXXn8Xo6JyIPJPyuXYQim8OYJX3tu6ci/TX3muOg/Lm8//nuY9nljN/sX3vTXX4qX1nIa//1Yc9x (2)请求参数信息: 只展示部分数据 1"supportAddBehavior":"ab","adblock":"adb","availResolution":"ar","canvasFP":"can","cpuClass":"cc","colorDepth":"cd","cookieEnabled":"ce","canPlayType":"cpt","collectTime":"ct","doNotTrack":"dnt","deviceMemory":"dm","languages":"lugs","mimeTypes":"mts","mediaDevices":"mds","platform":"np","supportOpenDatabase":"od","devicePixelRatio":"pr","resolution":"res","plugins":"rp","supportSessionStorage":"ss","timezoneOffset":"to","touch":"ts","userAgent":"ua","webgl":"web","webgl2":"gi" 通过加密以上操作系统与浏览器以及网络数据等信息生成c1请求参数param。 (3)加密算法 顶象-加密算法index index.js?_t=xxxx中,进行每日更新。那么该如何确保每日都能获取到准确的加密算法呢?通过解析原语法树,生成我们想要的语法树。(这个后续讲) (4)返回参数 1{"data":"f8839e00435f2e05f9ed60b3d3c5498554cb367655ec6e7318adefda150437040a74963c","msg":"lidinvalid","status":-4}//会根据指纹情况对指纹进行风险等级判断 (5)指纹本地存储 顶象会将指纹进行多地存储,例如cookie、Session Storage、Local Storage等 2、a请求:获取图片与token2 顶象-获取图片 (1)请求参数示例: 1de=0&wp=1&aid=dx-1547996895410-3601284-1&jsv=1.3.11.98&c=5c3c65a6uSNGXiEhdEwxwwICxBq5Qdjdky4kxjo1&ak=5f6727ec854786a86cd4c3c171d13499&s=50&h=150&w=300&_r=0.9866721061865382 wp:表示图片类型。非常重要,因为通过该参数选择图片类型,省去了我们取转换webp格式图片(0表示jpg,1表示webp。目前只有chrome支持,safari不支持webp格式)。 aid:时间戳+随机数+1。目前猜测其目的是number once。 jsv:表示版本号。 c:c1请求返回参数,可以理解为第一阶段token1。 ak:appKey,顶象会为每一个接入其无感验证码的server提供一个appKey,以便标识他们是哪一个服务。 s、h、w:描述图片的长宽等信息。h与w非常重要,因为获取的图片信息是200*400,所以我们在计算距离时,应该按比率缩小,否则滑块无法通过。 _r:number once。 (2)返回参数: 1{"sid":"86ae78ed0fba04f8384f3c9376271b0d","y":30,"success":true,"p1":"/dx/ib3oV3MeuO/zib3/b5cce61f91c6447bbc2f10e7836a7827.webp","p2":"/dx/ib3oV3MeuO/zib3/e34db2ab70064b2c998d14159f0b8ff8.webp","p3":"/dx/ib3oV3MeuO/zib3/50969dc0b2f14d118fcf166dc0871021.webp","msg":null,"t":null,"result":1,"type":0,"logo":null} sid:token2。 y:y坐标 p1:不完整图片 p2:拼图块 p3:完整图片 注意:如果该请求错误,可能会返回另一种图片验证码(点击类型) 3、v1请求:获取token3 (1)请求示例: 1y:30x:33aid:dx-1547997910506-78792589-1sid:75dc20f81b2bd0ff871b9f12e54ef2c8jsv:1.3.11.98c:5c448ee09pUgUAwgiaRMmhhDea79K4O1B7oQhRh1ak:5f6727ec854786a86cd4c3c171d13499ac:492#X8Xn8AQv/Y6pdvgYXXfOuMffR/W3XjVgMOYfQntkaQbRZ/smuRlFpvD6L+qHM21JPdOjXTgzLGKauxS0c29jXCAeYaTRl589z9Ug+vZ1YDXIBNwrm8X1k6vEf3rKmrXmS2WXmFPMgH3nXX8XXXXmY8XEJ37H/cWc68WnDdW+DXSjXtXZj9c6v33n6aa6W9bhYu/c8XIXui83m5U1gFUJzxqJzxoGUqNSYXpFY2Xio3O5Ja/dw/NP2X5X3oFjcWf2r9znqaJQ2LQr7homIr5X3oFPjnXMr9znqaJQ2LQr7homIr2XsBHwJhSSRygCnh3ufTc1v/8V8YZsiz/D4raoj9XLRtOYv9a53D2kZcWV8YcHUAa/mrXmS25X3oFPjvS2r9znqaJQ2LQr7homIr5XjoFPjvrMr9DIGaUiwYmYYrXslqs+7bRz2kEuonW= y:坐标,a请求会返回,所以不需要自己计算。 x:坐标,需要计算。 aid:时间戳+随机数+1。目前猜测其目的是number once。 jsv:表示版本号。 c:c1请求的返回值,理解为token1 ak:appKey,顶象会为每一个接入其无感验证码的server提供一个appKey,以便标识他们是哪一个服务。 ac:加密信息。包括时间戳、操作系统对应编码、Referer、特殊加密算法(随机取加密算法中一段并加密)、JSV、token(sid)、是否使用headless、浏览器版本、屏幕信息、服务网址、版本、检测是否使用webdriver等方式抓取、token、鼠标轨迹等进行数据加密。 (2)返回示例: 1{"success":true,"token":"37AD877DC3953CA1116E487DFBF6DB435DFE7C4CD204B8DC861D5857BE2FDDCF4565648C95BAFD15030AA555713B5DA1FD5D57C82427F4C7D222E5990F47AB83B3F13ED08965032CFF26FA37B82012FF","msg":null,"tp":null,"sv":null,"retry":0} 返回token则表示成功。但注意此时token未必有效,如果c1请求使用appKey与v1请求使用的不同,是无法通过check校验的。 4、check 在获取token之后会通过check接口,进行token时效性与正确性校验。使用方通过调用顶象的open api进行token校验。通过则说明,验证码完成。 五、部分服务化源码 greenseer.js部分服务化代码 1functiongetParamUa(sid,position,referer,browserVersion){ 2var_ua=""; 3varua=""; 4vartm=newDate().getTime(); 5deleterequire.cache[require.resolve('../builder/dingxiang_greenseer_finish.js')]; 6varencryptionFunc=require('../builder/dingxiang_greenseer_finish.js'); 7varreplace_encrypt=encryptionFunc.replace_encrypt; 8 9deleterequire.cache[require.resolve('../builder/dingxiang_version_finish.js')]; 10varversionFunc=require('../builder/dingxiang_version_finish.js'); 11varreplace_version=versionFunc.replace_encrypt; 12letmouseMove=buildMouseMove(position); 13letmouseMoveEvent=buildMouseMoveEvent(mouseMove); 14letmouseDownEvent=buildMouseDownEvent(mouseMoveEvent); 15app=function(u,c){ 16.... 17ua.replace("xxxxxxxxx",""); 18} 19process=function(t){ 20varc=[].slice.call(arguments); 21returnt=c.length===1&&(0,isArray)(t)?t:c,t=(0,flatten)(t),(0,toStr)(t) 22} 23getTM=function(){ 24vara=process((0,replace_bs8)(tm)); 25app(1,(0,replace_encrypt.encryptTM)(a)) 26} 27//浏览器版本 28getBR=function(browserVersion){ 29.... 30app(2,(0,replace_encrypt.encryptBR)(a)) 31} 32//屏幕信息 33getSC=function(){ 34.... 35app(3,(0,replace_encrypt.encryptSC)(n)) 36} 37//网址 38getLO=function(referer){ 39.... 40app(4,(0,replace_encrypt.encryptLO)(i)) 41} 42//函数 43getCF=function(){ 44letcfParam=buildCFParam(); 45.... 46app(5,(0,replace_encrypt.encryptCF)(s)) 47} 48//是否开启控制台 49getDI=function(){ 50.... 51app(6,(0,replace_encrypt.encryptDI)(a)) 52} 53//检测是否使用webdriver等方式抓取 54getEM=function(){ 55.... 56app(7,(0,replace_encrypt.encryptEM)(s)) 57} 58//版本 59getJSV=function(){ 60.... 61app(8,(0,replace_encrypt.encryptJSV)(o)) 62} 63//token 64getTK=function(sid){ 65.... 66app(9,(0,replace_encrypt.encryptTK)(a))) 67} 68getMM=function(mouseMove){ 69varv=mouseMove.eventName 70,h=mouseMove.tm 71,d=mouseMove.x 72,p=mouseMove.y 73.... 74this.app(11,(0,replace_encrypt.encryptMM)(l)) 75} 76getMD=function(mouseDown){ 77varf=mouseDown.eventName 78,s=mouseDown.button 79,v=mouseDown.tm 80,h=mouseDown.x 81,d=mouseDown.y 82.... 83this.app(12,(0,replace_encrypt.encryptMD)(p)) 84} 85var_sa=[]; 86recordSA=function(mouseMove){ 87vari=mouseMove.tm 88,u=mouseMove.x 89,c=mouseMove.y 90.... 91_sa.push((0,replace_encrypt.encryptSA)(f)) 92} 93sendSA=function(r){ 94this.app(17,r); 95} 96sendTemp=function(t){ 97varn=process((0,replace_bs2)(t.length),(0,replace_bss)(t)); 98app(10,(0,replace_encrypt.encryptTEMP)(n)) 99} 100 101vartm=getTM(); 102varbr=getBR(browserVersion); 103varLO=getLO(referer); 104varCF=getCF(); 105varDI=getDI(); 106varEM=getEM(); 107varJSV=getJSV(); 108varTK=getTK(sid); 109varSC=getSC(); 110varMM=getMM(mouseMoveEvent[mouseMoveEvent.length-1]); 111varMD=getMD(mouseDownEvent[0]); 112varDI=getDI(); 113for(letmveIndex=mouseMoveEvent.length-2;mveIndex>=0;mveIndex--){ 114varMM=getMM(mouseMoveEvent[mveIndex]); 115} 116for(letmvIndex=mouseMove.length-1;mvIndex>=0;mvIndex--){ 117varSA1=recordSA(mouseMove[mvIndex]); 118} 119for(letsaIndex=0;saIndex<_sa.length;saIndex++){ 120varSA2=sendSA(_sa[saIndex]); 121} 122vartemp=sendTemp(position); 123returnua; 124} Java服务化代码 1@Override 2publicCrackResultcrackDingXiang(DingXiangParamdingXiangParam,IntegercaptchaType,Stringcaller)throwsIOException{ 3Preconditions.checkNotNull(dingXiangParam,"crackDingXiangdingXiangParamisnull"); 4Preconditions.checkNotNull(StringUtils.isNotBlank(caller),"callerisnull"); 5 6StringuserAgent=HeaderBuilder.buildChrome(); 7StringbrowserVersion=getBrowserVersion(userAgent); 8dingXiangParam.setUserAgent(userAgent); 9//Lid请求 10StringlidResult=getDingXiangLidFunc(dingXiangParam); 11dingXiangParam=changeDingXiangParam(dingXiangParam,lidResult); 12//C请求 13StringcFunctionResult=getDingxiangCFunc(dingXiangParam); 14CFunctionResponsecFunctionResponse=buildCFunctionResponse(cFunctionResult); 15AFunctionParamaFunctionParam=buildPictureRequestParam(cFunctionResponse,dingXiangParam); 16//A请求 17StringaFunctionResult=getDingXiangAFunc(aFunctionParam); 18AFunctionResponseaFunctionResponse=buildAFunctionResponse(aFunctionResult); 19//分析图片 20PictureAnalysisParampictureAnalysisParam=buildPictureAnalysisParam(dingXiangParam,aFunctionResponse,browserVersion); 21StringpictureAnalysisResult=analysePicture(pictureAnalysisParam); 22PictureAnalysisResponsepictureAnalysisResponse=buildPictureAnalysisResponse(pictureAnalysisResult,pictureAnalysisParam); 23//V请求 24VFunctionParamvFunctionParam=buildVFunctionParam(pictureAnalysisResponse,aFunctionParam,aFunctionResponse,dingXiangParam); 25List<NameValuePair>vFunctionPostParam=buildDingXiangVFuncPostParam(vFunctionParam); 26StringvFunctionResult=getDingXiangVFunc(vFunctionPostParam,dingXiangParam); 27VFunctionResponsevFunctionResponse=buildDingXiangVFuncResponse(vFunctionResult); 28CrackResultcrackResult=buildCrackResult(vFunctionResponse,cFunctionResponse,dingXiangParam,captchaType); 29 30returncrackResult; 31} 六、服务化关键 1、图片还原 顶象-图片还原 2、随机加密算法解析(重点设计部分) 加密算法会随着js混淆的改变而改变,如何服务化? 顶象-随机算法解析 (1)语法树思路 顶象-语法树思路 (2)举例子 (1)未处理的index.js。 顶象-未处理index (2)创建模板并生成其语法树。 顶象-处理后生成语法树模板 (3)将加密算法转化成语法树-> 与模板语法树结合 -> 生成新的语法树 -> 生成新的js文件 极验完整流程 (4)启用定时任务,每天固定时间去生成新的加密算法js文件。 七、成果 顶象-成果 八、顶象无感验证流程 (1)顶象无感验证流程图 顶象-无感验证码流程图 (2)顶象风控流程图 顶象-验证流程图 九、总结 (1)顶象相对于极验产品设计更加清晰。步骤非常明确 "设备数据->行为数据->验证"; (2)通过每日更新混淆js文件、加密算法提升破解服务化难度; (3)通过验证码下发策略,进行验证码种类变换,提升验证码识别难度; (4)验证码产品创新能力强,例如:空间语义验证、乱序拼图验证、面积验证、旋转验证等; (5)整体而言,顶象对于验证码的理解应该是业界领先的。 十、最后 1、目前国内在安全产品方面的文章较少,我们很难全面了解与学习安全产品,希望我的文章能够帮助到更多人。 2、如果想系统地了解第三代验证码,可以结合我的另几篇篇文章 (1)《第三代验证码研究》https://www.cnblogs.com/boycelee/p/11363611.html(推荐) (2)《顶象验证码破解与研究》https://www.cnblogs.com/boycelee/p/14269941.html(推荐) (3)《极验验证码破解与研究》https://www.cnblogs.com/boycelee/p/14021048.html(推荐) (4)《极验无感验证破解》https://www.cnblogs.com/boycelee/p/13951819.html (5)《同盾小程序指纹破解》https://www.cnblogs.com/boycelee/category/1819211.html 3、本文不提供完整解决方案和完整数据,仅用于理论研究,维护网络安全,人人有责。