BUUCTF_gwctf_2019_shellcode
通过这道题的学习与收获有:
1、strlen函数是可以被00给截断的,而shellcode本身执行的时候并不会因为00截断。
2、第一次手写open,read,write函数的汇编
3、push一个字符串的话,比如push 0x67616c66 (这个是flag),不足八字节,push的时候会自动填充00补全八字节,从而占满一个内存单元。
保护策略:
发现没开NX,那基本就是shellcode没跑了。
然后发现开启了沙箱,禁用了execve函数,那就考虑写一个orw的shellcode
程序分析:
由于这个main函数里面存在一个这个汇编指令,因此不能生成伪代码,那就只能读汇编了,好在程序也不复杂。
逻辑就是执行is_printable之后,去将eax与自身相与,如果eax的值为1,test执行之后的运算结果为1(标志寄存器的值为0,否则反之)如果标志寄存器的值为1,则jz指令进行跳转,跳转到loc_AC1函数,如果触发了该函数则程序直接结束,并不会触发call rax的指令,如果jz不进行跳转,则执行call rax(执行完lea之后,rax的值存放的就是read函数输入进去的内容,因此我们输入的时候直接布置shellcode即可)。
大致思路:
因此我们要触发call rax,就需要让loc_AC1函数的返回值为0。
而这个函数返回值为0的前提就是输入的内容ascii码必须要大于31,并且不能等于127。因为我们构造的shellcode经常会存在不可见字符,因此这里我起初考虑的是将写的shellcode转换成可见字符。
然后转换成可见字符发现,这个shellcode太长了。(下面是转换成可见字符之后的shellcode)
然后到这里就卡住了,参考了另一篇师傅的博客,发现strlen函数是可以被00截断的(我自己试了一下发现确实如此)
也就是说只要让shellcode中出现00,并且在00之前的是可见字符就ok了,因为strlen获取的长度就到00这里。
这个循环就不会再往后跑了,因此它不会对00后面的内容进行检查。在这里要说一下,shellcode本身执行的话并不会被00截断,因为shellcode本身毕竟就是一堆机器码而已,CPU执行机器码的时候,才不管你什么00截断不截断呢,机器码是什么它就执行什么。真正会因为00而截断shellcode的其实是一些函数,比如strcpy这个函数。
因此我们只需要让shellcode中尽早的出现00机器码即可
然后就是开始手动编写shellcode了。
手写orw-shellcode
首先我们要执行的如下的代码:
open(flag_addr,0) |
接下来,就开始用汇编来实现上面的内容。
open(flag_addr,0) |
EXP:
最后脚本的话有一点要注意一下。
这个地方有一个指令,它将把我们输入的payload的最后一字节改成0。(如下图)
这样的后果就是将我们的shellcode最后一个syscall给破坏了,因此我们在syscall后面随便再写个指令,syscall就是完整的了。
最后exp:
from pwn import * |