Android 渗透测试实战——TikTok rce 远程代码执行案例分析

105 阅读8分钟

官网:http://securitytech.cc/

Android 渗透测试实战——TikTok rce 远程代码执行案例分析

这篇文章之前被删除过,因为我个人认为使用 TikTok 是哈拉姆(不允许的)。我删除它是为了避免鼓励其他穆斯林走这条路。如果你想确认,请咨询宗教方面有深厚知识的人。

不过,我认为在加上说明后重新发布是个好主意。我决定重新发布它,改善写作,并补充关键点,因为它包含丰富内容,能够帮助漏洞猎人和研究人员。因此,它值得被重新发布和改写。

在深入之前

我想说明,这篇文章不会只聚焦于 TikTok 的 RCE 漏洞。我把这个漏洞当作一个很好的案例研究,提供一个真实、实用的 Android 应用渗透测试教程。所以虽然博客讲的是 TikTok 远程代码执行漏洞,我也会尽量在你的 Android 应用测试过程中提供指导。同时,我期望读者具备以下能力:

  1. 基本源码审查能力:能够在源码中跟踪数据流向,理解数据去向。
    1. 基础编程理解:博客包含一些代码,我不会解释非常基础的部分——只需专注!
    1. 熟悉基础 Android 渗透测试Android App Hacking 前三章和 FRIDA 章节足够入门。

推荐的阅读方法

我强烈建议以下方式阅读本文:阅读博客时,把自己不熟悉的内容标记出来。例如,我们会简略谈到组件生命周期。如果你不了解组件生命周期,就标记它,然后继续阅读。之后再回顾标记内容,查阅文档或请教 AI。这样的方法效果很好。

入口点

WebView 是门,让我们进去!

WebView 是寻找安全问题的宝贵资源。开发者经常自定义 WebView 以实现特定目标,而这些自定义正是我们评估能否通过各种手段访问 WebView 的关键。 评估自定义包括:

  1. Javascript 是否启用?
    1. 是否有 Javascript 接口,实现方式如何?
    1. 是否允许 file 协议?
    1. 是否有 WebViewClient,如果有,其方法实现如何?

tiktok://webview 深度链接会在加载 url 参数前进行 URL 验证,以确保只加载白名单主机。乍一看,我们无法进入,但别着急,先放下它,稍后再回头看,你会有新的视角。

如何进入一个进行安全 URL 验证的 WebView?虽然这不是 TikTok 的具体情况,但你可以利用 XSS 攻击白名单 URL 或者开放重定向漏洞。通常,URL 验证机制也通过 WebViewClient 的 shouldOverrideUrlLoading 方法实现,以防止访问非白名单 URL,但值得检查它是否使用了可绕过的验证方法。

但这就是尽头吗?必须通过 Web 漏洞才能进入吗?不。TikTok 案例教会我另一种方式。

我们无法在 TikTok WebView 上加载自己的 URL,但 WebViewClient 方法会处理加载的 URL,通过 onPageStartedonPageFinishedshouldOverrideUrlLoading 方法。即使不能加载自己主机名,也值得检查。开发者实现了一个令人印象深刻的功能:一些名为 "falcon" 的文件可以离线加载。当 URL 在 WebView 加载时,如果是 "falcon" URL,它会从离线缓存文件中加载。尽管此功能有其目的,但在拦截过程结束时,执行了以下代码:

this.a.evaluateJavascript(
"JSON.stringify(window.performance.getEntriesByName('" +
this.webviewURL +
"'))",
v2
);

这就足够了。如果 falcon URL 被拼接到页面加载完成后执行的 JavaScript 代码中,并且我们能控制 URL,就可以注入 JavaScript,实现 WebView 上任意网站的 XSS(Universal XSS),从而进入 WebView!

我第一次尝试加载 https://m.tiktok.com/falcon/?test,test&',想测试是否能在 JavaScript 中逃逸字符串并注入代码。遗憾的是第一次尝试失败了,因为 URL 编码。Frida 记录了方法调用,参数如下:

