前期回顾
漏洞免费实战部分-安卓应用层getLastPathSegment函数问题
漏洞实战部分3-ContentProvider组件的openFile接口问题
漏洞学习之PWN-HITCON_CTF_2016:Secret Holder
安卓应用漏洞学习-Content Provider组件的自定义权限
CVE-2020-6828 Firefox Android 任意文件写漏洞,我们模拟学习这个漏洞原理。首先创建case8应用代替Firefox应用,case8需要能被外部调用,且为隐式调用。
为了方便区分,这里创建一个名为 MainActivity2的组件,要满足隐式调用和MIME需要增加intent-filter标签和data数据标签。如下:
image1
Intent 的显式(explicitly)和隐式(implicitly)
显式:
在构造Intent对象时就指定接收者
Intent intent = new Intent(MainActivity.this,targetActivity.class);
startActivity(intent);
隐式:
在构造Intent对象时,不知道接收对象。这种有利于降低开发的耦合度。
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setPackage(“com.targetname”);
startActivity(intent);
隐式的匹配规则:
主要是查找已注册在AndroidManifest.xml 文件中所有的IntentFilter,来找到匹配的Intent。通过action、type、category三个属性标签判断。隐式调用至少有一个category(“android.intent.category.DEFAULT”),不然Intent的匹配就会失败,报错说找不到匹配的组件。
action的匹配
隐式调用必须设置action标签,调用者构造Intent时必须指定action变量,且值必须与被调用组件声明的一直。如:
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
</intent-filter>
调用者:
Intent intent = new Intent();
intent.setAction("android.intent.action.VIEW");
startActivity(intent);
data的匹配
和action一样,只要Intent的data和目标组件在IntentFilter中声明data完全相同,则data匹配成功。
data有两部分:mimetype、URI
mimeType是指类型例如:image/jpeg,auto/*,text/html.等。可表图片、文本、媒体等格式。
URI是指协议和地址如:scheme、host、port、path、pathPattern、pathPrefix等组成。
<scheme>://<host>:<port>/[path|pathPattern|pathPrefix]
具体在xml中:
<intent-filter>
<data android:scheme="http"
android:host="case8"
android:port="xxxx"
android:path="xxxx"
android:pathPattern="xxxx"
android:pathPrefix="xxxx"
android:mimeType="text/html"
/>
</intent-filter>
category的匹配规则
这个特殊,构造Intent时不设置category的话,系统会默认带上”android.intent.category.DEFAULT”,它必须保证和过滤规则中的一个category相同。不强制要求Intent设置category。
配置好隐式调用后,MainActivity2组件实现接收外部传入的Intent,并将intent中的uri提取出来使用。模拟Firefox 的逻辑,Firefox使用接收的uri调用query获取文件名,代码如下:
image2
查数据得到文件名后,没有判断文件是否包含”../“特殊符号,直接与contentUri目录拼接写入文件。
image3
FileUtils.copy是读取uri指向的文件,写入到file2创建的新文件中。
image4
File 对象本身不检查带特殊符号”../“的路径。
case8模拟这个逻辑,代码如下:
image5
MainActivity2组件接收外部传入的Intent并取出uri,使用query读取uri指向的poc8提供的Content Provider组件。
image6
这里会返回poc8提供的文件名,文件名可控能携带’../’特殊字符。没有做任何校验,直接返回与this.getCacheDir()拼接并创建文件,随后调用copy函数将uri指向的文件拷贝到新创建的文件中。
image8
this.getContentResolver().openInputStream(uri)会调用poc8中的openFile重载函数,也可被控制。
poc8只需要实现query函数和openFile函数,即可对cae8沙箱内任意文件写入。
image9
MatrixCursor 是一种虚表,父类继承Cursor 所以可以当返回值用。
image10
image11
image
openFile实现:
image13
这里读取创建好的poc8.txt文件。文件是在poc8启动时创建,写入”hello poc8”字符串。创建路径为:“/storage/emulated/0/Android/data/com.test.poc8/cache/poc8.txt”
image14
准备好所有调用函数之后,执行poc调用case8进行文件写入,poc代码:
image15
添加包名、隐式启动需要action进行匹配,不设置category系统会默认增加”android.intent.category.DEFAULT”,addFlags增加临时读写权限,在Intent中要同时设置Data和Type属性就只能调用setDataAndType函数,如果是单独setData、setType会互相抵消。
image16
image17
完成所有代码后运行查看效果。
image18
image19
poc8执行之前当前case8沙箱内文件:
image20
poc8执行之后当前case8沙箱内文件:
image21
打破了安卓沙箱隔离机制,poc8的文件写入到了case8内,当然如果case8内有加载的插件,那么poc8提供插件覆盖这个case8的插件,重启case8应用执行加载这个插件就可以在case8中执行代码了。原漏洞CVE-2020-6828是覆写了profile.ini文件和增加user.js文件,profile.ini文件指向加载js的路径,poc修改为指向同目录下文件也就是user.js。user.js内部执行设置代理的逻辑,将所有流量转向攻击者提供的ip地址和端口。
CVE-2020-6828演示视频:
https://drive.google.com/open?id=1q6IQP8SCcpqtUTZ-Wb6z73Y8vWcFdLzM
CVE-2020-6828补丁修复也简单,在 query查询完之后,只获取文件名。:
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/FileUtils.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/FileUtils.java
@@ -319,17 +319,17 @@ public class FileUtils {
try (Cursor metaCursor = cr.query(uri, projection, null, null, null);) {
if (metaCursor.moveToFirst()) {
fileName = metaCursor.getString(0);
}
} catch (Exception e) {
e.printStackTrace();
}
- return fileName;
+ return canonicalizeFilename(fileName);
}
关注微信公众号或者可以直接加作者微信: