四大组件之一—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. 欢迎访问新博客aiyoupass.com

    新博客基本搭建好了,欢迎访问.aiyoupass.com

  2. Django REST Framework JWT提供的登录签发的视图

    Django REST Framework JWT提供了一个视图.在我们登录的时候,会校验用户名.密码是否正确.如果信息无误,可以返回一个JWT token.就可以简单地实现了记录用户登录状态. 用法 ...

  3. 关于HTML,css3自适应屏幕,自适应宽度

    设置了在不同分辨率下,显示的css样式: @media screen and (min-width:1080px){ .box{ width: 1080px;}.content{width: 1040 ...

  4. linux命令(36):which命令

    我们经常在linux要查找某个文件,但不知道放在哪里了,可以使用下面的一些命令来搜索:        which  查看可执行文件的位置.       whereis 查看文件的位置.         ...

  5. Servlet的监听器Listener

    Servlet的监听器Listener,它是实现了javax.servlet.ServletContextListener 接口的服务器端程序,它也是 随web应用的启动而启动,只初始化一次,随web ...

  6. 使用kubeadm安装k8s集群故障处理三则

    最近在作安装k8s集群,测试了几种方法,最终觉得用kubeadm应该最规范. 限于公司特别的网络情况,其安装比网上不能访问google的情况还要艰难. 慢慢积累经验吧. 今天遇到的三则故障记下来作参考 ...

  7. 【转】eclipse for java ee的tomcat配置(常见问题解决)

    原文:http://blog.csdn.net/lanzhizhuxia/article/details/8087709 前一段时间准备学习ssh的源码,但是web开发的环境一直没有弄好,myecli ...

  8. AC日记——围栏木桩 洛谷 P2362

    围栏木桩 思路: DP: 代码: #include <bits/stdc++.h> using namespace std; #define maxn 2001 int n,m,ai[ma ...

  9. 走进 Prism for Xamarin.Forms

    一.使用环境 OS:Win 10 16273 VS:VS2017- 15.3.4 Xamarin:4.6.3.4,nuget:2.4 Android Emulator:Visual Studio fo ...

  10. jstree无限级菜单ajax按需动态加载子节点

    业余时间研究了一下jstree,更新非常快已经是3.0了,首先看一下效果截图: 1.页面引入样式和脚本(注意路径根据实际情况) <link href="~/Scripts/vakata ...