未加星标

【漏洞分析】深入分析TIMA任意内核模块认证绕过漏洞

字体大小 | |
[网络安全 所属分类 网络安全 | 发布者 店小二04 | 时间 | 作者 红领巾 ] 0人收藏点击收藏
【漏洞分析】深入分析TIMA任意内核模块认证绕过漏洞 2017-01-12 14:37:19 来源:bugs.chromium.org 作者:shan66
【漏洞分析】深入分析TIMA任意内核模块认证绕过漏洞

翻译:shan66

预估稿费:140RMB

投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿


前言

为了确保Android设备中linux内核的完整性,三星推出了一个名为“lkmauth”的功能。该功能的最初目的是,确保只有三星核准的那些内核模块才可以加载到Linux内核中。


TIMA任意内核模块认证绕过漏洞分析

每当内核尝试载入内核模块时,系统就会用到“lkmauth”功能。在加载内核模块之前,内核首先会加载“lkmauth”的trustlet,并发送一个请求,来验证模块的完整性。

由于三星设备使用了两个不同的TEE,所以对于每个TEE都单独实现了相应的“lkmauth”功能。

在使用QSEE TEE(它使用了内核配置TIMA_ON_QSEE)的设备上,使用“tima_lkmauth”的trustlet来验证待加载的内核模块的完整性。当然,这个trustlet本身是相当简单的——它提供了一个硬编码的列表,来保存所有“可允许”的内核模块的SHA1哈希值。如果当前待加载的内核模块的SHA1没有出现在硬编码的列表中,那么它就会被拒绝。

对于使用MobiCore TEE(使用内核配置TIMA_ON_MC20)的设备而言,它们会通过“ffffffff00000000000000000000000b.tlbin”trustlet来验证待加载内核模块的完整性。然而,在这种情况下,其流程会稍微有点复杂,下面简单介绍加载模块的具体步骤:

1. [如果trustlet尚未加载]:加载trustlet。 2. [如果已批准的哈希值列表尚未加载]:向trustlet发送请求,以便加载已批准的SHA1哈希签名列表。

3. 将存放内核模块的缓冲区传递给trustlet进行验证。如果该内核模块的SHA1哈希值不在先前加载的已批准哈希值列表中,则会被拒绝。

已经批准的模块的哈希值组成的列表,将作为设备固件的一部分,存储在文件“/system/lkm_sec_info”中。该文件的结构如下所示:

<LIST_OF_APPROVED_SHA1_HASHES>||<RSA-SHA1(LIST_OF_APPROVED_HASHES)>

RSA签名本身会使用PKCS#1 v1.5进行填充,其中BT = 1,PS是0xFF字节的常量字符串。

用于验证签名的公钥,我们可以通过静态分析方法从trustlet中找到。在trustlet的自身代码中,2048位的模数(N)是以反向字节顺序硬编码的形式存在的。经验证,在许多不同的设备和版本(如GT-I9300、SM-P600、SM-G925V等)中,都使用了相同的常量模量。这个模数本身是

23115949866714941391353337177289175219285878274139282906616665210063884406381659531323213685988661310147714551519208211866717752764819593136041821730036424774768373518089158559738346399417711215445691103520271683108620470478217421253901045241463596145712323679479119182170178158376677146612087823704797563128645982031650495998390419939015769566125776929249878666421780560391442439477189264423758971325406632562977618217815844688082799802924540355522191958147326121713251815752299744182840538928330568160188518794896256711464745438125835732128172016078553039694575936536720388879378619731459541542508235684590815108447

这里使用的公钥指数为3。

发送到trustlet的请求缓冲区具有以下结构:

/*Messagetypesforthelkmauthcommand*/ typedefstructlkmauth_hash_s{ uint32_tcmd_id; uint32_thash_buf_start;/*startingaddressofbufforkohashes*/ uint32_thash_buf_len;/*lengthofhashbuf,shouldbemultiplesof20bytes*/ uint8_tko_num;/*totalnumberko*/ }__attribute__((packed))lkmauth_hash_t;

通过对trustlet中处理这个命令的代码进行逆向工程,得到了处理函数高级逻辑代码,具体如下所示:

