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 ...
随机推荐
- Windows10下配置Linux下C语言开发环境
今天为大家介绍如在Windows10下配置Linux下C语言开发环境,首先安装linux子系统:启用开发者模式 1.打开设置 2.点击更新和安全3.点击开发者选项 4.启用开发人员模式 5.更改系统功 ...
- device tree 負值 property 寫法
倘若你要設定 負值的property, 可能需要括符才會 build 過. 正確 decidegc = <(-10)>; 錯誤 decidegc = <-10>;
- python基础===时间处理模块
时间模块 Python中有很多方便我们处理时间信息的模块 time 模块 datetime 模块 pytz 模块 dateutil 模块 这里我们着重介绍的是前两种 time模块 time.time( ...
- python实战===国内很简单实用的一些开源的api以及开源项目
原创 2017年03月25日 15:40:59 标签: api / 开源项目 / app / 免费接口 声明 以下所有 API 均由产品公司自身提供,本人皆从网络获取.获取与共享之行为或有侵犯产品 ...
- 斯坦福开源无Bug的随机计算图Certigrad
斯坦福开源无Bug的随机计算图Certigrad https://news.cnblogs.com/n/573690/ ttps://github.com/dselsam/certigrad
- 【uva11248】网络扩容
网络流裸题. 求完最大流之后保留残余容量信息,依次将已经加入最小割的弧变成c再跑,记录下即可. #include<bits/stdc++.h> #define N 20005 #defin ...
- linux命令(17):pwd命令
1:查看当前工作目录的完整路径命令:pwd 2:目录连接链接时,pwd -P 显示出实际路径,而非使用连接(link)路径:pwd显示的是连接路径: [root@host-172-168-80-55 ...
- virtualbox测试k8s要注意的情况
想在virtualBox上测试k8s,遇到两个情况要注意.. 第一是flannel和dashborad起不起来,master都无法正常..这时可以想办法把Iptables,selinux,firewa ...
- es6关于let和const的总结
set用于声明变量 1.var 的一个升级版 2.不存在变量提升 console.log(a);//Uncaught ReferenceError: a is not defined let a=1; ...
- linux在命令执行过程中ctrl +z 后[1]+ Stopped
进程挂起 stopped 代表有进程挂起 [1]是id号 可以通过Linux命令:jobs 查看挂起进程 fg 1 把任务1放到前台 bg 1 把任务1放到后台