当前位置: 首页 > news >正文

【APK安全】Receiver嗅探:Android广播组件的权限与UID安全防护及测试指南

文章目录

    • 前言
    • 一、Receiver嗅探的核心风险与防御方案
      • 1. 风险1:隐式广播未授权嗅探(权限配置缺陷)
        • 风险本质
        • 典型攻击案例:验证码短信广播嗅探
        • 防御方案:权限强控+显式广播+接收者校验
      • 2. 风险2:动态注册Receiver的嗅探(权限缺失与校验遗漏)
        • 风险本质
        • 典型攻击案例:动态注册广播的登录状态篡改
        • 防御方案:动态注册指定权限+发送者强校验
      • 3. 风险3:UID共享与签名漏洞导致的嗅探
        • 风险本质
        • 典型攻击案例:共享UID的系统工具广播嗅探
        • 防御方案:严控UID共享+强化签名安全
    • 二、Receiver嗅探的安全测试方法
      • 1. 静态测试:定位配置与代码漏洞
        • (1)静态注册Receiver检测(Manifest配置)
        • (2)动态注册Receiver检测(代码逻辑)
      • 2. 动态测试:模拟嗅探验证防御有效性
        • (1)隐式广播嗅探测试
        • (2)动态注册Receiver权限测试
        • (3)UID共享漏洞测试(需签名密钥)
    • 三、总结:Receiver安全防御的核心原则

前言

Receiver作为Android跨组件通信的核心组件,承担着“事件通知”与“数据传递”的关键角色(如系统启动完成、网络状态变化、应用内部状态更新)。但其“广播式”的通信特性,也使其成为恶意应用窃取敏感数据的目标——Receiver嗅探即恶意应用通过注册与目标应用相同的广播规则(Intent Filter)、绕过权限校验或利用UID(用户标识符)漏洞,非法接收目标应用发送的广播数据(如验证码、订单信息、用户隐私),导致数据泄露或业务逻辑被篡改。

本文聚焦Receiver嗅探的三大核心风险(权限配置缺陷、UID共享漏洞、动态注册防护缺失),拆解攻击路径、提供可落地的防御方案,并详解全流程安全测试方法。

一、Receiver嗅探的核心风险与防御方案

Receiver的安全本质是“可控的广播接收”,所有嗅探风险均源于“接收范围超出预期”。以下从权限管控、UID隔离、动态注册三个维度,分析风险成因与防御手段。

1. 风险1:隐式广播未授权嗅探(权限配置缺陷)

风险本质

隐式广播(未通过setComponent/setPackage指定接收者)依赖Intent Filter(Action、Category、Data)匹配Receiver,若目标应用的Receiver未配置android:permission(接收权限)或sendBroadcast时未指定发送权限,恶意应用可通过注册相同的Intent Filter,“监听”并接收目标应用的广播数据。常见漏洞包括:

  • Receiver无接收权限(exported=true却未配置android:permission);
  • 发送广播时未指定permission参数(允许任何Receiver接收);
  • 自定义接收权限未声明(游离权限,恶意应用可自定义同名权限)。
