Android Content Provider Security(转)
四大组件之一—content provider安全详解
原帖地址:http://drops.wooyun.org/tips/4314
0x00 科普
内容提供器用来存放和获取数据并使这些数据可以被所有的应用程序访问。它们是应用程序之间共享数据的唯一方法;不包括所有Android软件包都能访问的公共储存区域。Android为常见数据类型(音频,视频,图像,个人联系人信息,等等)装载了很多内容提供器。你可以看到在android.provider包里列举了一些。你还能查询这些提供器包含了什么数据。当然,对某些敏感内容提供器,必须获取对应的权限来读取这些数据。
如果你想公开你自己的数据,你有两个选择:你可以创建你自己的内容提供器(一个ContentProvider子类)或者你可以给已有的提供器添加数据,前提是存在一个控制同样类型数据的内容提供器且你拥有读写权限。

0x01 知识要点
参考:http://developer.android.com/guide/topics/providers/content-providers.html
Content URIs
content URI 是一个标志provider中的数据的URI.Content URI中包含了整个provider的以符号表示的名字(它的authority) 和指向一个表的名字(一个路径).当你调用一个客户端的方法来操作一个provider中的一个表,指向表的content URI是参数之一.

A. 标准前缀表明这个数据被一个内容提供器所控制。它不会被修改。
B. URI的权限部分;它标识这个内容提供器。对于第三方应用程序,这应该是一个全称类名(小写)以确保唯一性。权限在 元素的权限属性中进行声明:
<provider name=".TransportationProvider" authorities="com.example.transportationprovider" . . . >
C. 用来判断请求数据类型的路径。这可以是0或多个段长。如果内容提供器只暴露了一种数据类型(比如,只有火车),这个分段可以没有。如果提供器暴露若干类型,包括子类型,那它可以是多个分段长-例如,提供"land/bus", "land/train", "sea/ship", 和"sea/submarine"这4个可能的值。
D. 被请求的特定记录的ID,如果有的话。这是被请求记录的_ID数值。如果这个请求不局限于单个记录, 这个分段和尾部的斜线会被忽略:
content://com.example.transportationprovider/trains
ContentResolver
ContentResolver的方法们提供了对存储数据的基本的"CRUD" (增删改查)功能
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
getIContentProvider() Returns the Binder object for this provider. delete(Uri uri, String selection, String[] selectionArgs) -----abstract A request to delete one or more rows. insert(Uri uri, ContentValues values) Implement this to insert a new row. query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) Receives a query request from a client in a local process, and returns a Cursor. update(Uri uri, ContentValues values, String selection, String[] selectionArgs) Update a content URI. openFile(Uri uri, String mode) Open a file blob associated with a content URI. |
Sql注入
sql语句拼接
|
1
2
|
// 通过连接用户输入到列名来构造一个选择条款 String mSelectionClause = "var = " + mUserInput; |
参数化查询
|
1
2
|
// 构造一个带有占位符的选择条款 String mSelectionClause = "var = ?"; |
权限
下面的 元素请求对用户词典的读权限:
<uses-permission android:name="android.permission.READ_USER_DICTIONARY">
申请某些protectionLevel="dangerous"的权限
<uses-permission android:name="com.huawei.dbank.v7.provider.DBank.READ_DATABASE"/>
<permission android:name="com.huawei.dbank.v7.provider.DBank.READ_DATABASE" android:protectionLevel="dangerous"></permission>
android:protectionLevel
normal:默认值。低风险权限,只要申请了就可以使用,安装时不需要用户确认。
dangerous:像WRITE_SETTING和SEND_SMS等权限是有风险的,因为这些权限能够用来重新配置设备或者导致话费。使用此protectionLevel来标识用户可能关注的一些权限。Android将会在安装程序时,警示用户关于这些权限的需求,具体的行为可能依据Android版本或者所安装的移动设备而有所变化。
signature:这些权限仅授予那些和本程序应用了相同密钥来签名的程序。
signatureOrSystem:与signature类似,除了一点,系统中的程序也需要有资格来访问。这样允许定制Android系统应用也能获得权限,这种保护等级有助于集成系统编译过程。
API
Contentprovider组件在API-17(android4.2)及以上版本由以前的exported属性默认ture改为默认false。
Contentprovider无法在android2.2(API-8)申明为私有。
<!-- *** POINT 1 *** Do not (Cannot) implement Private Content Provider in Android 2.2 (API Level 8) or earlier. -->
<uses-sdk android:minSdkVersion="9" android:targetSdkVersion="17" />
关键方法
- public void addURI (String authority, String path, int code)
- public static String decode (String s)
- public ContentResolver getContentResolver()
- public static Uri parse(String uriString)
- public ParcelFileDescriptor openFile (Uri uri, String mode)
- public final Cursor query(Uri uri, String[] projection,String selection, String[] selectionArgs, String sortOrder)
- public final int update(Uri uri, ContentValues values, String where,String[] selectionArgs)
- public final int delete(Uri url, String where, String[] selectionArgs)
- public final Uri insert(Uri url, ContentValues values)
0x02 content provider 分类