(agent) [6710433681464] Arguments
android.webkit.WebView.evaluateJavascript("JSON.stringify(window.performance.getEntriesByName('https://m.tiktok.com/falcon/?test,test&%27'))",
"<instance: android.webkit.ValueCallback, $className:
com.bytedance.ies.i.a$2$1>")

先放下,稍后再回头看,你会有新视角。是的,URL 会被编码,但整个 URL 都必须被编码吗?不——# 后面的片段不会被编码。于是,我们可以加载:

https://m.tiktok.com/falcon/?x,x#',alert(1),'

通过 tiktok://webview,在 WebView 上执行 alert(1),成功进入 WebView!

从 WebView 到 Activity

WebViewClient 的 JavaScript 注入问题允许我们进入 WebView,但我们还没检查 WebView 的其它实现与配置。shouldOverrideUrlLoading 方法的实现让我很惊讶。检查其代码后,我发现它会拦截 intent scheme 的 URL,并使用 Intent.parseUri 解析成 Intent,然后启动:

// 省略部分 Java 代码
arg25.getContext().startActivity(v2);
return true;

更多安全分析可参考 Oversecured Blog

因此,我可以使用这个 gadget 导航到受保护组件。不过,我发现执行 JavaScript 启动 UserFavoritesActivity 时它没启动。检查 shouldOverrideUrlLoading 方法后发现:

boolean v0_7 = v0_6 == null ? true : v0_6.hasClickInTimeInterval();

v4 必须为 true 才能启动 intent scheme Activity,而 hasClickInTimeInterval() 不返回 true 时就不会启动,这确保了必须用户点击才能触发。虽然是两次点击漏洞,但可以持续尝试,一旦用户点击,Activity 就会启动。

另外,我还有另一种漏洞搜寻技巧:不是从入口点深入,而是向外挖掘。例如,我搜索应用中调用 Intent.parseUri 的地方,因为这是 Access Protected Component 漏洞的重要指示。结果发现它在 AddWikiActivity 的 WebViewClient 实现中:

// 省略部分 Java 代码

这个 Activity 通过深度链接 aweme://wiki?url={URL_TO_LOAD} 打开,但外部无法触发。幸运的是,tiktok://webview 的 WebView 有一个丰富的 JavascriptInterface ToutiaoJSBridge,提供 openSchema 方法可以打开内部深度链接。

window.ToutiaoJSBridge.invokeMethod(
JSON.stringify({
func: "openSchema",
params: { schema: "aweme://wiki?url=https://m.tiktok.com/" },
})
);

绕过 URL 验证

aweme://wiki 深度链接允许白名单主机,但不检查 URL scheme。我利用 javascript scheme 在 WebView 上执行 JavaScript:

javascript://m.tiktok.com/%0alaert(1)

最终流程:在 tiktok://webview 利用 XSS → 使用 ToutiaoJSBridge 打开内部深度链接 → 绕过 aweme://wiki URL 验证 → 启动受保护组件。

获取远程代码执行

在 Android 上安装包含 native 库的 APK 时,通常会解压 native 库到只读路径 /data/app/com.package.name。有些应用需要更新 native 库而不更新整个 APK,会将库存到 /data/data/com.package.name 可写目录。像 Facebook、Instagram、TikTok 都有此功能。

观察 /data/data/com.zhiliaoapp.musically,我发现 app_lib 目录包含 TikTok 启动时加载的 native 库。

TikTok,休息一下——还有另一个案例!

在讨论如何修改 native 库获取 TikTok RCE 前,我先讲一个热门应用的案例(无法透露应用名)。我发现文件写入漏洞,但 data 目录无 native 库。初看没有,但应用可能尝试加载更新目录。我用 Frida 追踪 access 函数,发现应用会检查 .hotpatch 目录是否存在,进而加载更新。我利用文件写入漏洞写入恶意更新,实现 RCE。

回到 TikTok

到此,我们通过 WebView XSS 成功启动了受保护组件。接下来,需要寻找可以执行更多操作的 gadget。我们已在 WebView 获取 XSS,但需要 RCE。可能通过命令注入或覆盖 /data/data/com.zhiliaoapp.musically/app_lib 中的 native 库。经过两周搜索,发现了 split APK 中的 TmaTestActivity,它会处理下载 SDK 更新包。利用 JSBridge 调用 preloadMiniApp 初始化上下文后,可以下载任意恶意 zip 包。

哦,Zip 漏洞

ResolveDownloadHandler 处理下载后,会调用 IOUtils.a 解压 zip。参数 arg7 总是 false,导致路径穿越检查被禁用:

if((arg7) && !TextUtils.isEmpty(v1) && (v1.contains("../"))) { // 永远不执行

因此可以覆盖 /data/data/com.zhiliaoapp.musically/app_lib/.../libjsc.so 文件,实现 RCE。

一个“蛋糕”

漏洞可以通过深度链接直接触发。我还发现可以通过 TikTok 消息伪造预览信息发送链接,利用 ToutiaoJSBridge.shareWebToChat 欺骗用户:

window.ToutiaoJSBridge.invokeMethod(
JSON.stringify({
func: "shareWebToChat",
params: {
title: "Titlerrdltesteeeddd",
web_url: "https://www.tiktok.com/",
},
})
);

按回车或点击查看完整图片

教训:不要点我的链接。

过程比结果更重要

TikTok RCE 并非一天发现,需要数周分析、死胡同和挫折。安全研究的现实就是如此,理解这一点对初学者至关重要。

核心要点

  1. 坚持不懈
    1. “休息一下”方法论
    1. 不要只往里挖——也要向外挖
    1. 挑战假设

漏洞链:创造力的典范

Universal XSS (URL fragment injection)
↓
Open internal deep-link (via ToutiaoJSBridge openSchema)
↓
Bypass URL validation (javascript:// scheme)
↓
Trigger intent scheme handler (Intent.parseUri gadget)
↓
Start protected TmaTestActivity
↓
Exploit Zip Slip (disabled path traversal check)
↓
Overwrite native library
↓
RCE on app restart

每一步都:

  • 非显而易见
    • 充满创造力
    • 需要深入 Android 知识

这不是运气——是经验积累的模式识别。

必读资源

你的旅程从现在开始

你已经了解方法论、思维方式和检查清单。 下一个 RCE 可能就在你手机上的某个应用里,可能需要几天、几周甚至几个月才能发现。你会遇到死胡同,需要“休息一下再回头看”。

公众号:安全狗的自我修养

vx:2207344074

Gitee:gitee.com/haidragon

GitHub:github.com/haidragon

Bilibili:haidragonx