JSB鉴权条件竞争绕过
Last Update:
参考文档:初探JSB鉴权条件竞争绕过–以ByteCTF2024-JSBMaster为例 - 先知社区 (aliyun.com)
ByteCTF2024-JSBMaster
题目分析
题目要求传入一个POC URL拿到m.toutaio.com
域上的flag
代码流程
先接收一个Intent获取URL,需要满足uri.getHost() != null && uri.getHost().endsWith("app.toutiao.com")
才会loadUrl,获取不到就打开example.html
程序自己的流程其实到这里就结束了,剩下的是作者提供了JSB的接口,但没有给出调用,所以说咱们的初步目标就是要写一个html来调用这个JSB的接口,至于具体怎么获取flag后文再讲
先看JSB的代码:
想要调用JSB代码的话,是有白名单的,需要满足url.startsWith("https://app.toutiao.com/") || url.equals("file:///android_asset/example.html")
接下来的目标就是如何绕过这个鉴权
关注到重写的shouldOverrideUrlLoading
函数,该函数会在页面重新加载时调用,也会获取到一个url,并且这个url只判断了非空,所以可以导致任意页面加载,这也意味着可以加载咱们自己写的html页面,格式就是 http://app.toutiao.com/?url=http://xxxxx.com/
可以验证:
1 |
|
条件竞争绕过JSB鉴权
这里出题人给出了hint:as-21-Qin-The-Tangled-WebView-JavascriptInterface-Once-More.23 (blackhat.com)
这个ppt我暂时没看懂,后文ppt解读补坑。(这里借鉴参考文档)参考文档提到: “实时” 访问控制,在 UI线程(一般是主线程)中通过 WebView.getUrl
获取 URL
可以看到本题的jsb方法是跑在UI线程上
继续查查这个 runOnUiThread
如果当前线程是 UI 线程,则立即执行该操作。如果当前线程不是 UI 线程,则该操作将发布到 UI 线程的事件队列中。
这里他跑在UI线程中,因为程序逻辑就有更新UI的操作
js与java方法的交互是在WebView 的私有后台线程上跑的,而我们的 WebView.getUrl
只能是在UI线程中调用。当我们发起多个jsb调用,那么对应的操作就会放在UI 线程的事件队列中排队等待执行。这些操作排队调用WebView.getUrl
,期间若WebView发生了某些改变,这个改变可能会影响到 getUrl
的取值,那很有可能获取到的URL是不一致的。(这里直接复制大佬的文章了)
那么我们就是要先发出大量的jsb事件,让他们排队等待执行的过程中,做一些事情,使得webview发生改变,进而影响getUrl的取值,使得本来通不过白名单的jsb事件变得能通过
[shouldOverrideUrlLoading](https://developer.android.com/reference/android/webkit/WebViewClient#shouldOverrideUrlLoading(android.webkit.WebView, android.webkit.WebResourceRequest)) 也是运行在UI线程中的,由网页发起的导航会调用该方法,若其中使用了 WebView.loadUrl
处理进行了浏览器启动的导航,那么就有可能会改变 getUrl
获取到的值
所以这里引出了两个新概念,浏览器启动的导航和渲染启动的导航。(后文详细介绍)
- 浏览器启动的导航是指在
WebView
中直接调用类似loadUrl()
方法加载页面的过程,浏览器导航负责页面请求的发起、URL 的解析、服务器资源的获取等。 - 渲染启动的导航是指用户在
WebView
中点击页面上的链接或者通过 JavaScript、重定向等操作时触发的导航过程,它与浏览器导航不同,导航的起点是 Web 页面内容本身,而不是通过 Java 代码直接调用loadUrl()
。
先来实验验证下,我们准备攻击的js代码
1 |
|
就是发现可以成功在app.toutiao.com
域上执行了js代码
而另外一种渲染启动导航的方式
1 |
|
发现不可行
这两种区别以及原理后文ppt解读补坑
那么总结一下,我们现在可以通过浏览器启动的导航来更改geturl的值,进一步影响jsb消息队列中的白名单检测,又因为有xss洞,使得可以在app.toutiao.com域上执行任意的jsb命令
但是flag是需要在m.toutiao.com这个域上执行js的
UXSS
概念
通用型跨站脚本(UXSS,Universal Cross-Site Scfipting),主要是利用浏览器及插件的漏洞(比如同源策略绕过,导致A站的脚本可以访问B站的各种私有属性,例如cookie等)来构造跨站条件,以执行恶意代码。它与普通的XSS的不同点就在于漏洞对象及受害范围的差异上,如表1所示。
调用 [evaluateJavascript](https://developer.android.com/reference/android/webkit/WebView#evaluateJavascript(java.lang.String, android.webkit.ValueCallback)) 方法在当前显示页面的上下文中异步执行JavaScript代码,然后这里是直接拼接的外部参数
1 |
|
这里的攻击链
浏览器启动的导航更改geturl()获得的值
jsb绕过鉴权,在app.toutiao.com域执行任意js代码
evaluateJavascript()构造闭合触发uxss(有种嵌套的感觉),变为在m.toutiao.com域上执行jsb
执行即可获得flag,一个hook页面拦截
攻击链模型
自己写了个最简单的利用模型,原理是可以得到验证的,输出webview里geturl的url值,可以看到成功修改为了指定域名 (代码在附件)
1 |
|
两篇PPT解读
文中提到两种鉴权方法,第一种:
Tangled getUrl —— Lifecycle-based access control 基于生命周期的访问控制
ppt中只有这张图提了一下,初步理解就是从生命周期回调中获取 URL,例如 onPageStarted
、ShouldOverrideUrlLoading
等
第二种:
Tangled getUrl —— “real-time” access control “实时”访问控制
在 UI线程(一般是主线程)中通过 WebView.getUrl
获取 URL
本题用到的就是第二种方法,ppt中也提到
java与java方法的交互是在Webview后台的私有线程上跑的,WebView.getUrl
只能是在UI线程中调用
其中runOnUiThread:
如果当前线程是 UI 线程,则立即执行该操作。如果当前线程不是 UI 线程,则该操作将发布到 UI 线程的事件队列中。
js与java方法的交互是在WebView 的私有后台线程上跑的,而我们的 WebView.getUrl
只能是在UI线程中调用。当我们发起多个jsb调用,那么对应的操作就会放在UI 线程的事件队列中排队等待执行。在这个过程中,我们做一些事情让**WebView.getUrl
** 也发生改变,那么这个操作就会插入jsb的消息队列中,使得后面发生的jsb调用,如果里面有白名单鉴权,他的geturl获得的url就是发生改变后的url。
那么我们就是要先发出大量的jsb事件,让他们排队等待执行的过程中,做一些事情,使得webview发生改变,进而影响getUrl的取值,使得本来通不过白名单的jsb事件变得能通过
New Attack Model —- Navigation Confused Vulnerability(NCV) 导航混淆漏洞
Render-initiated VS Browser-initiated Navigation 渲染启动的 vs 浏览器启动的
文中提到
During different types of navigation, WebView.getUrl will return different value
在不同类型的导航过程中,WebView.getUrl 会返回不同的值
当使用浏览器启动的导航时,getUrl
底层返回的是 pendingentry ,而使用渲染启动的导航时返回的是GetLastCommittedEntry() 的值
图中倒数第二个区别:浏览器启动的导航不需要大量的检查,渲染启动的则需要
那么依赖于他的loadurl后,geturl也可以快速执行得到改变后的url值,后者在jsb调用链中很慢,jsb执行完他也没有loadurl,所以说触发不了
Attack In Real World#1
首先设置一个 400 毫秒的延迟后调用 getToken
,然后立即调用 browser_navigation
。
时间延迟攻击:攻击者利用 setTimeout
来操控函数的执行顺序,试图在导航发生之前先获取令牌。这种方式可能允许攻击者在用户进行导航之前,窃取敏感信息
预防:
严格的安全验证:确保在处理敏感操作时实施严格的安全验证,尤其是在允许 JavaScript 调用本地接口时。
防止时间延迟攻击:设计时避免依赖于操作顺序,确保敏感操作在用户导航前不会被执行。(有点逻辑洞的感觉)
ppt里还有两种attack 感兴趣喂给gpt基本也能看懂,不做赘述
Temporary solution
- 不要将
loadUrl
暴露给 JavaScript 接口 - 不要在生命周期回调中暴露
loadUrl
- 注意 WebView 活动的
launchMode
- 注意 WebView 的重用
[https://conference.hitb.org/hitbsecconf2021ams/materials/D2T1%20-%20A%20New%20Attack%20Model%20for%20Hybrid%20Mobile%20Applications%20-%20Ce%20Qin.pdf](https://conference.hitb.org/hitbsecconf2021ams/materials/D2T1 - A New Attack Model for Hybrid Mobile Applications - Ce Qin.pdf)