这个老外分的特别细,个人认为就分private、public、in-house差不多够用。
0x03 安全建议
- minSdkVersion不低于9
- 不向外部app提供的数据的私有content provider设置exported=“false”避免组件暴露(编译api小于17时更应注意此点)
- 使用参数化查询避免注入
- 内部app通过content provid交换数据设置protectionLevel=“signature”验证签名
- 公开的content provider确保不存储敏感数据
- Uri.decode() before use ContentProvider.openFile()
- 提供asset文件时注意权限保护
0x04 测试方法
1、反编译查看AndroidManifest.xml(drozer扫描)文件定位content provider是否导出,是否配置权限,确定authority
|
1
2
|
drozer: run app.provider.info -a cn.etouch.ecalendar |
2、反编译查找path,关键字addURI、hook api 动态监测推荐使用zjdroid
3、确定authority和path后根据业务编写POC、使用drozer、使用小工具Content Provider Helper、adb shell // 没有对应权限会提示错误
|
1
2
3
4
5
6
|
adb shell: adb shell content query --uri <URI> [--user <USER_ID>] [--projection <PROJECTION>] [--where <WHERE>] [--sort <SORT_ORDER>] content query --uri content://settings/secure --projection name:value --where "name='new_setting'" --sort "name ASC"adb shell content insert --uri content://settings/secure --bind name:s:new_setting --bind value:s:new_value adb shell content update --uri content://settings/secure --bind value:s:newer_value --where "name='new_setting'"adb shell content delete --uri content://settings/secure --where "name='new_setting'" |
|
1
2
|
drozer: run app.provider.query content://telephony/carriers/preferapn --vertical |
0x05 案例
案例1:直接暴露
- WooYun: 盛大Youni有你Android版敏感信息泄露(可读用户本地消息)
- WooYun: 新浪微博Android应用本地信息泄露
- WooYun: 盛大起点读书Android客户端token等用户敏感信息泄露
- WooYun: 傲游浏览器限制不严格可导致网页欺诈攻击
- WooYun: 搜狗手机浏览器隐私泄露和主页篡改漏洞二合一(需要手机里有恶意应用)
- WooYun: 酷派S6流量监控绕过(偷跑流量不是事儿)
- WooYun: 酷派最安全手机s6通知栏管理权限绕过
案例2:需权限访问
案例3:openFile文件遍历
- WooYun: 赶集网Android客户端Content Provider组件任意文件读取漏洞
- WooYun: 猎豹浏览器(Android版)任意私有文件数据可被本地第三方窃取漏洞
- WooYun: 58同城Android客户端远程文件写入漏洞
Override openFile method
错误写法1:
|
1
2
3
4
5
6
|
private static String IMAGE_DIRECTORY = localFile.getAbsolutePath(); public ParcelFileDescriptor openFile(Uri paramUri, String paramString) throws FileNotFoundException { File file = new File(IMAGE_DIRECTORY, paramUri.getLastPathSegment()); return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY); } |
错误写法2:URI.parse()
|
1
2
3
4
5
6
|
private static String IMAGE_DIRECTORY = localFile.getAbsolutePath(); public ParcelFileDescriptor openFile(Uri paramUri, String paramString) throws FileNotFoundException { File file = new File(IMAGE_DIRECTORY, Uri.parse(paramUri.getLastPathSegment()).getLastPathSegment()); return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY); } |
POC1:
|
1
2
3
4
5
6
7
|
String target = "content://com.example.android.sdk.imageprovider/data/" + "..%2F..%2F..%2Fdata%2Fdata%2Fcom.example.android.app%2Fshared_prefs%2FExample.xml"; ContentResolver cr = this.getContentResolver(); FileInputStream fis = (FileInputStream)cr.openInputStream(Uri.parse(target)); byte[] buff = new byte[fis.available()]; in.read(buff); |
POC2:double encode
|
1
2
3
4
5
6
7
|
String target = "content://com.example.android.sdk.imageprovider/data/" + "%252E%252E%252F%252E%252E%252F%252E%252E%252Fdata%252Fdata%252Fcom.example.android.app%252Fshared_prefs%252FExample.xml"; ContentResolver cr = this.getContentResolver(); FileInputStream fis = (FileInputStream)cr.openInputStream(Uri.parse(target)); byte[] buff = new byte[fis.available()]; in.read(buff); |
解决方法Uri.decode()
|
1
2
3
4
5
6
7
8
9
10
|
private static String IMAGE_DIRECTORY = localFile.getAbsolutePath(); public ParcelFileDescriptor openFile(Uri paramUri, String paramString) throws FileNotFoundException { String decodedUriString = Uri.decode(paramUri.toString()); File file = new File(IMAGE_DIRECTORY, Uri.parse(decodedUriString).getLastPathSegment()); if (file.getCanonicalPath().indexOf(localFile.getCanonicalPath()) != 0) { throw new IllegalArgumentException(); } return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY); } |
0x06 参考
https://www.securecoding.cert.org/confluence/pages/viewpage.action?pageId=111509535
http://www.jssec.org/dl/android_securecoding_en.pdf
http://developer.android.com/intl/zh-cn/reference/android/content/ContentProvider.html
0x07 相关阅读
Android Content Provider Security(转)的更多相关文章
- Android Content Provider Guides
Android Content Provider Guides Content Providers管理对结构化数据集的访问.它们包装数据,并且提供一种定义数据安全的机制. Content provid ...
- Android Content Provider基础
Android Content Provider基础 Content Providers Content providers管理对一个结构化的数据集合的访问.它们封装了数据,并且提供了保护数据安全性的 ...
- [Android] Content provider, ContentResolver
Content provider的作用: Content providers manage access to a structured set of data. They encapsulate t ...
- (转载)Android content provider基础与使用
android有一个独特之处就是,数据库只能被它的创建者所使用,其他的应用是不能访问到的,所以如果你想实现不同应用之间的数据共享,就不得不用content provider了.在Android中,co ...
- Android Content Provider简介
Content Provider是Android的四大组件之一,与Activity和Service相同,使用之前需要注册: Android系统中存在大量的应用,当不同的应用程序之间需要共享数据时,可以 ...
- Android Content Provider的启动过程源码分析
本文參考Android应用程序组件Content Provider的启动过程源码分析http://blog.csdn.net/luoshengyang/article/details/6963418和 ...
- 6、Android Content Provider测试
如果你的应用中使用了Content Provider来与其他应用进行数据交互,你需要对Content Provider进行测试来确保正常工作. 创建Content Provider整合测试 在Andr ...
- [典型漏洞分享]exported Android content provider引发的隐私泄露问题
YS android手机APP对外开放多余的content provider,可任意增.删.改和查images数据库表格,导致隐私泄露 问题描述: YS android手机APP使用SQLITE数据库 ...
- android Content Provider介绍
ContentProvider(内容提供者)是Android中的四大组件之一.主要用于对外共享数据,也就是通过ContentProvider把应用中的数据共享给其他应用访问,其他应用可以通过Conte ...
随机推荐
- bind类成员函数
首先描述一个情景: 先贴出代码: class Solution { public: bool compare(int a, int b) { return a > b; } int functi ...
- linux===linux后台运行和关闭、查看后台任务(转)
fg.bg.jobs.&.ctrl + z都是跟系统任务有关的,虽然现在基本上不怎么需要用到这些命令,但学会了也是很实用的 一.& 最经常被用到这个用在一个命令的最后,可以把这个命令放 ...
- MACACA===gradle下载和安装
gradle下载地址: http://services.gradle.org/distributions/ 或者直接点击这个: http://services.gradle.org/distribut ...
- Unsupported gpu architecture 'compute_20'
NVCC src/caffe/layers/reduction_layer.cunvcc fatal : Unsupported gpu architecture 'compute_20'Make ...
- Winfrom窗体间传值
1.通过tag属性传输,tag属性是存储与空间密切相关的数据.比如登陆界面的数据传输给主界面. 子窗体 ...
- 关于星号(**/*.java)
关于星号(**/*.java) (1)前面两个星号(**)表示在项目的所有文件夹(包括子文件夹)中的文件:*.java表示以 .java结尾的所有文件. (2)如果前面两个星号(**)没有,只有星号( ...
- Python软件源PyPI中国镜像 2016
作为 easy_install 的升级版,pip 为 Pyhton 的包管理提供了极大的方便.一行命令即可完成所需模块的安装: pip install pandas 可是官方镜像的访问速度相当慢,几乎 ...
- 支持flv的播放神器
h1:让浏览器支持flv去https://github.com/Bilibili/flv.js h2:让手机电脑都支持mp4使用: <link rel="stylesheet" ...
- XenServer创建镜像Storage
Youtube:https://www.youtube.com/watch?v=-AK3nauKUw0 在安装了XenServer后,是没有镜像Storage的,我们可以在XenCenter中创建一个 ...
- mocha测试es6问题
平时在写完正常的逻辑代码后,需要使用单元测试去测试逻辑代码,现在比较流行的是使用mocha进行测试 现在都是使用es6的写法,如果直接使用mocha test\某个文件,会出现下面的错误,原因是因为m ...