intload_hash_list(char*hash_buf_start,uint32_thash_buf_len,uint8_tko_num){ //Checkingthesignatureofthehashbuffer,withoutthelengthofthe //publicmodulus(256bytes=2048bits) uint32_thash_list_length=hash_buf_len-256; char*rsa_signature_blob=hash_buf_start+hash_list_length; if(verify_rsa_signature(hash_buf_start,hash_list_length,rsa_signature_blob)) returnSIGNATURE_VERIFICATION_FAILURE; //Copyingintheverifiedhashesintothetrustlet //SHA1hashesare20byteslong(160bits)each //Themaximalnumberofcopiedhashesis0x23 //g_hash_listisalistintheBSSsectionofthetrustlet //g_num_hashesisalsointheBSSsectionofthetrustlet uint8_ti; for(i=0;i<ko_num&&i<0x23;i++){ memcpy(g_hash_list+i*20,hash_buf_start+i*20,20); } g_num_hashes=i; returnSUCCESS; }

问题在于,上述代码包含了一个逻辑缺陷:没有对“ko_num”字段进行相应的验证,以确保其匹配哈希值列表的实际长度。这意味着攻击者能够欺骗trustlet来加载额外的“允许哈希值”,即使它们不是已经签名的blob的一部分。为此,可以在提供与哈希值列表的原始长度匹配的"hash_buf_len"的时候,通过提供一个大于实际哈希值数量的“ko_num”字段来达到这一目的。然后,攻击者可以在缓冲器中的签名blob之后提供任意的SHA1哈希值,从而导致这些额外的哈希值也会被复制到已经批准的可信哈希值列表中。

下面给出此类攻击的一个具体例子:

hash_buf_start=<ORIGINAL_SIGNED_HASH_LIST>|| <RSA-SHA1(ORIGINAL_SIGNED_HASH_LIST)>|| <4GARBAGEBYTES>|| <ATTACKER_CONTROLLED_SHA1_HASH> hash_buf_len=len(<ORIGINAL_SIGNED_HASH_LIST>)+ len(<RSA-SHA1(ORIGINAL_SIGNED_HASH_LIST)>) ko_num=(<ORIGINAL_SIGNED_HASH_LIST>/20)+ceil(256/20)+1

由于“/system/lkm_sec_info”中的原始哈希值列表长度总是很短(例如从来不超过8)的,因此表达式((<ORIGINAL_SIGNED_HASH_LIST> / 20)+ ceil(256/20)+1)的值永远不会大于22。但是它仍然小于0x23(35)个哈希值的硬编码下限,这意味着上面的代码能够正常执行所提供的命令。此后,已批准的哈希值列表将会变成如下所示的结构:

original_approved_hash_1 original_approved_hash_2 ... original_approved_hash_n bytes_00_to_20_of_rsa_signature bytes_20_to_40_of_rsa_signature ... bytes_240_to_256_of_rsa_signature||4_garbage_bytes attacker_controlled_sha1_hash

实际上,这就将攻击者控制的SHA1哈希值插入到了已批准的哈希值列表中,从而成功绕过了签名验证。

该漏洞的一种利用方法是,控制一个可以加载内核模块的进程,然后将感染的哈希值列表请求发送给trustlet。例如,“system_server”进程就具有这种能力,同时还能够加载trustlet,并与之进行通信(我们已经在SM-G925V的默认SELinux策略中进行了相应的验证):

allowsystem_servermobicore-user_device:chr_file{ioctlreadwritegetattrlockappendopen}; allowsystem_servermobicoredaemon:unix_stream_socketconnectto; allowsystem_servermobicore_device:chr_file{ioctlreadwritegetattrlockappendopen};

将受感染的哈希值列表加载到trustlet之后,攻击者就可以尝试加载与刚才插入到列表中的SHA1哈希值相匹配的内核模块了。需要注意的是,加载模块的第一次尝试将会失败,因为内核将尝试加载已批准的哈希值列表本身,但是trustlet将检测到此情况并返回错误代码RET_TL_TIMA_LKMAUTH_HASH_LOADED。这样的话,内核会做一个标记,指出列表已经加载好了——也就是说,下一次加载模块的时候,就不会重新加载这个列表了:

... elseif(krsp->ret==RET_TL_TIMA_LKMAUTH_HASH_LOADED){ pr_info("TIMA:lkmauth--lkm_sec_infoalreadyloaded\n"); ret=RET_LKMAUTH_FAIL; lkm_sec_info_loaded=1; } ...

之后,第二次尝试加载已经感染的模块的时候,就会成功了,因为它的哈希值已经位于已批准的哈希值列表中了。


【漏洞分析】深入分析TIMA任意内核模块认证绕过漏洞
【漏洞分析】深入分析TIMA任意内核模块认证绕过漏洞 本文由 安全客 翻译,转载请注明“转自安全客”,并附上链接。
原文链接:https://bugs.chromium.org/p/project-zero/issues/detail?id=960

本文网络安全相关术语:网络安全工程师 网络信息安全 网络安全技术 网络安全知识

主题: LinuxLinux内核360TIAndroidAUTAUUT三星PPR
分页:12
转载请注明
本文标题:【漏洞分析】深入分析TIMA任意内核模块认证绕过漏洞
本站链接:http://www.codesec.net/view/523081.html
分享请点击:


1.凡CodeSecTeam转载的文章,均出自其它媒体或其他官网介绍,目的在于传递更多的信息,并不代表本站赞同其观点和其真实性负责;
2.转载的文章仅代表原创作者观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,本站对该文以及其中全部或者部分内容、文字的真实性、完整性、及时性,不作出任何保证或承若;
3.如本站转载稿涉及版权等问题,请作者及时联系本站,我们会及时处理。
登录后可拥有收藏文章、关注作者等权限...
技术大类 技术大类 | 网络安全 | 评论(0) | 阅读(28)