四大组件之一—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 安全建议


  1. minSdkVersion不低于9
  2. 不向外部app提供的数据的私有content provider设置exported=“false”避免组件暴露(编译api小于17时更应注意此点)
  3. 使用参数化查询避免注入
  4. 内部app通过content provid交换数据设置protectionLevel=“signature”验证签名
  5. 公开的content provider确保不存储敏感数据
  6. Uri.decode() before use ContentProvider.openFile()
  7. 提供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:直接暴露

案例2:需权限访问

案例3:openFile文件遍历

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 相关阅读

http://zone.wooyun.org/content/15097

http://drops.wooyun.org/tips/2997

Android Content Provider Security(转)的更多相关文章

  1. Android Content Provider Guides

    Android Content Provider Guides Content Providers管理对结构化数据集的访问.它们包装数据,并且提供一种定义数据安全的机制. Content provid ...

  2. Android Content Provider基础

    Android Content Provider基础 Content Providers Content providers管理对一个结构化的数据集合的访问.它们封装了数据,并且提供了保护数据安全性的 ...

  3. [Android] Content provider, ContentResolver

    Content provider的作用: Content providers manage access to a structured set of data. They encapsulate t ...

  4. (转载)Android content provider基础与使用

    android有一个独特之处就是,数据库只能被它的创建者所使用,其他的应用是不能访问到的,所以如果你想实现不同应用之间的数据共享,就不得不用content provider了.在Android中,co ...

  5. Android Content Provider简介

    Content Provider是Android的四大组件之一,与Activity和Service相同,使用之前需要注册: Android系统中存在大量的应用,当不同的应用程序之间需要共享数据时,可以 ...

  6. Android Content Provider的启动过程源码分析

    本文參考Android应用程序组件Content Provider的启动过程源码分析http://blog.csdn.net/luoshengyang/article/details/6963418和 ...

  7. 6、Android Content Provider测试

    如果你的应用中使用了Content Provider来与其他应用进行数据交互,你需要对Content Provider进行测试来确保正常工作. 创建Content Provider整合测试 在Andr ...

  8. [典型漏洞分享]exported Android content provider引发的隐私泄露问题

    YS android手机APP对外开放多余的content provider,可任意增.删.改和查images数据库表格,导致隐私泄露 问题描述: YS android手机APP使用SQLITE数据库 ...

  9. android Content Provider介绍

    ContentProvider(内容提供者)是Android中的四大组件之一.主要用于对外共享数据,也就是通过ContentProvider把应用中的数据共享给其他应用访问,其他应用可以通过Conte ...

随机推荐

  1. bind类成员函数

    首先描述一个情景: 先贴出代码: class Solution { public: bool compare(int a, int b) { return a > b; } int functi ...

  2. linux===linux后台运行和关闭、查看后台任务(转)

    fg.bg.jobs.&.ctrl + z都是跟系统任务有关的,虽然现在基本上不怎么需要用到这些命令,但学会了也是很实用的 一.& 最经常被用到这个用在一个命令的最后,可以把这个命令放 ...

  3. MACACA===gradle下载和安装

    gradle下载地址: http://services.gradle.org/distributions/ 或者直接点击这个: http://services.gradle.org/distribut ...

  4. Unsupported gpu architecture 'compute_20'

    NVCC src/caffe/layers/reduction_layer.cunvcc fatal   : Unsupported gpu architecture 'compute_20'Make ...

  5. Winfrom窗体间传值

    1.通过tag属性传输,tag属性是存储与空间密切相关的数据.比如登陆界面的数据传输给主界面. 子窗体                                                 ...

  6. 关于星号(**/*.java)

    关于星号(**/*.java) (1)前面两个星号(**)表示在项目的所有文件夹(包括子文件夹)中的文件:*.java表示以 .java结尾的所有文件. (2)如果前面两个星号(**)没有,只有星号( ...

  7. Python软件源PyPI中国镜像 2016

    作为 easy_install 的升级版,pip 为 Pyhton 的包管理提供了极大的方便.一行命令即可完成所需模块的安装: pip install pandas 可是官方镜像的访问速度相当慢,几乎 ...

  8. 支持flv的播放神器

    h1:让浏览器支持flv去https://github.com/Bilibili/flv.js h2:让手机电脑都支持mp4使用: <link rel="stylesheet" ...

  9. XenServer创建镜像Storage

    Youtube:https://www.youtube.com/watch?v=-AK3nauKUw0 在安装了XenServer后,是没有镜像Storage的,我们可以在XenCenter中创建一个 ...

  10. mocha测试es6问题

    平时在写完正常的逻辑代码后,需要使用单元测试去测试逻辑代码,现在比较流行的是使用mocha进行测试 现在都是使用es6的写法,如果直接使用mocha test\某个文件,会出现下面的错误,原因是因为m ...