BindService错误处理与CVE-2023-21138 & CVE-2023-40130

背景

漏洞分析

  • CVE-2023-21138位于2023 #6补丁中,是一个造成BAL Bypass的漏洞,描述如下:

    In onNullBinding of CallRedirectionProcessor.java, there is a possible long lived connection due to improper input validation. This could lead to local escalation of privilege and background activity launches with User execution privileges needed. User interaction is not needed for exploitation.Product: AndroidVersions: Android-11 Android-12 Android-12L Android-13Android ID: A-273260090

  • 对于了解BAL Bypass的同学来说很容易理解,但是新手只看描述可能觉得不知所云。这里需要仔细阅读一下后台Activity启动限制的官方说明,豁免条件里面有这么一段:

    The app has one of the following services that is bound by the system. These services might need to launch a UI.

  • 具体而言有这些服务:

    AccessibilityService, AutofillService, CallRedirectionService, HostApduService, InCallService, TileService, VoiceInteractionService, VrListenerService.

  • 而CallRedirectionProcessor正是和CallRedirectionService交互的类,由于此项豁免的存在,只要我们的应用声明一个CallRedirectionService被系统bindService,就可以后台弹出界面了。到这里其实都没什么问题,因为本来就是这么设计的,但是呢在CallRedirectionProcessor里面对onNullBinding的处理不正确,导致了漏洞出现,这个属于对bindService错误处理不当的问题,下一节我们继续分析。

onNullBinding

  • 这个方法属于ServiceConnection,平时写代码的时候可能开发者就只关注onServiceConnected和onServiceDisconnected,从来没关注过onNullBinding,而且这个方法还不是必须实现。那么我们看下onNullBinding的说明:

    Called when the service being bound has returned null from its onBind() method. This indicates that the attempted service binding represented by this ServiceConnection will never become usable.

  • 同时Google还提醒我们注意:

    Note: The app that requested the binding must still call Context#unbindService(ServiceConnection) to release the tracking resources associated with this ServiceConnection even if this callback was invoked following Context.bindService() bindService().

  • 很好理解,意思就是当onNullBinding被调用的时候,开发者还需要主动调用unbindService去释放资源。那么问题来了,这个情况会不会影响BAL的判断呢?答案是肯定的,实际上在BAL的检查中,当bindService时候会记录被绑定的UID,列入允许后台启动Activity的一个List中,当unBindService的时候才会将其移除,所以如果开发者在onNullBinding被调用的时候,没有主动调用unbindService去释放资源,同样会导致UID记录不会移除,从而导致BAL Bypass。这部分代码可以参考ServiceRecord的源代码,有兴趣的同学可以自己阅读。

交互条件

  • 一般来说比较理想的BAL Bypass需要无条件触发,这样对黑灰产才有实用价值,而该漏洞中想成为CallRedirectionService需要用户主动交互,且需要有电话拨出,所以这种漏洞实用性上比较差,只是去获得Google Bug Bounty。当然如果是TileService其实可以诱骗用户去放到菜单中,这个暂且不讨论。

扩展分析之onBindingDied

  • 那么ServiceConnection中除了onNullBinding,还有没有类似的错误处理回调呢?我们阅读文档发现还是有的,它就是onBindingDied,其API描述:

    Called when the binding to this connection is dead. This means the interface will never receive another connection. The application will need to unbind and rebind the connection to activate it again. This may happen, for example, if the application hosting the service it is bound to has been updated.

  • Google也同样提醒开发者:

    Note: The app that requested the binding must call Context#unbindService(ServiceConnection) to release the tracking resources associated with this ServiceConnection even if this callback was invoked following Context.bindService() bindService().

  • onBindingDied比onNullBinding复杂一点的是,如果开发者为获取到的Binder对象注册了DeathRecipient,那么我们就需要看他在DeathRecipient的回调中有没有调用unbindService,不过很有趣的是在CallRedirectionProcessor中,onBindingDied和DeathRecipient都是没有的。于是我在7月初提交了这个漏洞,官方也予以承认并标记为CVE-2023-40130,在2023 #10补丁中进行了修复,Google对待漏洞的态度还是非常值得赞赏的。

总结

  • 这两个漏洞我们可以从两个角度分析,一方面是开发者需要对官方提供的API更加熟悉,同时考虑各种错误处理的情况。当然另一方面呢我觉得Android有些API设计的确实也不太合理,应该对错误处理进行适当的简化,比如在这个案例中,实际上无论是Binder对方返回null还是对方进程死亡,都完全可以自动地进行unbind操作,而无需开发者自己进行,不然的话就很容易导致开发者出现错误。本例对于普通应用开发者应该影响不大,但是由于系统开发者而言,这就造成漏洞的出现。