sctf - pwn200(总结版)

sctf - pwn200(总结版)用ida打开文件,查看代码

这道题思路:首先输出一个字符串,溢出改写nbytes,并且通过判断条件然后输入v1(长度为nbytes),利用写入v1来覆盖栈区域,导致在整个函数返回的时候,调用被改写的栈的内容(填入想要跳转的地址)来进行返回,就可以控制整个程序的流程了因为程序开启了ASRL,地址是随机的所以我们需要在程序运行过程中得到system的地址来得到shell——虽然开启了ASRL,但是程序中函数的相对偏移是不变的——可以先进行地址泄露,然后根据相对偏移得到system的地址对于整个rop链来说,我们将return的地址先转到write,打印程序中存在的一个函数的地址,根据事先得到的偏移量可以计算system的地址,其中write的输出用io.recv()来截获。然后再跳转到read函数将plt表中的strlen的地址改成system的地址,并将返回地址改成整个原函数的入口地址(再进行一次),这个时候,在input name的时候,输入/bin/sh,就可以在比较strlen的时候(strlen实际上是system函数),传入参数/bin/sh,最终得到shell
然后再来分析一下payload
payload1:'syclover'+'\x00'+'\xff'*8因为要满足判断的条件,而且17个字符,在第十七个字符的位置进行覆盖nbytes其中‘\x00’是为了绕过strlen的判断(strlen以ASCII码0作为终止标志)

payload2:sctf - pwn200(总结版)
因为v1的位置是bp-9ch——所以起码要填入9c个字节才能覆盖到栈顶,(在这个过程中注意会再次覆盖nbytes,因为后面的write要用到nbytes,所以nbytes的值不要太大,不然可能会要读取不可读的段,引起错误)然后就是正式的溢出了:因为在返回地址和数据之间还有一个ebp的空间,需要覆盖才能将返回地址的位置改成我们想要的——这个ebp的位置没有什么具体的作用,不过不要定的太离谱就好之后我们需要跳转到write函数,泄露程序当前运行时的部分地址,所以将got_plt表中指向write函数的表项的地址当做参数传入,(因为plt表中的表项的值才是泄露的地址,不是plt表项的位置,而plt表项中的值就是got表的表项地址)number1,number4是write函数的另外两个参数在传入参数之前,我们要将write函数执行完毕之后的返回地址压栈,因为我们是模拟的整个write函数的调用过程,我们需要在write泄露地址之后将整个函数的调用结束———一般的函数结束之后要:pop;pop;pop;ret——我们在程序中找到这个连续指令的地址(pop3_addr),传入(因为我们之前压入了ebp的值,为了保险起见可以pop4次,把ebppop出来,其实不pop好像也没关系),那么在函数返回的时候,PC会指向四个包含连续pop指令的地址,所以在write指向之后会指向4个连续的pop指令第四个pop会把栈的内容给ebp地址空间然后再进行第二次溢出:首先我们压入的是plt表中的read的地址,接着是read返回的地址,因为需要在改写strlen地址之后再将整个函数调用一遍,所以压入整个函数的入口地址function,然后number0,number4都是参数,传入got表中的strlen的地址就可以got表中的表项的值,是真正的该函数的入口地址(我们需要将这个值改为system函数的入口地址)
在后面的步骤中,我们首先接受泄露出来的地址值,利用的是io.recv()函数sctf - pwn200(总结版)

然后就可以计算出system的入口地址传入改写
在第二次要求input name的时候输入/bin/sh,在进行判断strlen的时候,实际上就执行了system(nbutys)





PS:
  • 关于函数压栈的ebp——上一个栈帧的地址
          sctf - pwn200(总结版)
  • libc.so
          动态链接库           linux 下有动态库和静态库,动态库以.so为扩展名,静态库以.a为扩展名。     
     按照共享库的命名惯例,每个共享库有三个文件名:real name、soname和linker name。     real name     真正的库文件(而不是符号链接)的名字是real name,包含完整的共享库版本号。例如上面的libcap.so.1.10、libc-2.8.90.so等。     soname     soname是一个符号链接的名字,只包含共享库的主版本号,主版本号一致即可保证库函数的接口一致,因此应用程序的.dynamic段只记录共享库的soname,只要soname一致,这个共享库就可以用。例如上面的libcap.so.1和libcap.so.2是两个主版本号不同的libcap,有些应用程序依赖于libcap.so.1,有些应用程序依赖于libcap.so.2,但对于依赖libcap.so.1的应用程序来说,真正的库文件不管是libcap.so.1.10还是libcap.so.1.11都可以用,所以使用共享库可以很方便地升级库文件而不需要重新编译应用程序,这是静态库所没有的优点。注意libc的版本编号有一点特殊,libc-2.8.90.so的主版本号是6而不是2或2.8。     linker name     linker name仅在编译链接时使用,gcc的-L选项应该指定linker name所在的目录。有的linker name是库文件的一个符号链接,有的linker name是一段链接脚本。例如上面的libc.so就是一个linker name,它是一段链接脚本
链接库好处

  1. 使用共享库可以大大节省内存。比如libc,系统中几乎所有的进程都映射libc到自己的进程地址空间,而libc的只读部分在物理内存中只需要存在一份,就可以被所有进程共享,这就是“共享库”这个名称的由来了。
  2. 使用共享库可以很方便地升级库文件而不需要重新编译应用程序。

  • got和plt表
sctf - pwn200(总结版)

     函数名就是一个内存地址,这个地址指向函数的入口。调用函数就是压入参数,保存返回地址,然后跳转到函数名指向的代码。问题是,如果函数在共享库中,共享库加载的地址本身就不确定,函数地址也就不确定了,那如何调用共享库中的函数呢?     ELF文件的动态连接机制。每一个外部定义的符号在全局偏移表 (Global Offset Table GOT)中有相应的条目,如果符号是函数则在过程连接表(Procedure Linkage Table PLT)中也有相应的条目,且一个PLT条目对应一个GOT条目。

附件列表

     

    上一篇:浅谈WebSocket协议、WS协议和WSS协议原理及关系


    下一篇:基于未知情况的WSS失效异常数据预测判断方法