前端无秘密:看我如何策反JS为我所用(下) – 作者:yangyangwithgnu

近日,参加金融行业某私测项目,随意选择某个业务办理,需要向客户发送短信验证码:

1607309185_5fcd978122b3f5f65ec51.png!small?1607309190058

响应报文中包含大段加密数据:

1607309193_5fcd978967899a7c73501.png!small?1607309198453

全站并非全参数加密,加密必可疑!尝试篡改密文,页面提示“实名认证异常”:

1607309201_5fcd9791123ccf8809c02.png!small?1607309206035

猜测该密文涉及用户信息,且通过前端 JS 解密,验证之。

前端无秘密:看我如何策反 JS 为我所用(上)

武器化利用

分析清楚漏洞详情,接下来一定是将手工利用转变为自动攻击,实现武器化,才能将战果最大化。

武器化,我有两个选择:一是复用报文,对 PHONE_NO 参数加载手机号码字典,借助 python 的 requests 库,访问 /xx/api/xxxx/h5/xx/sChkBlPhone 接口获取 Data,调用前面已实现的解密脚本,批量获取用户信息;二是复用页面,驱动 webdriver,模拟人工操作,输入手机号、点击“获取验证码”按钮、抓包获取 Data、解密脚本,批量获取用户信息。粗略分析,前者运行高效、后者实现简单。选择一还是二呢?~( ´•︵•` )~,我都要!

2.1 复用报文方式

我计划基于已有原始请求,用脚本不断填写新 PHONE_NO 参数后提交,获取不同用户的个人信息。要让这条路可行,必须具备两个前提,服务端未限制篡改参数、服务端未限制重放请求。

2.1.1 防篡改与防重放

我在页面上输入手机号 13988888840,点击“获取验证码”按钮,用 burp 的 proxy 抓包拦截请求(不放),将 PHONE_NO 参数值改为 13988888849 后放行:

1607327740_5fcddffc930befbc35c61.png!small?1607327740949

报错“参数签名异常”,说明存在参数防篡改的限制。1607327746_5fcde0028d7b3ca11bb7b.png!small?1607327747041

刷新页面,我重新在页面上输入手机号 13988888840,点击“获取验证码”按钮,用 burp 的 proxy 抓包拦截请求(不放),将该请求转至 burp 的 repeater,对报文不作任何修改,第一次发送,响应 200,可获取 Data,第二次,响应 412,无法获取 Data:

1607327859_5fcde0732f6d7ec730440.png!small?1607327859595

报错“条件不达标”(Precondition Failed),说明存在请求防重放的限制。

服务端是如何晓得我在篡改参数、重放请求呢?肯定离不开客户端的配合。于是,我仔细审查请求报文中的 headers,首部 authorization 引起了我的注意:

1607327874_5fcde08204f613f442a66.png!small?1607327874309

怀疑是 sign 在作祟。

我重新从页面输入 13988888840,点击“获取验证码”按钮,抓包拦截请求,首部 authorization 如下:

authorization: origin=2|appkey=200000056|token=null|ts=1605169433400|noncestr=K2FZpfbe|sign=40ca525898eba6df88bca451342515c1

这次不对 PHONE_NO 参数值作任何变更,只把 sign 最末尾的字符从 1 改为 2(即 40ca525898eba6df88bca451342515c2),同样报“参数签名异常”的错:

1607327894_5fcde096903b6f34e2601.png!small?1607327895106

基本上验证了我的猜测,业务系统的防重放和防篡改能力依赖 sign 参数。我得想法绕过防御机制。

业务系统的防御,我大致了解(谁还没点在蓝队背景 <(▰˘◡˘▰)>)。客户端对所有请求参数进行哈希计算,得到参数签名(sign),将签名放入首部 authorization 中提交至服务端,服务端基于相关信息生成签名,与客户端提交的签名进行比较,若不同,说明参数被篡改,则不响应该请求,若相同则响应。签名用后即废,若重复,说明请求被重放,则不响应该请求,若不重复则响应。

刺探出 sign 的重要性,只要我能控制随意生成 sign,那么服务端防御的问题也就迎刃而解啦。

2.1.2 分析参数签名逻辑

虽然该站点前端有代码混淆、反调试等保护措施,但之前已加被我解决掉,找寻并提取实现签名逻辑的 JS 不会太困难。

全局搜索 sign 关键字,找到多个匹配项,只有 1875 行是生成首部 authorization 的值的逻辑:

1607327911_5fcde0a7cf7566d6fb399.png!small?1607327912708

跳至 1875 行,进入函数 _e() 内部,找到了计算签名的逻辑(s),以及生成 authorization 值的逻辑(函数返回值);为查看 _e() 的调用实参,我在入口处设置断点,为查看生成的 authorization 值,在出口处设置断点:

1607327919_5fcde0afa23aeaff4fdab.png!small?1607327920027

再回到页面上输入手机号 13988888840,点击“获取验证码”按钮,流程进入断点,了解传递实参的信息:

1607327931_5fcde0bb8a6e9a60c8c73.png!small?1607327931829

切换至 console 中,参照调用方式,改用实参 13988888849 调用 _e():

_e("POST", "{\"BODY\":{\"PHONE_NO\":\"13988888849\",\"GROUP_ID\":\"2\",\"REGION_ID\":\"11\"}}")

生成新签名:

1607327955_5fcde0d37162794319687.png!small?1607327955942

burp 开启拦截模式,放行前端断点,burp 中拦截到参数值 13988888840 及其 authorization 值的请求报文,将其改为 13988888849 及其新 authorization 值:

1607327962_5fcde0da37098b760fd75.png!small

服务端正常响应,返回 13988888849 加密后的用户信息 Data:

1607327973_5fcde0e59db31b9dc97f4.png!small?1607327974282

现在,我可以绕过参数签名机制,具有随意更改参数的能力了。只要能控制生成签名,绕防重放也就易如反掌,每次提交请求时,我同步生成新签名即可。

为方便生成参数签名,我把 JS 的 _e() 转为 python 脚本 gen_authorization.py:

1607327982_5fcde0ee476d261f462b9.png!small?1607327982698

整理下,现在我可以访问 /xx/api/xxxx/h5/xx/sChkBlPhone 接口获取加密后的用户信息 Data,可以调用 decrypt_data_by_nodejs.py 解密 Data,获取用户姓名、单位地址、家庭地址、身份证号码,拿到单个用户的敏感信息三要素;我可以调用 gen_authorization.py 绕过防重放和防篡改机制,批量获取多个用户的敏感信息。综合已有脚本编写 exp,实现武器化 get_users_info_by_requests.py:

1607649281_5fd2c801356b18dfceb48.png

来源:freebuf.com 2020-12-07 15:56:08 by: yangyangwithgnu

© 版权声明
THE END
喜欢就支持一下吧
点赞0
分享
评论 抢沙发

请登录后发表评论