从WebView到RCE:TikTok安卓应用渗透测试案例研究
入口点
WebView是寻找安全问题的丰富资源。开发者经常自定义WebView以实现特定目标,这种自定义正是我们需要评估的重点。
自定义评估包括:
- JavaScript是否启用?
- 是否有JavaScript接口,其实现是什么?
- 是否允许文件方案?
- 是否有WebViewClient,如果有,其方法的实现是什么?
tiktok://webview深度链接在加载URL参数之前实施了URL验证,以确保仅加载白名单中的主机名。乍一看我们无法进入,但稍作休息后重新审视,你会发现不同的视角。
进入WebView
虽然无法在TikTok WebView中加载我们的URL,但WebViewClient方法通过onPageStarted、onPageFinished和shouldOverrideUrlLoading处理在WebView上加载的URL。即使无法在WebView上加载我们的主机名,也值得检查。
开发者实现了一个令人印象深刻的功能。有一些可以离线加载的文件称为"falcon"。当URL在WebView中加载时,如果它是"falcon" URL,它会被拦截以从离线缓存文件加载。
在拦截过程结束时,执行了以下代码行:
|
|
这就足够了。如果falcon URL连接到在页面加载完成后执行的JavaScript代码,并且我们可以控制URL,我们就可以注入JavaScript,从而在WebView上加载的任何网站上实现跨站脚本(Universal XSS),我们就进入了WebView!
从WebView到Activity
WebViewClient上的JavaScript注入问题让我们进入了WebView,但我们没有审查WebView实现的其余部分和配置。WebViewClient的shouldOverrideUrlLoading方法的实现让我感到惊讶。
在检查其代码后,我发现如果URL具有intent方案,它会拦截URL,使用Intent.parseUri解析它,然后启动:
|
|
因此,我可以使用这个小工具导航到受保护的组件。
绕过URL验证
aweme://wiki深度链接实施了URL验证,仅允许白名单中的主机名,但它不检查URL方案!我利用javascript方案在WebView上执行javascript,而无需操作URL的主机名段:javascript://m.tiktok.com/%0alaert(1)
我们可以利用:
tiktok://webview上的XSS- 使用ToutiaoJSBridge接口打开内部深度链接
- 利用
aweme://wiki深度链接上的URL验证绕过 - 从那里启动受保护的组件
获取远程代码执行
当你在Android上安装包含本地库的APK时,Android通常将这些本地库提取到只读路径/data/app/com.package.name。即使是应用程序本身也无法写入或修改那里的本地库。但有时,应用程序需要更新这些库,而不需要通过Play商店更新整个应用程序,因此应用程序将本地库存储在应用程序的可写保护数据目录/data/data/com.package.name中。
通过观察/data/data/com.zhiliaoapp.musically目录,我发现了一个app_lib目录,其中包含TikTok启动时加载的本地库。
发现关键Activity
在搜索代码时,我在一个名为split_df_miniapp.apk的split APK中发现了一个名为TmaTestActivity的Activity。
该Activity将Intent中的Data Uri传递给以下方法:
|
|
它从URL获取新的SDK信息(sdkUpdateVersion、sdkVersion、latestSDKUrl),然后调用DownloadBaseBundleHandler实例,将下一个处理程序设置为ResolveDownloadHandler,然后是SetCurrentProcessBundleVersionHandler。
Zip Slip漏洞
让我们分析文件下载后的流程,从ResolveDownloadHandler类开始:
|
|
它提取zip文件,虽然它有路径遍历缓解,但这种缓解被禁用,因为IOUtils.a的布尔参数arg7始终为false,而缓解需要arg7为true:
|
|
当提取这样的ZIP文件时:
|
|
它将覆盖/data/data/com.zhiliaoapp.musically/app_lib/df_rn_kit/df_rn_kit_a3e37c20900a22bc8836a51678e458f7/arm64-v8a/libjsc.so文件!
漏洞链总结
让我们欣赏连接这些错误所需的创造力:
- 通用XSS(URL片段注入)
- 打开内部深度链接(通过ToutiaoJSBridge openSchema)
- 绕过URL验证(javascript://方案)
- 触发intent方案处理程序(Intent.parseUri小工具)
- 启动受保护的TmaTestActivity
- 利用Zip Slip(禁用的路径遍历检查)
- 覆盖本地库
- 应用程序重启时实现RCE
经验教训
-
坚持到底
- 在
tiktok://webview上找到安全URL验证?发现了JavaScript注入。 - 无法在没有点击的情况下执行intent方案?搜索其他小工具。
- 没有命令注入漏洞?转向本地库覆盖。
- 两周后没有找到任何东西?意识到我错过了split APK。
- 在
-
“休息一下"的方法论
- URL编码阻止了你的XSS?稍后回来 → 记住片段不会被编码。
- 再次安全URL验证?你发现它错过了验证方案。
/data/data中没有本地库?稍后回来 → 意识到应用程序可能仍尝试从那里加载。- 基础APK没有小工具?稍后回来 → 记住检查split APK。
-
不要只深入挖掘 — 也要向外挖掘
- 不要只是:“让我跟随这个深度链接,看看它去哪里”
- 也要尝试:“让我搜索Intent.parseUri,看看什么调用它”
-
挑战你的假设
- “这个应用程序不从/data/data加载本地库” → 但它尝试吗?
- “我无法在WebView中加载我的URL” → 但当其他URL加载时,我可以注入JS吗?
- “base.apk没有漏洞” → split APK呢?