问答
发起
提问
文章
攻防
活动
Toggle navigation
首页
(current)
问答
商城
实战攻防技术
漏洞分析与复现
NEW
活动
摸鱼办
搜索
登录
注册
记一次实战小程序漏洞测试到严重漏洞
渗透测试
记录一次从小程序静态分析+动态调试获取到严重漏洞的过程
前言 == 本文记录了最近的一次src漏洞挖掘,并成功获取到严重漏洞的过程,漏洞本身就是几个接口的组合利用,但是其中小程序的代码的分析审计过程比较有趣,遂记录一下和大家分享。 准备工作 ==== 本文涉及到小程序的静态分析、动态调试、签名算法逆向、自动化签名计算等方面,很多技术在网上都有比较详细的文章,我就不在深入赘述,这里只记录我在分析过程中用到的技术和方法。 获取小程序源码 ------- 获取小程序的源码比较简单,直接在微信-设置-文件管理-打开文件夹 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/10/attach-4a590587113a1d1fc1d22a58257b0162089f0751.png) 会自动打开文件目录 ```php C:\Users\Administrator\Documents\WeChat Files\AppId ``` 这里我们需要跳到小程序的目录 ```php /WeChat Files/Applet/{wxid}/{n}/ ``` 会看到一个`__APP__.wxapkg`,这个就是小程序的打包程序,需要对该文件进行解密,即可获取到小程序源码,解密脚本我使用的是`https://github.com/zhuweiyou/wxapkg` 直接把文件拖到exe程序的图标上便可以自动解密 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/10/attach-b28d0be6c53df8c7ac339e19be30835131306a13.png) 解密后会在当前目录保存解包后的文件。 开启Devtools ---------- 强制开启Devtools我用的是`https://github.com/JaveleyQAQ/WeChatOpenDevTools-Python`这个项目。 对应使用的微信版本是`WeChatSetup-3.9.10.19` 微信的历史版本可以在`https://github.com/tom-snow/wechat-windows-versions/tags`下载。 这里需要注意一下的是,如果电脑上安装了高版本的微信,需要先卸载掉,再安装低版本,否则hook的时候会失败。并且hook有风险,建议使用小号在虚拟机里运行。 程序的运行很简单,退出微信后执行 ```php WeChatOpenDevTools-Python.exe -all ``` ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/10/attach-56a4c7e047123fd3c39b2f1c85d275bd30a25291.png) 会自动弹出微信,登录后打开小程序即可hook成功 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/10/attach-8c5f30298844ac8feb5805f92fb93be77fac820c.png) ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/10/attach-cea8243ba27e30e144ed4bec9519e0da29d388a7.png) 小程序抓包 ----- 小程序抓包我使用的是proxifier+burp 在peoxifier中新建代理服务器,ip和端口对应burp的监听端口,协议选择https ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/10/attach-79e7b095ec12e9f183cc0dae0f1dd7b316760931.png) 然后新建代理规则。 应用程序选择 ```php WeChatApp.exe;WechatBrowser.exe;WeChatAppEx.exe ``` 动作选择拦截 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/10/attach-1d9703d6719fd42892d9e3e7e3f1eec7fd538a34.png) 即可实现小程序的抓包 自动计算签名 ------ 前面的准备工作已经基本完成,可以实现对小程序的分析了,这个步骤并不是分析小程序所必须的步骤,只是在这个目标中,使用了sign值来校验数据包,所以我们在修改了数据包后,需要使用自动计算签名的脚本进行自动签名。 自动签名我用的是mitmproxy,支持python代码编写,可控性高 下载地址:`https://www.mitmproxy.org/` 使用方法如下: ```php mitmdump -s xx.py -p 7777 ``` 用于加载一个代理脚本,并监听7777端口,然后我们在burp中把数据包中转到7777端口,即可实现mitmproxy自动对数据包进行签名。 burp设置如下: ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/10/attach-df6e9e4ae1955922899521b138ca15e8ba994ab7.png) 然后再burp中发送数据包进行测试, ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/10/attach-5e980e844f98e6240c452e86e7e60d96d9277262.png) 看到终端中有数据再跑了,说明配置成功, 这里需要特别说明一下,在使用编译好的exe程序运行python程序时,可能会出现`ModuleNotFoundError: No module named 'Crypto'`找不到库的情况,即使是最新版也有可能出现,这是一个bug,使用pip3重新安装mitmproxy即可解决 ```php pip3 install mitmproxy ``` 至此,准备工作完成,开始分析小程序 代码分析 ==== 数据包分析 ----- 在小程序分析过程中,我喜欢先抓包,浏览一下大概功能并看一下数据包的接口,大部分的小程序都是`host+api接口+uri接口`的形式,我们直接看数据包,可以免去我们找host和api接口的步骤,直接拼接提取出来的url即可。 这次的目标是一个客服系统,直接点进去小程序,提示没有对应的客服,然后一篇空白,什么功能都没有,那么看一下他的数据包, ```php POST /wxapi/Customer/Acc HTTP/1.1 Host: Content-Length: 37 Wxversion: 3.9.12 Mobilemodel: microsoft Clientplatform: windows Sdkversion: 3.5.8 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 MicroMessenger/7.0.20.1781(0x6700143B) NetType/WIFI MiniProgramEnv/Windows WindowsWechat/WMPF WindowsWechat(0x63090c0f)XWEB/11275 Minprogram: v1.9.9 Content-Type: application/json Xweb_xhr: 1 X-App: mgwx T: 1728436146712 Systemversion: Windows 7 x64 Sign: 1e02db58edb4f1e0710f7d9b3da9e39d0af98ffe Accept: */* Sec-Fetch-Site: cross-site Sec-Fetch-Mode: cors Sec-Fetch-Dest: empty Referer: https://servicewechat.com/wxf7xxx/78/page-frame.html Accept-Encoding: gzip, deflate, br Accept-Language: zh-CN,zh;q=0.9 Connection: close {"UserID":"14979"} ``` 有个code,随便修改一下,提示鉴权失败,仔细观察数据包有个sign,那么很有可能就是利用的sign来校验数据包,我们需要分析一下sign的算法。 sign算法分析 -------- 打开devtools 一般在appcontext下的no domain中存储的是小程序的js代码 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/10/attach-10d0901776eabdba07dce6120489fabe8a2e1adb.png) 分别打开这几个js,查找sign关键字 定位到这个地方 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/10/attach-662b3a1ce25a361ed0776df4118cf7d8ae52e556.png) 两个处理逻辑,一个get的,一个其他,需要分别分析。 先看get的, ```javascript var g = (new Date).getTime(); if (s.t = g, "get" == r.toLowerCase()) { s.requrl = n; var c = n + t.globalData.key + g , m = e.SHA1(c).toString(); s.sign = m ``` n就是requrl, `var g = (new Date).getTime();`就是时间戳 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/10/attach-96497fc33a85ea31f90f967868913a5d00cebc8f.png) 还差一个`t.globalData.key`,全局搜索`globalData`。在32486行找到了key ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/10/attach-085508d62ed7c2c4f2b39ddbecd631e40263eaec.png) 将url+key+g拼接后进行sha1就得到了sign 然后分析其他类型的sign计算 ```javascript c = JSON.stringify(o) + t.globalData.key + g, m = e.SHA1(c).toString(); s.sign = m ``` get传的是url+key+g,而这个传的是json格式化之后的o,往上找o在哪里定义的,发现o是直接传参传进来的, ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/10/attach-55e62bd753b1a2f3afaaa809627c57ecd86ce5ca.png) 这样再往上去找函数调用的话不如动态调试来的方便,于是在32185行打上断点 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/10/attach-b7da4ba2162c9f95236e5959f3a8598dc18d2083.png) 找一个post功能的点,提交一下,便会停在断点上 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/10/attach-ad62178e7787095fc75142e1be532cc412448955.png) 这里便可以看到动态传进来的值,把拼接后的c复制出来: ```php {"sessionId":"jS9NZxxx","encryptedData":"xxx","iv":"xxx"}tiixxx1728443242572 ``` 和最后发出去的数据包对比一下, ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/10/attach-72c98927ba27ebfef4e65295a91b5ee63bb4b2e3.png) 很明显o是传入的data数据,至此sign的算法分析完毕,mitmproxy脚本也就直接可以写出来了,代码有点渣,能用就行 ```python import datetime import hashlib from mitmproxy import ctx import json # 获取当前时间 now = datetime.datetime.now() timestamp_ms_precise = int(now.timestamp() * 1000) key="tiihxxxx==" def request(flow): # 获取请求对象 request = flow.request # # 实例化输出类 # info = ctx.log.info # # 打印请求的url # info(request.url) # # 打印请求方法 # info(request.method) # # 打印host头 # info(request.host) # # 打印请求端口 # info(str(request.port)) # # 打印所有请求头部 # info(str(request.headers)) # # 打印cookie头 # info(str(request.cookies)) if request.method == 'POST': data=(request.get_text()) print(data) n=data #n = json.dumps(data).replace(": \"", ":\"") print(n) T = 1728192063292 c = n + key + str(T) print(c) encoded_data = c.encode('utf-8') # 创建一个sha1哈希对象 sha1 = hashlib.sha1() # 更新哈希对象(可以多次调用update()来添加数据) sha1.update(encoded\_data) # 获取十六进制表示的哈希值 hex_digest = sha1.hexdigest() Sign = hex_digest print(Sign) request.headers["Sign"] = Sign request.headers["T"] = "1728192063292" else: n=request.url print(n) T = 1728208280365 c=n + key + str(T) print(c) encoded_data = c.encode('utf-8') # 创建一个sha1哈希对象 sha1 = hashlib.sha1() # 更新哈希对象(可以多次调用update()来添加数据) sha1.update(encoded\_data) # 获取十六进制表示的哈希值 hex_digest = sha1.hexdigest() Sign = hex_digest print(Sign) request.headers["Sign"] = Sign request.headers["T"] = "1728208280365" request.headers["Requrl"] = n ``` 接口分析(漏洞挖掘) ---------- 解决了sign的校验问题,就开始分析程序接口了 在静态分析的源码中提取出来几十个接口,挨个进行测试 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/10/attach-3dc31374b7c358fa4c013d4d6d5170f51f1bc808.png) 经过多次测试,发现该客服系统使用的websocket协议进行的聊天,抓到的数据包如下: ```php GET /websocket?appId=mgb7xxx&token=juukgLAxxx&sdkVer=5.6.1&pid=&apiVer=normal&platform=MiniProgram&protocolVer=3 HTTP/1.1 Host: wsproxy.xxx.com Connection: Upgrade Pragma: no-cache Cache-Control: no-cache User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36 MicroMessenger/7.0.20.1781(0x6700143B) NetType/WIFI MiniProgramEnv/Windows WindowsWechat/WMPF WindowsWechat(0x63090a13) XWEB/8555 Upgrade: websocket Origin: https://wsproxy.xxx.com Sec-WebSocket-Version: 13 Accept-Encoding: gzip, deflate, br Accept-Language: zh-CN,zh;q=0.9 Sec-WebSocket-Key: Z7s5axxx== ``` 这个数据包之后就会直接从http协议切换到websocket协议,而这个数据包中的appId和token是由客户端直接发送给服务器的,所以这两个值要么是js计算出来的,要么是服务器返回的数据,先搜一下前端的js代码,发现appid是写死的 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/10/attach-32f83cd9b83ea4bd437131668a6da1dbe6e6eba9.png) token在js中没有相关算法,基本就可以确定是服务器返回的数据了,在burp的proxy history中搜索token字符串,看一下是哪个包返回的 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/10/attach-25119c406a4300f2fa4a3afd9e0cb22efde43c9a.png) 找到了接口,发送到repeater 尝试修改userid,发现仍然会返回token,说明这里没有身份验证,可直接接管任意用户的聊天会话。写个python利用websocket库切入websocket会话,成功接管会话内容。 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/10/attach-db10f6feecd0c0d110db8fbb9527f327b44ccc43.png) 至此漏洞已成。顺带补充一下src中客服聊天系统的危害描述: 1、泄露客服与客户的聊天记录,能参与聊天的都是高意向客户,客户的购买意向、联系方式均属于核心商业机密,接管会话可循环遍历所有会话,窃取机密信息。 2、websocket无身份验证,可冒充客服对客户发起钓鱼,极大的损害企业形象。 最终,该漏洞给了严重。
发表于 2024-10-31 09:00:01
阅读 ( 1720 )
分类:
渗透测试
8 推荐
收藏
2 条评论
大侠爱吃汉堡包
2024-10-31 16:06
可以帮个忙么?
请先
登录
后评论
用户444511143
2024-11-12 09:23
写的好详细啊,大佬,谢谢你
请先
登录
后评论
请先
登录
后评论
律师
2 篇文章
×
发送私信
请先
登录
后发送私信
×
举报此文章
垃圾广告信息:
广告、推广、测试等内容
违规内容:
色情、暴力、血腥、敏感信息等内容
不友善内容:
人身攻击、挑衅辱骂、恶意行为
其他原因:
请补充说明
举报原因:
×
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!