这个故事是基于Linux内核的真实漏洞。
帝国危机
夜幕降临,喧闹声渐渐淡去,忙碌的Linux帝国渐渐平静下来谁也没有想到,一场危机正悄然降临
敲门!帝国安全部长办公室的敲门声打破了夜晚的宁静。
部长,刚刚发现有一个程序在修改passwd文件,原来是文件系统部的小黑来访。
安全部长双眉紧锁此passwd文件不常见它记录了系统中所有用户的信息,但0.1ms后,紧锁的眉头舒展开了
有什么大惊小怪的只要有root权限,这都是允许的!,安全部长没有抬头,继续看每日系统日志
部长,重点是这个程序不是从系统调用进入内核的,而是从中断门户进入的
安全部长停顿了一下,大约0.2ms后,放下手中的日志,站了起来。
"你是说,他是通过中断描述符表进来的。"
小黑点了点头。
小王,你应该和他一起去IDT,调查清楚后尽快向我汇报,部长对一旁的助手说。
助手点点头,准备出发,刚走到门口,就被部长叫住了。
等等!这是一件严肃的事情我还是自己去吧
IDT修订神秘
安全部长立即出发,来到IDT所在的地方这里的一切都和以前一样,没有什么不同
牧师指着中间的描述符表问道:他是从哪个门进来的。
4号,这时,守卫IDT大门的白发老人走过来回答。
奇怪,IDT表中的函数条目都是由我们的操作系统安排的他们谁也不会修改passwd文件,这是合理的部长看着这些条目,向自己低下了头
部长,我必须向您汇报这件事那小子进来之前把第4项入口地址的高32位改成了0x00000000,进来之后才恢复成0xFFFFFFFF
听到这里,牧师猛地抬起头来这个入口地址是64位的,它被分成三个部分用于存储IDT条目高32位通常是0xFFFFFFFF,指向我们内核空间的中断处理函数现在是0x00000000,那么整个函数入口地址不都指向用户态地址空间吗
而小黑的助理不敢说话大家都知道后果有多严重天知道那家伙用内核权限在用户空间执行了什么代码
不,在他进来之前,一个用户空间程序怎么可能改变IDT的内容他没有权限你错了吗
我没有看错他改的时候我还特别注意了他的调用栈,不是在用户空间,而是在内核空间的函数perf _ swevent _ init方向,老人说
整数+1的悲剧
事不宜迟,部长带大家去了perf_swevent_init函数。
老大爷,你还记得具体位置吗,部长问道
"它来自第19行中static_key_slow_inc函数"
让我看看,助手挤到前面,试图在部长面前露一手。
嗯,这个static_key_slow_inc做的就是对一个整数执行atom+1运算但是,它运行perf _ swevent _ enabled数组,该数组不能与IDT结合使用怎么能修改成IDT呢助手摸了摸他的头,后退了两步他看的时候没看出什么问题
不一定!部长还是皱着眉头说:你看,它是用数字event_id作为下标来访问数组元素的。如果这个event_id出界,指向IDT,也不是不可能!
助理很快扫了一眼event_id,然后露出失望的表情不,第九行有一张支票你看,超过8个小时就会检查失败
线索在这里被切断了我曾希望在perf_swevent_init函数中寻找IDT被修改的奥秘,但似乎将一无所获
不知不觉已经很晚了,部长一行决定先回去,再从长计议。
牧师走了几步,但当他看到他的助手没有跟上他时,他把他叫了回来。
部长,请留步,我好像感觉有些不对劲,助理一时皱起了眉头。
你发现了什么,部长和小黑他们又往回走了
部长,看3号线这个event_id是一个int变量,也就是说它是一个有符号数助理说
签的号怎么了,小黑忍不住问
"如果"
如果event_id变为负数,它将能够越界访问数组,并且还将通过第9行的大小检查还没等助理说完,部长就揭开了谜底!
他们再一次关注了这个event_id,打算看一看第三行分配给它的event—gt,attr.config是谁。
首先是perf_event中的attr成员变量:
structperf_event//...structperf _ event _ attrattr//...,
后跟perf_event_attr中的配置成员变量:
struct perf _ event _ attr//_ _ u64 config,//,
最后,部长和助理气喘吁吁地说这个配置结果是一个64位无符号整数
见大家都不说话,小黑挠了挠头,弱弱地问:怎么了你为什么不说话有什么问题
助理布莱基靠边站问题大了你觉得如果我给event_id赋一个值为0xFFFFFFFF的config,event_id会变成什么样
负,负,负1。
没错,有符号数的最高位是用来标记正负的如果这个config的最高位是1,那么后面的位是经过精心设计的,不仅可以在那里骗过第9行的验证,还可以对某个位置的数字进行原子+1运算,助手继续说道
还不错,小王,有进步!不知道什么时候,部长也走了过来,被部长这么表扬,助手们都有点不好意思。
听了半天,不就是过线,在某处数字加1吗有什么大不了的,小黑一脸不屑
助手连连摇头你不要小看这种加1的行为
小黑想知道,像什么。
比如记录中断和异常处理函数的IDT,记录系统调用的sys_call_table,这些表中所有的函数地址都位于帝国内核空间如果这个加1,如果不是别人而是这些表中的函数地址就麻烦了助手继续说道
我明白,但就算加个1,问题应该不大吧。
助理叹了口气,看来你还是不明白。我将以这个修改后的IDT表为例,向你展示表项的格式——中断描述符
IDT中中断/异常处理函数的地址不是一个完整的64位,而是分成几个部分,其中高32位用红色标出。在64位Linux帝国中,内核空间地址的高32位是0xFFFFFFFF,如果
如果用之前event_id数组的下标越界访问,把这个place atom+1,就变成0了,对吧,小黑终于明白了
这个案子已经完全澄清了。
安全部长为他助手精彩的分析鼓掌。好,好,大家都很聪明!现在,我们回来吧!
第一步:精心设计一个config值,从应用层传入内核空间的perf_swevent_init函数。
第二步:利用内核漏洞将64位无符号数赋给int变量,导致变量溢出为负数。
第三步:使用溢出event_id越界访问perf_swevent_enabled,指向IDT入口,atom+1第四个中断处理函数的高32位。
第四步:修改后的中断处理函数指向用户空间,恶意代码事先安排在这里。
第五步:应用层执行int 4汇编指令,触发第4次中断,线程将以最高权限进入内核空间执行事先安排好的恶意代码。
最后,事情水落石出安全部长回去后汇报了问题,修复了漏洞,将event_id的类型从int修改为u64这一次,危机终于解除了
。郑重声明:此文内容为本网站转载企业宣传资讯,目的在于传播更多信息,与本站立场无关。仅供读者参考,并请自行核实相关内容。