四大组件之一—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. 【Educational Codeforces Round 22】

    又打了一场EDU,感觉这场比23难多了啊…… 艹还是我太弱了. A. 随便贪心一下. #include<bits/stdc++.h> using namespace std; ,ans=- ...

  2. Python爬虫之百度API调用

    调用百度API获取经纬度信息. import requests import json address = input('请输入地点:') par = {'address': address, 'ke ...

  3. django “如何”系列8:如何为模型提供初始化数据

    当你第一次配置一个app的时候,有时候使用硬编码的数据去预填充你的数据库是非常有用的.这里有几个你可以让django自动创建这些数据的方法:你可以提供固定格式的初始化数据或者提供通过SQL初始化数据. ...

  4. 883H - Palindromic Cut(思维+STL)

    题目链接:http://codeforces.com/problemset/problem/883/H 题目大意:给一段长度为n的字符串s,想让你把s切成几段长度相同的回文串,可以改变s中字符的排列, ...

  5. Rotate Image——数学相关

    You are given an n x n 2D matrix representing an image. Rotate the image by 90 degrees (clockwise). ...

  6. JQuery 实现 锚点跳转

    $('.nav-jump').click(function() { $('html,body').animate( { scrollTop:$($.attr(this, 'href')).offset ...

  7. 树莓派与windows互传文件

    这是 meelo 原创的 玩转树莓派 系列文章 安装WinSCP 登录即可在左右两侧分别显示windows和树莓派中的文件 只需将文件从一侧拖到另一侧即可开始文件的传送

  8. EL表达式无法显示Model中的数据

    后台程序通过Debug都能正常返回数据并封装到Model中.而在前台通过EL表达式取值时却是原样输出,如${cart.num}... ///展现我的购物车 @RequestMapping(" ...

  9. windows 安装tp5 composer方式

    1.下载windows composer-setup.exe(我已下载一个Composer-Setup.exe); 2.我电脑使用的是phpstudy2018版 php-7.0.12-NTS 3.然后 ...

  10. WCF+Windows2008&&sqlserver2008

    这些在我的文件和云盘当中也可以在csdn中查找