典型攻击案例:验证码短信广播嗅探
  1. 目标应用(如金融APP)通过隐式广播传递短信验证码,用于登录验证,Manifest与代码配置存在漏洞:

    <!-- 风险配置:Receiver允许外部接收(exported=true),无接收权限控制 -->
    <receiverandroid:name=".receiver.SmsCodeReceiver"android:exported="true"> <!-- 允许外部应用接收 --><intent-filter><!-- 隐式匹配规则:Action为自定义验证码事件 --><action android:name="com.target.app.action.SMS_CODE_RECEIVED" /><category android:name="android.intent.category.DEFAULT" /></intent-filter>
    </receiver>
    
    // 风险代码:发送隐式广播时未指定发送权限,任何Receiver均可接收
    String smsCode = "123456"; // 用户登录验证码(敏感数据)
    Intent smsIntent = new Intent("com.target.app.action.SMS_CODE_RECEIVED");
    smsIntent.putExtra("sms_code", smsCode); // 携带敏感验证码
    sendBroadcast(smsIntent); // 未指定permission参数,广播“裸发”
    
  2. 恶意应用通过以下步骤嗅探验证码:

    • 在自身Manifest中注册相同的Intent Filter:

      <receiverandroid:name=".receiver.FakeSmsReceiver"android:exported="true"><intent-filter><action android:name="com.target.app.action.SMS_CODE_RECEIVED" /> <!-- 与目标应用相同的Action --><category android:name="android.intent.category.DEFAULT" /></intent-filter>
      </receiver>
      
    • FakeSmsReceiver中接收并窃取验证码:

      public class FakeSmsReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {if ("com.target.app.action.SMS_CODE_RECEIVED".equals(intent.getAction())) {// 窃取目标应用的验证码String smsCode = intent.getStringExtra("sms_code");// 上传验证码到恶意服务器uploadToServer(smsCode);}}
      }
      
  3. 当目标应用发送验证码广播时,系统会同时将广播传递给目标应用的SmsCodeReceiver和恶意应用的FakeSmsReceiver,导致验证码泄露。

防御方案:权限强控+显式广播+接收者校验
  1. 为Receiver配置接收权限,避免游离权限
    在Manifest中为Receiver配置android:permission(接收广播需声明的权限),并确保该权限为自定义权限且已声明(建议保护级别设为signature),禁止normal级权限(恶意应用可直接声明):

    <!-- 声明自定义接收权限(签名级保护,仅同签名应用可接收) -->
    <permissionandroid:name="com.target.app.permission.RECEIVE_SMS_CODE"android:protectionLevel="signature"android:description="@string/receive_sms_perm_desc" /> <!-- 权限用途说明 --><!-- Receiver关联接收权限 -->
    <receiverandroid:name=".receiver.SmsCodeReceiver"android:exported="true"android:permission="com.target.app.permission.RECEIVE_SMS_CODE"> <!-- 接收需权限 --><intent-filter><action android:name="com.target.app.action.SMS_CODE_RECEIVED" /><category android:name="android.intent.category.DEFAULT" /></intent-filter>
    </receiver>
    
  2. 发送广播时指定发送权限,双重防护
    调用sendBroadcast时通过第二个参数指定“发送权限”(接收者需声明该权限才能接收),即使Receiver权限配置失效,也能通过发送权限拦截非法接收:

    String smsCode = "123456";
    Intent smsIntent = new Intent("com.target.app.action.SMS_CODE_RECEIVED");
    smsIntent.putExtra("sms_code", smsCode);
    // 关键:指定发送权限,接收者需声明该权限才能接收
    sendBroadcast(smsIntent, "com.target.app.permission.RECEIVE_SMS_CODE");
    
  3. 优先使用显式广播,禁止非必要隐式广播
    对应用内部或指定合作应用的广播,通过setComponentsetPackage指定接收者,避免隐式匹配导致嗅探:

    String smsCode = "123456";
    Intent smsIntent = new Intent("com.target.app.action.SMS_CODE_RECEIVED");
    // 显式指定接收者(目标应用的SmsCodeReceiver)
    smsIntent.setComponent(new ComponentName("com.target.app", "com.target.app.receiver.SmsCodeReceiver"));
    smsIntent.putExtra("sms_code", smsCode);
    sendBroadcast(smsIntent);
    

    注意:Android 8.0(API 26)后对隐式广播有严格限制(除少数系统广播外,后台应用无法接收隐式广播),但前台应用仍可接收,需持续防护。

  4. 代码层校验发送者UID/包名,拒绝非法广播
    在Receiver的onReceive方法中,通过getCallingUid(获取发送者UID)或getCallingPackage(获取发送者包名)校验身份,仅允许可信应用发送的广播:

    public class SmsCodeReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {// 校验发送者UID(仅允许自身或可信应用的UID)int callingUid = Binder.getCallingUid();Set<Integer> trustedUids = new HashSet<>();trustedUids.add(context.getApplicationInfo().uid); // 自身UIDtrustedUids.add(10086); // 可信合作应用UID(需实际替换)if (!trustedUids.contains(callingUid)) {abortBroadcast(); // 终止广播传递throw new SecurityException("Unauthorized sender UID: " + callingUid);}// (可选)校验发送者包名,防止UID伪造String callingPackage = context.getPackageManager().getNameForUid(callingUid);Set<String> trustedPackages = new HashSet<>();trustedPackages.add("com.target.app"); // 自身包名trustedPackages.add("com.cooper.app"); // 可信合作应用包名if (callingPackage == null || !trustedPackages.contains(callingPackage)) {abortBroadcast();throw new SecurityException("Unauthorized sender package: " + callingPackage);}// 校验通过,处理验证码String smsCode = intent.getStringExtra("sms_code");handleSmsCode(smsCode);}
    }
    

2. 风险2:动态注册Receiver的嗅探(权限缺失与校验遗漏)

风险本质

动态注册的Receiver(通过registerReceiver在代码中注册,无需Manifest配置)若未指定permission参数(接收权限),或未在代码中校验发送者身份,恶意应用可通过发送伪造广播或监听敏感事件,实现嗅探或业务干扰。常见漏洞包括:

  • 动态注册时未传递permission参数(允许任何应用发送广播);
  • 接收系统广播(如CONNECTIVITY_CHANGE)时未过滤敏感数据;
  • 未校验发送者UID,导致恶意应用发送伪造广播篡改逻辑。
典型攻击案例:动态注册广播的登录状态篡改
  1. 目标应用动态注册LoginStatusReceiver,用于接收登录状态更新广播(如用户登录/登出),但未配置权限与身份校验:

    // 风险代码:动态注册Receiver时未指定接收权限,无身份校验
    private LoginStatusReceiver loginReceiver;@Override
    protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);loginReceiver = new LoginStatusReceiver();IntentFilter filter = new IntentFilter("com.target.app.action.LOGIN_STATUS_CHANGED");// 未传递permission参数,任何应用均可发送广播registerReceiver(loginReceiver, filter); 
    }// 接收登录状态广播,未校验发送者
    private class LoginStatusReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {boolean isLogin = intent.getBooleanExtra("is_login", false);// 直接更新登录状态(无校验,恶意应用可伪造)updateLoginStatus(isLogin);}
    }
    
  2. 恶意应用发送伪造的“登录成功”广播,篡改目标应用状态:

    // 恶意应用发送伪造广播
    Intent fakeLoginIntent = new Intent("com.target.app.action.LOGIN_STATUS_CHANGED");
    fakeLoginIntent.putExtra("is_login", true); // 伪造登录成功
    // 发送广播,目标应用的Receiver会接收并更新状态
    sendBroadcast(fakeLoginIntent);
    
  3. 目标应用未校验广播发送者,误将伪造广播当作合法事件,导致未登录用户被强制显示“已登录”状态,可能泄露其他用户的缓存数据。

