安卓任务劫持漏洞StrandHogg复现和分析

0x01.这个漏洞仅有学习目的,无任何实用价值



    近期(其实也是半年多前了),挪威一家安全公司(Promon)披露了一个 Android 任务栈劫持漏洞,并使用描述维京海盗突袭战术中的单词 StrandHogg 对其命名。该漏洞在捷克共和国内攻击者已利用其盗走多家银行用户的卡内余额(这包括 2017 年发现的 BankBot 银行木马变种)从而引发东欧金融机构安全服务商的多方求助。虽然 Google 已采取缓解措施暂停了受影响应用程序的安装,但到目前为止任何版本的 Android(包括 Android10)都还没有修复该漏洞。

    虽然这个漏洞的利用面和攻击面很广,但根据我的研究在现在实际的利用场景下其实危害并不大(也可能是我的技术不到家)。

0x02.漏洞原理剖析



    安卓app有四大组件,承担和用户交互的组件就是Activity。一个app可能有一个或者多个Activity。

    在我的理解里,这个漏洞发生的基本条件已经具备其一:那就是一个app有多个Activity或者称作任务的时候,安卓系统需要一个结构来对多个Act实施管理,这个结构就是任务栈,任务栈类似于传统的栈结构,先入后出(FILO),只有存在于栈顶的Activity才能和用户交互。这就需要设计一套规则来启动不位于栈顶的Activity。

这一套规则就是Activity的四种启动模式:

Standard 模式:

    又称为标准模式,也是系统的默认模式(可以不指定),在这样模式下,每启动一个Activity都会重新创建一个Activity的新实例,并且将其加入任务栈中,而且完全不会去考虑这个实例是否已存在。我们通过图解来更清晰地了解Standard模式


Standard

singleTop 模式:

    又称栈顶复用模式,顾名思义,在这种模式下,如果有新的Activity已经存在任务栈的栈顶,那么此Activity就不会被重新创建新实例,而是复用已存在任务栈栈顶的Activity。这里重点是位于栈顶,才会被复用。


singleTop 

singleTask 模式:

    又称为栈内复用模式。这是一种单例模式,与singTop点类似,只不过singTop是检测栈顶元素是否有需要启动的Activity,而singTask则是检测整个栈中是否存在当前需要启动的Activity,如果存在就直接将该Activity置于栈顶,并将该Activity以上的Activity都从任务栈中移出销毁。


singleTask 模式


singleInstance 模式:

    在singleInstance模式下,该Activity在整个android系统内存中有且只有一个实例,而且该实例单独尊享一个Task。


这个漏洞发生的基本条件其二也已经具备,就是安卓系统为了自身的高效和可扩展性(等等等等),允许一个App调用其他App的Activity,换句话说就是任务是“可迁移的”,能从一个任务栈中移动到另一个任务栈中。并且安卓系统直接为我们提供了简单快捷的接入方式:

android:taskAffinity

每个Activity都有taskAffinity属性,这个属性指出了它进入的Task,默认值为本APP的包名(任务栈名)

android:allowTaskReparenting

这个属性用来标记一个Activity实例在当前应用退居后台后,是否能从启动它的那个task移动到有共同affinity的task,“true”表示可以移动,“false”表示它必须呆在当前应用的task中,默认值为false。     

0x03.攻击实例



首先编写受攻击的App,只有一个空的activity


target

然后编写AttackApp



Attack.MainActivity
Attack.Attacked

还有一个拿来假装无害软件欺骗受害者的Activity,这里就不放图了。

通过代码可以看见,如何利用漏洞完成这次钓鱼攻击?

首先在最近一次点开target app之前Attack app需要被唤起,当Attack app被创建时会创建两个任务栈,一个是他本身的任务栈,该任务栈中存放的是假装无害软件的Activity。另一个是以target app的包名为任务栈名的任务栈,伪装的攻击Activity入栈,这样攻击就部署好了,用户再次点击target,与他交互的就是伪装的攻击Activity了。我们具体来看看:

首先打开target.app,图上面有

现在我们来看看任务栈的情况


很明显target.Main位于任务栈顶,处于活动状态

这时关闭或者把target切换到后台,打开Attackapp


看起来确实非常的无害

但是我们查看一下任务栈



为什么栈名明明是target,里面的Act确实伪装的攻击者呢?

这个漏洞就这么被触发了,用户再次点击target


实现了一个简单的钓鱼

0x04.原理探究



Android源码中对launchMode(启动模式)的处理主要位于ActivityStack.cpp文件的startActivityUnchecked-Locked函数中,有兴趣的朋友可以自己去查看源码。

当启动方式为SingleTask时,会直接对该栈栈顶执行重启动请求,所以再次执行的时候target的MainActivity的并没有进入到栈中,这也可以在demo中插入Log.e("test","target pause")等来观察Activity的生命周期

0x05.攻击面和防护探究



攻击面:

因为这个漏洞是安卓系统本身的先天性设计缺陷,甚至可以被视作为了可扩展性和高复用性作出的一种妥协,所以该漏洞的攻击面特别的大。

目前已经实现的有伪装钓鱼窃取用户名密码等;锁机实现勒索;启动后在后台索引database数据库文件,如果攻击者手中有相应的数据库解密算法,即可窃取本地db文件中大量的用户信息;攻击系统Activity如电话,短信等,窃取用户隐私。本例中实现了一个简单的钓鱼demo设计,目前正在研究第三方支付SDK是否会受到攻击(WX支付)

防护:

如果目标文件中android:taskAffinity属性为空 即 android:taskAffinity=''即可避免攻击

在漏洞的实际应用研究中我发现,当软件的第三方支付SDK如Alipay,openpay均是通过全局广播唤起第三方APP进行支付的,唤起的APP Activity属于唤起者的任务栈中而不是创建属于自己的任务栈,所以通过攻击第三方APP来进行中继攻击的技术实习也是比较难的,目前我正在研究通过进程注入的一些方法。

0x06.这个漏洞没什么利用价值,需要一个什么都不懂的人乱点一些链接,攻击APP才能装到别人手机上,这概率就很小了,就只有点学习价值