BUUCTF-PWN-ciscn_2019_c_1

这道题又是新的题型,研究了以下,要好好记录下来。
首先是看程序开启了哪些保护:

发现没开启栈溢出保护,我们继续往下看程序的逻辑:

一个简洁的页面,到此为止我们并不能看出可以利用哪些攻击方式,我们跟踪一下函数encryptbegin发现begin只是一个简单的显示程序,而重点在于encrypt函数,我们观察其程序内容:

int encrypt()
{
  size_t v0; // rbx
  char s[48]; // [rsp+0h] [rbp-50h] BYREF
  __int16 v3; // [rsp+30h] [rbp-20h]

  memset(s, 0, sizeof(s));
  v3 = 0;
  puts("Input your Plaintext to be encrypted");
  gets(s);
  while ( 1 )
  {
    v0 = (unsigned int)x;
    if ( v0 >= strlen(s) )
      break;
    if ( s[x] <= '`' || s[x] > 122 )
    {
      if ( s[x] <= 64 || s[x] > 90 )
      {
        if ( s[x] > 47 && s[x] <= 57 )
          s[x] ^= 0xFu;
      }
      else
      {
        s[x] ^= 0xEu;
      }
    }
    else
    {
      s[x] ^= 0xDu;
    }
    ++x;
  }
  puts("Ciphertext");
  return puts(s);
}

我们查看字符串,发现并没有看到想要的system\bin\sh\,也就是说我们要自己构造一个ROP链来实现攻击。但是这里有一个问题,这个程序会对输入的s字符串进行加密,也就是说会破坏我们的ROP链,同时我们也可以看到加密的位数是根据字符串的长度进行判断的strlen(s),这里有个小技巧:strlen(s)是通过'\0'的位置来进行对字符长度的判断的。也就是说,我们只要将字符串的首位设置为\0,那么程序逻辑就会判断要加密的程序长度为0,这样就不会对我们构造的ROP链进行破坏,接下来我们就可以利用栈溢出来构造我们的攻击方式了。

那么我们需要思考一下这类题的做法是什么呢?他什么后门函数也没给,即使栈溢出也不知道咋整啊?我们整理步骤如下:

  • 利用一个程序已经执行过的函数去泄露它在程序中的地址,然后取这个地址的末三位,然后取查找他使用的libc版本
  • 程序里的函数地址和它所使用的libc库中的函数地址是不一样的,他们之间存在一个偏移地址。我们通过offset = 函数中的地址 - libc中的函数地址取到这个偏移量
  • 得到偏移量之后之后我们可以通过程序中函数地址 = 函数地址 + offset的方式来找到其他函数在程序中的地址,然后我们就可以去构造rop链了

现在我们首先需要通过已经被执行的puts函数,利用他的plt和got地址来泄露我们的libc版本,我们构造以下程序:

我们得到了puts在程序中的地址的末九位9c0

接下来我们需要拿到它的libc版本,有两种方法:

  • 使用它libc检查的在线网站
  • 使用LibcSearcher库实现
    我们完成下面的libc定位使用,然后构造我们自己的ROP

    这里我们需要注意几个地方:
  • 一是我们获取我们的地址时我们并不知道会收到几条内容,一开始可以全部打印出来,然后收集你需要的信息
  • 二是关于libc的库要多进行几次尝试,如果都不正确就需要考虑是不是哪里出了问题
  • 三是关于构造ROP链时,我们需要在其前面添加一个ret实现栈对齐,关于这个我暂时还没有搞清楚,之后可能会继续加以记载吧
    执行攻击脚本,我们最终可以得到我们想要的构造程序,执行得到:
posted @   Ylin07  阅读(7)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
点击右上角即可分享
微信分享提示