防御方案:动态注册指定权限+发送者强校验
  1. 动态注册时指定接收权限,过滤非法发送者
    调用registerReceiver时,通过第三个参数传递接收权限(需提前在Manifest中声明),确保仅拥有该权限的应用能发送广播:

    // Manifest中声明自定义接收权限
    <!-- <permission android:name="com.target.app.permission.SEND_LOGIN_STATUS" android:protectionLevel="signature" /> -->// 动态注册时指定接收权限
    @Override
    protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);loginReceiver = new LoginStatusReceiver();IntentFilter filter = new IntentFilter("com.target.app.action.LOGIN_STATUS_CHANGED");// 关键:传递接收权限,发送者需声明该权限registerReceiver(loginReceiver, filter, "com.target.app.permission.SEND_LOGIN_STATUS", null);
    }
    
  2. 在动态Receiver中校验发送者身份
    即使指定了接收权限,仍需在onReceive中通过getCallingUid/getCallingPackage校验发送者,避免权限被绕过(如共享UID漏洞):

    private class LoginStatusReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {// 校验发送者包名(仅允许自身发送)String callingPackage = context.getPackageManager().getNameForUid(Binder.getCallingUid());if (!"com.target.app".equals(callingPackage)) {abortBroadcast();return;}// 校验通过,处理登录状态boolean isLogin = intent.getBooleanExtra("is_login", false);updateLoginStatus(isLogin);}
    }
    
  3. 动态Receiver注销,避免内存泄漏与持续暴露
    onDestroy中及时注销动态Receiver,避免应用退出后仍能接收广播,减少被嗅探的窗口:

    @Override
    protected void onDestroy() {super.onDestroy();if (loginReceiver != null) {unregisterReceiver(loginReceiver); // 注销Receiver}
    }
    

3. 风险3:UID共享与签名漏洞导致的嗅探

风险本质

Android通过UID实现应用隔离,不同应用默认拥有唯一UID;但同签名且声明android:sharedUserId的应用可共享UID(共享数据与权限)。若目标应用滥用sharedUserId且签名密钥泄露,恶意应用可通过相同签名与UID伪装成“可信应用”,接收本应仅内部或同签名应用才能接收的广播,实现嗅探。

典型攻击案例:共享UID的系统工具广播嗅探
  1. 目标应用(如系统清理工具)为方便与同厂商应用共享数据,在Manifest中配置:

    <!-- 风险配置:滥用sharedUserId,且签名密钥未妥善保管 -->
    <manifestxmlns:android="http://schemas.android.com/apk/res/android"package="com.target.systemcleaner"android:sharedUserId="android.uid.systemcleaner"> <!-- 共享UID --><receiverandroid:name=".receiver.CleanStatusReceiver"android:exported="false" <!-- 理论上仅内部接收,但UID共享导致漏洞 -->android:permission="com.target.permission.RECEIVE_CLEAN_STATUS"><intent-filter><action android:name="com.target.app.action.CLEAN_FINISHED" /> <!-- 清理完成广播,含敏感日志 --></intent-filter></receiver>
    </manifest>
    
  2. 恶意应用获取目标应用的签名密钥(如密钥泄露),在自身Manifest中配置相同sharedUserId与签名:

    <manifestxmlns:android="http://schemas.android.com/apk/res/android"package="com.malicious.fakecleaner"android:sharedUserId="android.uid.systemcleaner"> <!-- 共享同一UID --><!-- 用目标应用的泄露密钥签名 -->
    </manifest>
    
  3. 由于共享UID,恶意应用与目标应用拥有相同的权限,即使CleanStatusReceiver设为exported=false,恶意应用仍能:

    • 动态注册相同Action的Receiver,接收“清理完成”广播;
    • 读取广播中携带的敏感日志(如用户安装的应用列表、清理的文件路径)。
防御方案:严控UID共享+强化签名安全
  1. 禁止滥用sharedUserId,最小化权限范围
    非必要不使用sharedUserId,若需跨应用协作,优先通过“显式广播+签名校验”实现,而非依赖UID共享;若必须使用,需:

    • 自定义非系统级UID(避免android.uid.system等敏感系统UID);
    • 在Receiver中额外校验发送者包名(即使共享UID,也仅允许可信包名发送)。
  2. 妥善保管签名密钥,避免密钥泄露

    • 使用2048位以上RSA密钥签名,禁止使用debug签名;
    • 采用Android Keystore存储密钥,禁止硬编码到代码或配置文件;
    • 定期轮换签名密钥(发现泄露风险时立即更新),并通过应用商店推送签名更新。
  3. 对共享UID的Receiver强化校验
    若Receiver所在应用使用sharedUserId,需在onReceive中强制校验发送者包名,拒绝非预期包名的广播:

    @Override
    public void onReceive(Context context, Intent intent) {String callingPackage = context.getPackageManager().getNameForUid(Binder.getCallingUid());// 仅允许自身和可信同UID应用发送if (!"com.target.systemcleaner".equals(callingPackage) && !"com.trusted.partner".equals(callingPackage)) {abortBroadcast();throw new SecurityException("Unauthorized package for shared UID");}// 处理广播数据
    }
    

二、Receiver嗅探的安全测试方法

Receiver嗅探的测试需围绕“权限配置合规性”“UID与签名安全性”“广播接收可控性”三大核心,结合静态代码审计(反编译+源码分析)与动态攻击模拟,覆盖显式/隐式、静态/动态注册的全场景。

1. 静态测试:定位配置与代码漏洞

静态测试通过JADX(反编译APK)或Android Studio(源码审计),识别Receiver的权限配置、注册方式、身份校验中的风险点,核心工具为JADX、Android Lint。

(1)静态注册Receiver检测(Manifest配置)
  • 步骤1:筛选高风险Receiver
    在JADX中打开AndroidManifest.xml,搜索<receiver>标签,重点关注以下风险项:

    • android:exported="true"且未配置android:permission的Receiver(允许外部接收却无权限控制);
    • 配置android:permission但未在Manifest中声明(游离权限)的Receiver;
    • 配置intent-filter的隐式Receiver(非必要场景,如内部通信使用隐式广播);
    • 应用声明android:sharedUserId且Receiver处理敏感数据的场景。
  • 步骤2:验证权限有效性
    对Receiver依赖的接收权限,使用ADB命令检查是否为系统预定义权限或已声明的自定义权限:

    # 检查权限是否存在(非游离权限)
    adb shell pm list permissions -f | grep "目标权限名称"
    # 示例:检查com.target.app.permission.RECEIVE_SMS_CODE
    adb shell pm list permissions -f | grep "com.target.app.permission.RECEIVE_SMS_CODE"
    

    若命令无输出,说明权限未声明,确认为游离权限风险。

(2)动态注册Receiver检测(代码逻辑)
  • 步骤1:定位动态注册点
    在JADX中搜索registerReceiver关键字,筛选所有动态注册Receiver的代码,重点关注:

    • 未传递permission参数的registerReceiver调用(如registerReceiver(receiver, filter));
    • 传递的permission为游离权限(未在Manifest中声明);
    • 接收敏感事件(如登录状态、验证码)且无身份校验的动态Receiver。
  • 步骤2:审计onReceive中的身份校验
    检查Receiver的onReceive方法,是否存在getCallingUidgetCallingPackagecheckCallingPermission调用,无校验逻辑则为风险。

2. 动态测试:模拟嗅探验证防御有效性

动态测试通过ADB命令、测试应用模拟恶意嗅探,验证Receiver的防御逻辑是否生效,核心工具为ADB、Android Studio(测试应用开发)。

(1)隐式广播嗅探测试
  1. 构建测试应用

    • 在测试应用Manifest中注册与目标Receiver相同的Intent Filter;
    • 若目标Receiver配置了接收权限,在测试应用中自定义同名游离权限(normal级),尝试绕过。
  2. 测试步骤

    • 启动目标应用,触发其发送敏感广播(如验证码、登录状态);

    • 查看测试应用的日志,确认是否接收到目标广播的数据:

      # 查看测试应用的广播接收日志
      adb logcat -s FakeSmsReceiver:V
      
    • 若测试应用能接收数据,说明存在嗅探风险;若抛出SecurityException,说明权限控制有效。

(2)动态注册Receiver权限测试
  1. ADB命令模拟发送广播
    使用ADB发送与目标动态Receiver匹配的广播,验证是否需权限:

    # 格式:adb shell am broadcast -a <Action> -n <接收者组件> --ei <extra_key> <extra_value>
    # 示例:尝试发送登录状态广播(无权限)
    adb shell am broadcast -a com.target.app.action.LOGIN_STATUS_CHANGED --ei is_login true
    
    • 若目标应用的Receiver接收并处理广播,说明动态注册时未配置权限;
    • 若返回Permission Denial,说明权限控制有效。
  2. 测试应用模拟伪造广播
    开发测试应用,发送与目标动态Receiver匹配的伪造广播,验证onReceive中的身份校验是否生效:

    • 若测试应用能篡改目标应用状态(如伪造登录),说明无身份校验;
    • 若广播被abortBroadcast或抛出异常,说明身份校验有效。
(3)UID共享漏洞测试(需签名密钥)
  1. 构建测试应用
    • 使用与目标应用相同的sharedUserId和签名密钥(测试环境可用debug密钥);
    • 注册与目标Receiver相同Action的Receiver,或动态注册接收内部广播。
  2. 测试步骤
    • 安装目标应用与测试应用(需卸载同UID的其他应用);
    • 触发目标应用发送内部广播;
    • 若测试应用能接收广播,说明sharedUserId配置存在风险,需强化包名校验。

三、总结:Receiver安全防御的核心原则

Receiver的安全核心是“最小化广播接收范围”,所有防御措施需围绕以下原则展开:

  1. 权限强控:静态Receiver配置android:permission,动态Receiver注册时指定权限,发送广播时补充发送权限,避免游离权限;
  2. 显式优先:非必要不使用隐式广播,内部通信强制用显式广播(setComponent/setPackage);
  3. 身份校验:在onReceive中强制校验发送者UID/包名,即使配置权限也需二次确认;
  4. UID谨慎:禁止滥用sharedUserId,共享UID场景需额外校验包名,避免签名密钥泄露;
  5. 持续测试:静态审计配置与代码,动态模拟嗅探场景,确保防御逻辑在不同Android版本下生效。
http://www.dtcms.com/a/398933.html

相关文章:

  • (自用)vim的高级命令
  • ELK分析系统详解
  • 架构师成长之路06:缓存设计收官篇,缓存该放哪?写缓存怎么用?这篇讲透最后两个核心问题
  • 电子商务网站建设外包服务wordpress 教程
  • 简述Android应用程序结构包含哪些部分
  • 百度网站改版提交wordpress主题改中文版
  • 前端埋点(tracking)技术介绍(记录用户行为和页面性能数据)(埋点代码)ajax埋点、img埋点、navigator.sendBeacon埋点
  • 网站备案ps网站建设入门教程视频教程
  • 免费申请网站永久域名建设一个网站用什么软件下载
  • 内存泄漏可能由哪些原因导致?
  • 数据采集与同步
  • Weblogic T3 CVE-2018-2628漏洞复现
  • Spring Cloud Alibaba快速入门-Sentinel熔断规则
  • TDEngine-OSS-3.3.7.5开源版搭建手册(包含单节点与三副本高可用方案搭建)
  • 【强化学习】解决MPE环境中两个小球重合导致态势为nan问题
  • 建教会网站的内容部门网站建设个人总结
  • 陕西省建设监理协会网站证书关于网站开发的外文书籍
  • FFmpegLinux开发环境开荒
  • iPhone HTTPS 抓包实战,原理、常见工具、SSL Pinning 问题与替代工具的解决方案
  • 在百度做橱柜网站wordpress add_editor_style
  • 256m内存 wordpresswordpress 博客主题 seo
  • 【2025CVPR-域泛化方向】PEER Pressure:单源域泛化的模型间正则化方法解析
  • 北京网站建设咸宁软件外包公司的出路
  • 【力扣LeetCode】 67二进制求和
  • 世界模型是什么
  • 网站设计师联盟仿网站建设教程视频教程
  • 十大购物网站排名中山免费建网站
  • C++23特性全解析:从编译器支持矩阵到多维数组性能优化实战
  • Visual Studio 2022 / VS2022 激活码
  • MyBatis“别名扫描”功能