🚨 Android Service 组件的安全风险详解
本文旨在帮助 Android 渗透新手理解:导出型 Service 组件可能带来的安全风险,如何定位、分析与防护。
🌟 如何快速定位有问题的Service?
一、定位service位置
在 AndroidManifest.xml 中搜索<service:
<service
android:name=".MyService"
android:exported="true" />
- 查找
android:exported="true"是否存在,存在则表示该 Service 可被其他 App 调用。 - 是否有
android:permission代表有权限校验,没有代表缺失,需要看代码是否有校验。
二、源码分析是否存在校验
1、使用工具反编译
此处以Jadx为例,检查源码,按下面可跳转具体位置
![图片[1]-别再只看 Activity!新手也能看懂的 Android Service 安全解析-极客星球](https://monkey.geekserver.top/wp-content/uploads/2025/06/e388b9ebd320250603223251.png)
2、源代码结构示例
public class EMChatService extends Service {
...
public IBinder onBind(Intent intent) {
...
}
public int onStartCommand(Intent intent, int i, int i2) {
...
}
}
3、查看重点方法
检测下面两个方法:
onStartCommand(Intent intent, int flags, int startId)onBind(Intent intent)- 是否有调用:
checkCallingPermission(...)Binder.getCallingUid()checkPermission(...)SecurityException抛出- 使用
Intent的参数直接处理(危险)
- Intent 参数机制说明 👉 Intent 参数是什么?Android 四大组件必备通信机制详解 + 安全要点-极客星球
正常代码示例
在 onStartCommand() 或 onBind() 中:
- 安全做法:
if (PermissionChecker.checkCallingPermission(...) != PERMISSION_GRANTED) {
stopSelf();
return START_NOT_STICKY;
}
public int onStartCommand(Intent intent, int flags, int startId) {
if (Binder.getCallingUid() != Process.myUid()) {
stopSelf(); // 非本 App 启动,直接终止
return START_NOT_STICKY;
}
...
}
public IBinder onBind(Intent intent) {
if (Binder.getCallingUid() != Process.myUid()) {
return null; // 拒绝绑定
}
return this.mBinder;
}
- 危险信号:
- 没有任何调用者检查,直接执行逻辑;
- 对
intent.getExtras()、intent.getAction()等参数直接操作; - 返回
Binder对象供调用方使用。
🧪 ADB验证
📌 测试 EMChatService 是否可被任意启动
adb shell am startservice -n com.example.targetapp/com.hyphenate.chat.EMChatService
- 如果该命令返回:
Starting service: Intent { cmp=com.example.targetapp/com.hyphenate.chat.EMChatService }且没有报错,就说明 Service 可以被你直接启动(未做权限校验,危险)。 - 如果系统返回
SecurityException,可能是代码中有手动权限校验。
📚 常见漏洞示例
✅ 1. 命令执行漏洞(RCE)
📜 Manifest 配置
<service
android:name=".ExecService"
android:exported="true" />
🧨 示例代码
public class ExecService extends Service {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
String cmd = intent.getStringExtra("cmd");
Runtime.getRuntime().exec(cmd); // ❌ 未校验直接执行任意命令
return START_NOT_STICKY;
}
}
🧨 旧版本代码(IntentService):
public class ExecIntentService extends IntentService {
public ExecIntentService() {
super("ExecIntentService");
}
@Override
protected void onHandleIntent(Intent intent) {
String cmd = intent.getStringExtra("cmd");
Runtime.getRuntime().exec(cmd); // 未校验直接执行
}
}
🔍 关键词
Runtime.getRuntime().execProcessBuildergetStringExtra("cmd")android:exported="true"
⚠️ 风险说明
外部 App 可通过构造恶意 Intent 实现远程命令执行,例如:
adb shell am startservice -n com.vuln/.ExecService --es cmd "rm -rf /sdcard"
adb shell am startservice -n com.vuln/.ExecService --es cmd "id > /sdcard/test.txt"
🔎 排查流程
AndroidManifest.xml
└─ Service 配置是否导出
└─ 检查 onStartCommand 或 onHandleIntent 方法
└─ 是否从 Intent 获取参数
└─ 是否传入敏感 API(如 exec)
└─ 是否缺少参数校验或权限控制
🛡️ 修复建议
- 禁止导出:
android:exported="false" - 添加权限或校验调用者包名
- 限制命令在白名单范围内
✅ 2. 权限绕过漏洞
主要用于旧版APP检测,9以上基本不用检测这项
📜 Manifest 配置
<service
android:name=".DeleteAppService"
android:exported="true" />
🧨 示例代码
public class DeleteAppService extends Service {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
String pkg = intent.getStringExtra("pkg");
getPackageManager().deletePackage(pkg, null, 0); // ❌ 无权限判断
return START_NOT_STICKY;
}
}
🧨 旧版本代码(IntentService):
public class DeleteAppIntentService extends IntentService {
public DeleteAppIntentService() {
super("DeleteAppIntentService");
}
@Override
protected void onHandleIntent(Intent intent) {
String pkg = intent.getStringExtra("pkg");
getPackageManager().deletePackage(pkg, null, 0); // 无鉴权
}
}
🔍 关键词
deletePackagePackageManagergetStringExtra("pkg")
⚠️ 风险说明
攻击者可调用该服务删除其他 App(前提:拥有系统权限或被赋权)。
adb shell am startservice -n com.example.vuln/.DeleteAppService --es pkg "com.android.settings"
🔎 排查流程
AndroidManifest.xml
└─ 导出 Service 检查
└─ onStartCommand 或 onHandleIntent 是否处理敏感功能
└─ 是否传入 PackageManager 敏感操作
└─ 是否校验调用方身份或权限
🛡️ 修复建议
- 添加权限控制:
android:permission - 使用
checkCallingPackage()验证来源 - 拒绝非系统调用或加签限制
✅ 3. 敏感信息泄露(Bind Service)
📜 Manifest 配置
<service
android:name=".TokenService"
android:exported="true" />
🧨 示例代码:
public class TokenService extends Service {
private final IBinder binder = new MyBinder();
public class MyBinder extends Binder {
public String getToken() {
return AppConfig.SESSION_TOKEN; // ❌ 敏感信息直接返回
}
}
@Override
public IBinder onBind(Intent intent) {
return binder;
}
}
🔍 关键词
onBind、Binder、getToken()SharedPreferences.getString/AppConfig.xxx
⚠️ 风险说明
外部 App 可绑定该 Service 并调用公开方法获取敏感信息。
利用示例与说明 👉 Android 绑定服务是怎么回事?一文带你看懂原理与信息泄露风险-极客星球
🔎 排查流程
AndroidManifest.xml
└─ 检查 exported="true"
└─ 是否为 bind 类型服务
└─ 返回 Binder 是否包含敏感方法
└─ 是否未加权限限制
🛡️ 修复建议
- 使用
android:permission限制绑定 - 敏感方法加权限或调用方校验
- 或干脆不导出:
android:exported="false"
✅ 4. SQL 注入漏洞
📜 Manifest 配置
<service
android:name=".QueryService"
android:exported="true" />
🧨 示例代码
public class QueryService extends Service {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
String name = intent.getStringExtra("name");
Cursor cursor = db.rawQuery("SELECT * FROM user WHERE name='" + name + "'", null); // ❌ 拼接 SQL
return START_NOT_STICKY;
}
}
🧨 旧版本代码(IntentService):
public class QueryIntentService extends IntentService {
public QueryIntentService() {
super("QueryIntentService");
}
@Override
protected void onHandleIntent(Intent intent) {
String name = intent.getStringExtra("name");
db.rawQuery("SELECT * FROM user WHERE name='" + name + "'", null); // 拼接注入
}
}
🔍 关键词
rawQuery("SELECT、execSQL("+ 参数 +拼接getStringExtra("name")
⚠️ 风险说明
攻击者可注入恶意 SQL:
adb shell am startservice -n com.example.vuln/.QueryService --es name "' OR 1=1 --"
🔎 排查流程
AndroidManifest.xml
└─ 检查 Service 是否导出
└─ onStartCommand 或 onHandleIntent 是否拼接 SQL
└─ 是否直接使用外部输入构建语句
🛡️ 修复建议
- 改为参数绑定方式: db.rawQuery(“SELECT * FROM user WHERE name=?”, new String[]{name});
- 增加正则校验输入合法性
✅ 5. 拒绝服务漏洞(DoS)
📜 Manifest 配置
<service
android:name=".HeavyService"
android:exported="true" />
🧨 示例代码
public class HeavyService extends Service {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
for (int i = 0; i < 10000000; i++) {
Math.sqrt(i); // ❌ 无限制的重运算
}
return START_NOT_STICKY;
}
}
🧨 旧版本代码(IntentService):
public class HeavyIntentService extends IntentService {
public HeavyIntentService() {
super("HeavyIntentService");
}
@Override
protected void onHandleIntent(Intent intent) {
for (int i = 0; i < 10000000; i++) {
Math.sqrt(i); // 高负载计算
}
}
}
🔍 关键词
onStartCommand中存在大循环for (int i = 0; i < ...startService的频繁调用点
⚠️ 风险说明
攻击者频繁调用该服务,可能导致内存溢出 或系统卡顿。
adb shell am startservice -n com.example.vuln/.HeavyService
🔎 排查流程
AndroidManifest.xml
└─ 查找 exported Service
└─ onStartCommand 或 onHandleIntent 是否有大量计算/循环
└─ 是否对调用频率进行限制
🛡️ 修复建议
- 添加调用频率限制或冷却时间
- 使用前台服务或 JobScheduler 控制运行条件
- 添加权限限制或取消导出
✅ ADB 命令总结
1. 命令执行漏洞(RCE)
adb shell am startservice -n com.example.vuln/.ExecService --es cmd "rm -rf /sdcard/"
📌 向导出服务传入恶意命令,触发 Runtime.getRuntime().exec(cmd) 执行任意命令。
2. 权限绕过攻击
adb shell am startservice -n com.example.vuln/.DeleteAppService --es pkg "com.android.settings"
📌 利用无鉴权服务绕过权限检查,强行卸载任意包(如系统包需签名绕过)。
3. 敏感信息泄露(绑定服务)
❌ 无法直接用 ADB 利用,必须通过恶意 App 使用
bindService()获取 Binder 后调用。
4. SQL 注入漏洞
adb shell am startservice -n com.example.vuln/.QueryService --es name "' OR 1=1 --"
📌 向服务传递恶意 SQL 语句,拼接进 rawQuery() 导致注入漏洞。
5. 拒绝服务(DoS)
adb shell am startservice -n com.example.vuln/.HeavyService
📌 重复调用该服务导致 CPU 计算密集,造成卡顿或系统崩溃。
📘 ADB 命令简要说明
adb shell am startservice -n 包名/服务类名 --es 参数名 参数值
表示通过 ActivityManager (am) 工具发送一个启动服务的 Intent,并附带参数。











暂无评论内容