type
status
date
slug
summary
tags
category
icon
password
一、漏洞APP分析1. AndroidManifest.xml2. MainActivity3. TestActivity5. PendingIntent4. FlagReceiver二、攻击APP构造1. 攻击方案2.攻击APP完整代码3. 攻击结果三、总结1. 考点2.坑点参考
一、漏洞APP分析
1. AndroidManifest.xml
清单中声明了四个组件。两个Activity,其中
MainActivity
导出。还有一个FlagReceiver
,未导出。还有一个FileProvider
,也是未导出。
2. MainActivity
MainActivity
和EasyDroid
中的MainActivity
一样,先是获取intent的数据,然后经过校验后使用webView.loadUrl(data.toString())
进行加载,而且设置了setJavaScriptEnabled(true)
这意味着启用了JavaScript
。除此之外设置了setWebViewClient
,并且通过shouldOverrideUrlLoading
拦截请求,在内部校验intent
并且可以进行跳转。因此这里存在攻击点,即可以绕过getAuthority
校验加载恶意html,在html
内部使用JavaScript
构造重定向请求,然后跳转到指定的非导出Activity
3. TestActivity
TestActivity
中onCreate
读取Intent
中的url
,然后使用WebView
渲染,并且设置setJavaScriptEnabled
属性为true
,同时添加了一个JavaScripte接口webView.addJavascriptInterface(this, "jsi")
,这意味着可以在WebView渲染的网页中使用js调用Android类中的方法,但是前提必须要在可以使用js接口调用的方法前面加上@JavascriptInterface
的声明,在该类中的Te3t
即为可以使用js接口调用的方法。Te3t
则创建了一条通知,并且在创建通知的过程中使用PendingIntent.getBroadcast(this, 0, new Intent(), 0))
, 该PendingIntent
将执行一个广播操作,类似于调用Context.sendBroadcast()
方法。通过获取这个PendingIntent
,您可以在任何时候以PendingIntent
创建者APP的权限执行这个广播操作,而无需调用sendBroadcast()
方法。方法原型
public staticPendingIntent getBroadcast (Context context,int requestCode,Intent intent,int flags)
5. PendingIntent
Intent
是意图的意思。Android 中的Intent 正是取自这个意思,它是一个消息对象,通过它,Android 系统的四大组件能够方便的通信,并且保证解耦。Intent 可以说明某种意图,携带一种行为和相应的数据,发送到目标组件。PendingIntent
是对Intent
的封装,但它不是立刻执行某个行为,而是满足某些条件或触发某些事件后才执行指定的行为。A组件 创建了一个
PendingIntent
的对象然后传给 B组件,B 在执行这个 PendingIntent 的 send 时候,它里面的 Intent 会被发送出去,而接受到这个 Intent 的 C 组件会认为是 A 发的。B以A的权限和身份发送了这个Intent。上文提到的
PendingIntent
则创建了一个广播PendingIntent
,这意味着其他APP获取到该PendingIntent
时可以以创建者APP的权限发送广播。PendingIntent
是可变的,这意味着应用 B 可以按照 fillIn()
文档中所述的逻辑更新用于指定操作的内部 intent。换言之,恶意应用可能会修改未填充的 PendingIntent 字段,从而允许攻击者访问存在漏洞的应用中原本不支持导出的组件。如果其他APP能够修改
PendingIntent
封装的Intent
则可能会导致危险的事情发生,例如上文提到的广播,通过修改Intent然后发送广播可能会使被攻击APP未导出的广播接收器收到广播,受到攻击如果您的应用以 Android 6(API 级别 23)或更高版本为目标平台,请指定可变性。例如,可以通过使用
FLAG_IMMUTABLE
来防止恶意应用填充未填充的字段:在 Android 11(API 级别 30)及更高版本中,必须指定要将哪些字段设置为可变字段,以缓解此类意外漏洞。
getActivity()
的意思其实是,获取一个PendingIntent对象,而且该对象日后激发时所做的事情是启动一个新activity。也就是说,当它异步激发时,会执行类似Context.startActivity()那样的动作。相应地,getBroadcast()
和getService()
所获取的PendingIntent对象在激发时,会分别执行类似Context.sendBroadcast()
和Context.startService()
这样的动作。PendingIntent 是系统对于待处理数据的一个引用,称之为:token;当主程序被 Killed 时,token 还是会继续存在的,可以继续供其他进程使用。如果要取消 PendingIntent,需要调用PendingIntent 的
cancel
方法。4. FlagReceiver
FlagReceiver
就是接收广播设置flag的接收器二、攻击APP构造
1. 攻击方案
- 第一步肯定是需要通过导出的
MainActivity
中的WebView
设置不当的漏洞进入到未导出的TestActivity进行下一步攻击,这里面的绕过和攻击方案与EasyDroid
题目中的思路一致,构造恶意html,通过@
绕过域名检测,然后在html中添加重定向到Intent Scheme Url
,使得webview在渲染时,被shouldOverrideUrlLoading
拦截到后启动TestActivity
- 然后TestActivity中的webview加载由
Intent Scheme Url
传入的url,导致下一个恶意html被渲染,该HTML通过js接口
调用Te3t
触发通知创建PendingIntent
- 在攻击者APP中创建
MagicService
服务,用于监听通知,在获取到被攻击APP发出的通知后就可以获取到被攻击APP创建的PendingIntent
- 拿到
PendingIntent
后即可重新填充Intent,使其的Action设置为com.bytectf.SET_FLAG
,并且添加flag参数将值设置为恶意html内容,然后使用send
发送广播,当被攻击APP接收到广播后会设置flag,从而将恶意html内容插入到flag中,污染flag文件
- 当恶意代码被注入到flag中后在攻击者APP的数据目录创建
软链接 symlink.html
,指向被污染的flag,而第一步中的恶意html会再设置一个Intent跳转延迟执行,这个Intent与前者不同点就是S.url=file:///data/data/com.bytectf.pwnmediumdroid/files/symlink.html
,从而导致在TestActivity中再次通过WebView加载url时渲染symlink.html
,这导致被污染的flag文件被渲染,从而触发注入到flag中的恶意代码,将内容传送到远程
2.攻击APP完整代码
MainActivity.java
MagicService.java
AndroidManifest.xml
3. 攻击结果

三、总结
1. 考点

2.坑点
在构造
evil3.html
时第一步的S.url
中的http://
的://
最好进行编码,否则在WebView渲染时会将其转为https://
,从而导致访问出现问题,如果进行编码就不会进行转化。被攻击APP中虽然有FileProvider,但是无法利用其进行攻击,按照官方的解释:
sendBroadcast
没法通过Intent的方式授予Uri权限,也就是会flags被忽略掉,类似的还有bindService
。具体的原因没找到很好的解释,可阅读AOSP源码,关键函数checkGrantUriPermissionFromIntent
官方给出的环境是每次启动一个模拟器,接受一个APP,然后启动,如果还想进行攻击就需要重新连接再启动一个模拟器,并且每次在启动攻击APP后会使用adb设置允许监听通知
adb shell cmd notification allow_listener com.bytectf.pwnmediumdroid/com.bytectf.pwnmediumdroid.MagicService
。在MagicService中一定要加这两个方法,否则监听器只能监听一次,想重新测试必须重启模拟器,如果测试中途触发了crash,例如直接终止攻击者APP,可能是无感知的,但是表现在无法进行监听,重启即可解决。
参考
- 作者:LLeaves
- 链接:https://lleavesg.top//article/ByteCTF-2021-MediumDroid
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
相关文章