StrandHogg是一个 Android 任务栈劫持漏洞,属于应用层漏洞。
Activity属性:
1 | <activity |
taskAffinity(任务相似性):用来标识Activity与任务的联系,该属性如果没有被设置,就从自身的 Application 继承,Application 的taskAffinity,它的值为 Manifest 的包名,也就是taskAffinity默认属性为自身应用包名的字符串。那么该应用中的所有 Activity 都具有同一相似性。具有相同taskAffinity属性的 Activity归属同一任务(从用户的角度来看,则是归属同一“应用”),甚至可以在一个任务栈中放置不同应用中定义的Activity。如果指定 Activity 与任何任务均无相似性,可以将其设置为空字符串。
allowTaskReparenting(任务重编):这个属性会使该Activity具有在任务栈被重新编排的能力,当下一次将启动 Activity 的任务转至前台时, Activity 是否可以从启动它的任务移动到和它具有相同taskAffinity属性的任务,转移的时机是在具有相同taskAffinity属性的任务转移到前台时,true代表能够移动,false表示它必须留在它所在的任务中。它不会立即将攻击者活动转移到其目标上。只是当下次启动目标任务时,Android 操作系统会评估所有任务和活动,并查找标记有此属性的活动并在启动相关任务之前移动它们。由于任务栈的taskAffinity值与该栈中的根Activity永远一致,具有“singleTask”或“singleInstance”启动模式的 Activity 只能位于任务的根,因此Android 的官方文档认为这两种启动模式对allowTaskReparenting是没有意义的,但该漏洞利用的就是看似没有意义的singleTask启动模式。这样可以使恶意的Activity放到受害应用的任务栈的根位置。如果未设置此 allowTaskReparenting 属性,则元素的相应属性设置默认值为 false。
1. StrandHogg1.0
1.1 漏洞介绍
该漏洞为一个 Android 任务栈劫持漏洞,在2019年披露,影响范围包括Android 10以下。该漏洞可以让恶意应用注入一个Activity到它设定好的受害应用的顶层,因此恶意应用可以精心设计一个具有诱导性的Activity注入到受害应用中进行攻击。
1.2 漏洞原理
因为大部分应用的taskAffinity属性都没有设置,默认为其包名,那么就可以在恶意应用的一个Activity标签中,设置与受害应用包名一致的taskAffinity值。该Activity(下称hackActivity)启动的时候就会创建一个与受害应用的taskAffinity属性相同的一个任务栈,到时候会和受害应用共享一个任务栈,并且位于该任务栈的根。在我们启动受害应用时,受害应用的任务就会被放到前台,然后位于根的hackActivity就会放到前台,那么当我们打开受害应用所看到的Activity,并不是该应用原有的Activity,而是我们的hackActivity。我们可以将hackActivity设计成一个钓鱼页面,实现钓鱼攻击,获取用户的隐私和诱导用户授予恶意软件相应权限。
1.3 漏洞复现
假设受害应用的包名为com.zj.wuaipojie
。现在我们设计一个恶意应用,恶意应用中有三个Activity:MainActivity用来启动其它的Activity,Innocent是用来伪装自己的,通常是一个无害的界面,Hack则是一个钓鱼界面。Hack中设allowTaskReparenting和taskAffinity两个属性,taskAffinity设计为受害应用的包名,但是没有设计启动模式为singleTask,只是在启动这个Activity时,会对intent传入一个参数FLAG_ACTIVITY_NEW_TASK,这个跟singleTask的作用是类似的。
此时点击恶意应用,在CMD输入adb shell dumpsys activity activities
查看任务栈。可以看到MainActivity.class
与Innocent.class
处在同一个任务栈,而Hack.class
在另一个任务栈。
此时有三种情况可以达到钓鱼目的:
- 先启动受害应用,点击返回键或Home键切回到桌面,启动恶意应用,点击Home键或页面切换键回到受害应用,发现页面已更改。
这里为什么要点击Home键或页面切换键而不是返回键呢?正常来说,点击返回键会返回到桌面,而不是返回到另一个任务栈中。但是按照我们上面的实现方法,在恶意应用中点击返回键会去到我们的钓鱼页面,此时很有可能会被发现端倪。 - 启动恶意应用,点击Home键,启动受害应用,发现页面已更改。
如果在恶意应用中点击返回键会去到钓鱼页面,但用户之前并没有启动过受害应用,所以这个钓鱼就更明显了。 - 启动恶意应用,点击页面切换键去到非受害应用(此时受害应用还未加载,所以不可能通过页面切换键去到受害应用),在非受害应用中点击返回键或Home键,再启动受害应用,发现页面已更改。
这三种情况看似苛刻,但如果钓鱼页面做得足够精良,其实很难发现端倪的,生活中绝大部分人在玩手机的时候应该不会特意记住自己打开了什么软件,顺序是怎样的。
这里还有一个问题,为什么要设置Innocent?如果不设置,点击恶意应用,由于startActivity()
的原因会让Hack在MainActivity之上,所以恶意应用为了掩盖自己的真实意图,需要再startActivity()
一个Innocent,让用户看不到“不应该出现在这个应用的界面”,达到伪装目的。
2. StrandHogg2.0(CVE-2020-0096)
2.1 漏洞介绍
StrandHogg2.0和StrandHogg1.0均为同一家公司promo披露,相比StrandHogg1.0必须明确地将他们的目标应用程序硬编码到 Android Manifest,StrandHogg2.0并不需要在AndroidManifest.xml
上进行设计,而是在代码中实现,如有需要可以动态修改注入的应用程序,也可以同时对多个程序进行劫持。影响范围为Android 8和Andorid 9。
2.2 漏洞原理
这个漏洞主要是利用startActivities()
这个API,它的功能是一次启动多个Activity,传入一个intent数组,Android会解析每个intent,并逐个启动它们。startActivities()
默认认为intent数组第一个Activity的启动者是当前应用,而第二个Activity的启动者是第一个Activity所属的那个应用,以此类推,那么我们只要控制将第一个Activity的所有者设为我们所要攻击的受害应用,那么就可以让我们的hackActivity被受害应用启动,实现Activity劫持。
2.3 漏洞复现
在StrandHogg1.0的基础上,可以去掉allowTaskReparenting和taskAffinity属性,修改MainActivity,Intents[0]需要设置的是受害应用的包名和包名+可导出类名,从而使得Intents[1]里的Activity能够从受害应用中启动,就可以实现页面替换。
可导出类名可用drozer工具探测,一般MainActivity都是可导出的。
与StranHogg1.0同样以上三种情况可以达到钓鱼目的,但是2、3情况,对于有开屏广告的应用程序不适用,比如yiqi,weibo等。点击受害应用后,卡在了钓鱼页面几秒,再播放广告,正常进入MainActivity。而StranHogg1.0是可以对抗开屏广告的,因为启动受害应用,ui.MainActivity入栈,但由于Hack的启动模式为singleTask,会将上面的ui.MainActivity出栈,让Hack位于栈顶。
3. 漏洞修复
现在taskAffinity属性只对相同UID的应用有效,也就是说,只有共享UID的应用才可以进行Activity的移动,UID在应用安装时被分配,并且在应用存在于手机上期间,都不会改变。一个应用程序只能有一个UID,多个应用可以使用sharedUserId 方式共享同一个UID,但前提是这些应用的签名要相同。恶意应用也无法伪造UID,导致无法实现攻击。