这里总结一下shellcode的各种类型
一、直接调用
1 |
|
直接执行shellcode,考查对shellcode的编写能力,pwntool可以直接生成shellcraft.sh()
,没什么难度
二、禁用了system
参考pwnable.tw的orw,这种不能直接get shell,但是可以编写shellcode实现fp = open("flag") ,read(fp,buf,0x30),write(1,buf,0x30)
来读取flag
1 | #32位 |
三、限制字符
像这样的
1 | // gcc -m64 -z execstack -fPIE -pie -z now chall3.c -o chall3 |
限制了shellcode为可打印字符,也就是说现在的shellcode中不能出现不可见字符,那么能用的汇编语句就大大减少了,如32位的int 0x80
,64位的syscall
都不能直接输入,那怎么办呢,参考大牛的总结,此类题目可用到的汇编指令如下 :
1 | 1.数据传送: |
所以考查的是我们用上面有限的汇编指令编写出可用的shellcode,基本思想:mov a,b 用 push b;pop a替换;
而像int 0x80 ; syscall
这种则通过xor sub and inc dec
运算来操作shellcode使之变成我们要的指令;
参数题目pwnable.tw的death_note
具体wp
不过还是有工具可以生成可打印shellcode
x86可以msf内置的encoder,x64用github上的shellcode_encoder
但是个人觉得,,工具有点局限,并不是万能的
四、字符限制范围更小
上面的字符限制还是可见字符,但是还可以继续限制到[A-Z],[a-z],[0-9]
也就是字母和数字
像这样
1 | // gcc -m32 -z execstack -fPIE -pie -z now chall2.c -o chall2 |
这是中科大校赛上的一题,同样可以用msf生成符合的shellcode
exp:
1 | from pwn import * |
五、禁用了system和open
这种情况在2018-XNUCA-steak
中出现,具体程序漏洞的分析可以参考看雪上面大佬的:https://bbs.pediy.com/thread-250635.htm
https://bbs.pediy.com/thread-249556.htm
这里主要介绍在shellcode的编写:其主要思想就是通过调用32位的open来绕过,因为程序只是对64位的代码做限制,而通过写32位的shellcode能到达到open的目的,以32位的模式运行。
(骚操作,通过retfq切换模式),下面会以一道倒是来详细分析这种做法。
六、禁用了system和open,还限制了shellcode字符
这种情况可以说是我目前见到的最恶心的shellcode了,这就是来自ex师傅的shellcode题目
接下来详细分析一下这道题
1 | $ seccomp-tools dump ./shellcode |
查看一下沙箱发现,只允许6个函数,但是没有open,不过有mmap,并不知道有什么用,先放着
IDA看一下程序
1 | for ( i = 0; i < v4; ++i ) |
这里对输入进行检测,只能在可见字符范围
所以,我们要用这有限的输入,有限的函数cat flag
在这里我们要先知道,程序是怎么知道要以64位模式运行还是以32位模式运行的;寄存器中有一个cs寄存器,cs = 0x23代表32位模式,cs = 0x33代表64位模式,而cs寄存器就是通过上面提到的retfq
汇编指令来修改,具体怎么修改?
retfq有两步操作,ret以及set cs
,所以执行retfq会跳转到rsp同时将cs设置为[rsp+0x8],我们只需要事先在ret位置写入32位的shellcode就可以执行了,但是这里有一点需要注意的是,retfq跳转过去的时候程序已经切换成了32位模式,所以地址解析也是以32位的规则来的,所以原先的rsp = 0x7ffe530d01b8
会被解析成esp = 0x530d01b8
所以在跳转过去后要先平衡好esp的地址,不能直接执行push ...
还有就是这个返回地址0x40404040
怎么来的,这就用到了mmap
函数了,因为shellcode是写到栈上面的,如果把32位的shellcode在栈上的话,因为64位的栈地址长度比32位的长,所以32位模式下是无法解析出64位的栈地址的,retfq时就会crash掉,所以这里需要先调用mmap申请出一段适合32位的地址来存32位shellcode,mmap(0x40404040,0x7e,7,34,0,0)
走到这一步这道题基本完成了,我一开始的想法是直接调用32位下的read,write把flag打印出来,但是发现是bad system call,无法调用,所以还得回到64位模式下调用,再调用一次retfq
这里需要先把open的返回值保存到别的寄存器,因为在retfq回64位模式的时候会影响到rax
最后就read,write打印出来就OK啦!
整体思路:
1 | 1、用可见字符编写shellcode 调用mmap申请地址,调用read读入32位shellcode |
exp:
1 | #coding:utf-8 |
最后ex师傅牛逼!