一个整数+1引发的灾难

热门 来源:IT之家 2022-10-05 14:58 阅读量:5946   

这个故事是基于Linux内核的真实漏洞。

一个整数+1引发的灾难

帝国危机

夜幕降临,喧嚣声渐渐远去,忙碌的Linux帝国渐渐平静下来谁也没有想到,一场改变帝国命运的风暴正悄然来临

敲门!帝国安全部长办公室的敲门声打破了夜晚的宁静。

部长,我刚刚发现一个线程正在修改passwd文件,原来是文件系统部的小黑来访。

有什么大惊小怪的只要有root权限,这都是允许的!安全部长没有抬头,继续看每日系统日志

部长,重点是这个线程不是从系统调用进入内核的,而是从中断门户进入的

安全部长停顿了一下,大约0.2ms后,放下手中的日志,站了起来。

"你是说,他是通过中断描述符表进来的。"

小黑点了点头。

小王,你应该和他一起去IDT,调查清楚后尽快向我汇报,部长对一旁的助手说。

小王点点头,准备出发,刚走到门口,就被部长叫住了。

等等!这是一件严肃的事情我还是自己去吧

IDT修订神秘

安全部长立即出发,来到IDT所在的地方这里的一切都和以前一样,没有什么不同

大臣指着这一排门问道:他是从哪个门进来的。

4号,这时,守卫IDT大门的白发老人走过来回答。

奇怪,IDT表中的功能条目都是由帝国安排的按理说他们谁也不会修改passwd文件部长看着这些条目,向自己低下了头

部长,我必须向您汇报这件事那小子进来之前把第4项入口地址的高32位改成了0x00000000,进来之后才恢复成0xFFFFFFFF

听到这里,牧师猛地抬起头来"高32位变成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,然后露出失望的表情不,9号线有检查你看,8个多小时就检验不合格了

线索在这里被切断了我曾希望在perf_swevent_init函数中寻找IDT被修改的奥秘,但似乎将一无所获

不知不觉已经很晚了,部长一行决定先回去,再从长计议。

大臣走了几步,见小王没追上来,又把他叫了回来。

部长,请留步,我好像感觉不对劲小王此刻皱起了眉头

你发现了什么,部长和小黑他们又往回走了

部长,看3号线这个event_id是一个int变量,也就是说它是一个有符号数小王说

签的号怎么了,小黑忍不住问

"如果"

如果event_id变为负数,它将能够越界访问数组,并且还将通过第9行的大小检查没等王说完,部长就揭开了谜底!

他们再一次关注了这个event_id,打算看一看第三行分配给它的event—gt,attr.config是谁。

首先是perf_event中的attr成员变量:

struct perf _ event//struct perf _ event _ attr attr,//,

后跟perf_event_attr中的配置成员变量:

struct perf _ event _ attr//_ _ u64 config,//,

最后,部长和小王喘了一口气这个配置结果是一个64位无符号整数

见大家都不说话,小黑挠了挠头,弱弱地问:怎么了你为什么不说话有什么问题

小王把小黑拉到一边问题大了你觉得如果我给event_id赋一个值为0xFFFFFFFF的config,event_id会变成什么样

负,负,负1。

没错,有符号数的最高位是用来标记正负的如果这个config的最高位是1,那么后面的位是经过精心设计的,不仅可以在那里骗过第9行的验证,还可以对某个位置的数字进行原子+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函数。

第二步:利用Imperial Kernel的漏洞,将64位无符号数赋给int变量,导致变量溢出为负数。

第三步:使用溢出event_id越界访问perf_swevent_enabled,指向IDT入口,atom+1第四个中断处理函数的高32位。

第四步:修改后的中断处理函数指向用户空间,恶意代码事先安排在这里。

第五步:应用层执行int 4汇编指令,触发第4次中断,线程将以最高权限进入内核空间执行事先安排好的恶意代码。

最后,事情水落石出了安全部长回去后上报帝国总部,修复了这个漏洞,将event_id的类型从int修改为u64

即便如此,部长的心情也没有轻松多少未知的敌人闯入了帝国他们是谁你做了什么你现在藏在哪里一个又一个问题不断在我脑海中闪现

待续

郑重声明:此文内容为本网站转载企业宣传资讯,目的在于传播更多信息,与本站立场无关。仅供读者参考,并请自行核实相关内容。