muhammet-mucahit/Security-Exercises
GitHub: muhammet-mucahit/Security-Exercises
一份面向初学者的64位Linux缓冲区溢出漏洞利用教程,涵盖从基础原理到Shellcode注入的完整实践案例。
Stars: 47 | Forks: 15
[使用 gets 函数的缓冲区溢出]
Ubuntu 16.04,64 位操作系统
# 概述
## 什么是缓冲区溢出?为什么它是个问题?
简而言之,缓冲区溢出是一种异常现象,即程序在向缓冲区写入数据时,超出了缓冲区的边界。也就是说,缓冲区是以固定大小创建的,所以如果我们传递的数据超过了缓冲区可以存储的量,缓冲区就会溢出。当缓冲区溢出时,程序可以访问属于其他程序的内存其他部分。正如你所想的那样,这可能会导致非常严重的问题。
通常情况下,我们不应该有权限访问内存的其他部分,但在某些情况下这是可能发生的。这样我们就会面对一个有漏洞的程序,而且在这个程序面前没有任何东西是安全的。这就是缓冲区溢出!
## 我们会在哪里遇到缓冲区溢出?
我们可能会在 C/C++ 技术中遇到缓冲区溢出漏洞,因为这些技术没有针对访问或覆盖内存中任何部分的数据(关于缓冲区限制)提供内置保护,并且包含一些易受攻击的函数。但是,在允许直接操作内存的任何编程环境中,缓冲区溢出也可能存在。
除此之外,如果使用这些技术编程的程序员在使用易受攻击的函数时没有控制缓冲区的限制,那么在安全性方面将会出现大问题。正如你从项目标题中理解的那样,在这个项目中,我们将研究 C 语言的易受攻击函数之一 —— gets 函数。
## 缓冲区溢出的危险程度有多大?
这完全取决于攻击者的想象力。事实上,攻击者可以做任何想做的事,比如删除文件、窃取信息、利用这台电脑进行其他攻击。
## 更好地理解缓冲区溢出的要点
为了更好地理解缓冲区溢出,我们需要知道内存是如何运行的。这就是为什么我们将深入探讨内存和一些寄存器。顺便说一句,我们试图在 Stack(栈)中探索缓冲区溢出,但它也可能存在于 Heap(堆)中。我们只会简单介绍内存的其他部分,然后主要讨论 Stack。我们可以在下一页看到整个内存结构...
**运行进程的内存**
 来源:
[*[http://i.stack.imgur.com/1Yz9K.gif]{.underline}*](http://i.stack.imgur.com/1Yz9K.gif)
1. **Stack**: 这是存储所有函数参数、返回地址和函数局部变量的地方。它是一种 LIFO(后进先出)结构。随着新函数调用的进行,它在内存中向下增长(从高地址空间到低地址空间)。稍后我们将更详细地研究 Stack。
2. **Heap**: 所有动态分配的内存都驻留在这里。每当我们使用 malloc 动态获取内存时,都是从 Heap 分配的。随着需要越来越多的内存,Heap 在内存中向上增长(从低内存地址到高内存地址)。
3. **命令行参数和环境变量**: 在运行前传递给程序的参数和环境变量存储在这个区域。
4. **未初始化数据(Bss Segment)**: 所有未初始化的数据都存储在这里。这包括所有未被程序员初始化的全局变量和静态变量。内核默认将它们初始化为算术 0。
5. **已初始化数据(Data Segment)**: 所有已初始化的数据都存储在这里。这包括所有被程序员初始化的全局变量和静态变量。
6. **Text**: 这是存储可执行代码的区域。加载器从这里加载指令并执行它们。它通常是只读的。
**一些重要的寄存器**
1. **%rip**: **指令指针寄存器**。它存储下一条要执行的指令的地址。每执行一条指令后,其值会根据指令的大小而增加。
2. **%rsp**: **栈指针寄存器**。它存储 Stack 顶部的地址。这是 Stack 上最后一个元素的地址。Stack 在内存中向下增长(从高地址值到低地址值)。所以 %rsp 指向 Stack 中最低内存地址的值。
3. **%rbp**: **基指针寄存器**。%rbp 寄存器通常在函数开始时设置为 %rsp。这样做是为了记录函数参数和局部变量。局部变量通过从 %rbp 减去偏移量来访问,而函数参数通过向其加上偏移量来访问,正如你将在下一节中看到的那样。
**函数调用期间的内存管理**

假设我们的 **%rip** 指向 **main** 中的 **func** 调用。将采取以下步骤:
1. 发现一个函数调用,从右到左(以相反的顺序)将参数压入 Stack。所以 **2** 将先被压入,然后是 **1**。
2. 我们需要知道 **func** 完成后返回到哪里,所以将下一条指令的地址压入 Stack。
3. 找到 **func** 的地址并将 **%rip** 设置为该值。控制权已转移给 **func()**。
4. 由于我们在一个新函数中,我们需要更新 **%rbp**。在更新之前,我们将其保存在 Stack 上,以便稍后可以返回到 **main**。所以 **%rbp** 被压入 Stack。
5. 将 **%rbp** 设置为等于 **%rsp**。**%rbp** 现在指向当前的栈指针。
6. 将局部变量压入 Stack/在 Stack 上为它们保留空间。**%rsp** 将在此步骤中更改。
7. **func** 结束后,我们需要重置之前的栈帧。所以将 **%rsp** 设置回 **%rbp**。然后从 Stack 中弹出之前的 **%rbp**,将其存回 **%rbp**。这样基指针寄存器就指回了它在 **main** 中指向的位置。
8. 从 Stack 中弹出返回地址并将其设置为 **%rip**。控制流返回到 **main**,就在 **func** 函数调用之后。
这就是在 **func** 中时 Stack 的样子。

# 利用漏洞

这是我们的代码!如你所见,它包含一个未调用的函数。那就是 **secretFunction()**。我们将尝试执行它,即使它没有被调用。
--------------------------------------------------------------------------
首先,我们必须禁用操作系统的一些保护机制才能进行利用。我们稍后会讨论这些机制。
--------------------------------------------------------------------------
- **sudo su**
- **echo 0 \> /proc/sys/kernel/randomize\_va\_space 这将禁用 ASLR**
--------------------------------------------------------------------------
当我们编译代码时,我们应该使用这个命令:
- **gcc --fno-stack-protector --z execstack --mpreferred-stack-boundary=4 --o bufferoverflow bufferoverflow.c**
来禁用栈保护。
现在我们编译了没有保护的代码。也就是说,我们将能够利用缓冲区溢出漏洞。
--------------------------------------------------------------------------
--------------------------------------------------------------------------
- **objdump --d vulnerable**
使用此命令,我们将获得有关程序的一些信息。
此命令的结果:

正如你在上面看到的,**secretFunction 的地址** 是 **00000000004005b6**。
我们将溢出我们的缓冲区直到 **Base Pointer**,然后将这个 **secretFunction** 的地址放到 **Instruction Pointer** 以调用 **secretFunction**。
为 **vuln** 函数的局部变量保留了 **190(十六进制)或 400(十进制)** 字节。如果你还记得,我们在代码中也请求了 **400** 字节。

**400** 是我们的缓冲区大小。
我们的寄存器是 8 字节的,因为我们的系统是 64 位的。
--------------------------------------------------------------------------
--------------------------------------------------------------------------
- **nano payloadGenerator.py**
在此命令之后,我们将编写下面的代码并保存/关闭它。

这是我们的 payload 生成器。我们写入 'a' 字符 408 次,因为我们的缓冲区大小是 400,而且我们的 rbp(基指针)大小是 8。之后我们将访问 rip(指令指针),并在那里写入我们的 secretFunction 的地址。这样即使它没被调用,我们也能访问它。
--------------------------------------------------------------------------
- **./vulnerable \< textFile**
当我们使用我们的 payload 运行代码时,我们看到了这个结果:

看到了吗!我们利用了漏洞并访问了 secretFunction。
--------------------------------------------------------------------------
**第二个例子 (SHELL\_CODE -- 删除文件)**
--------------------------------------------------------------------------
我们利用了相同的代码并运行了一个 shell 脚本,该脚本删除了一个名为 dummy 的文件。

--------------------------------------------------------------------------

这是我们的 payload 生成器。你也可以在这里看到 shellcode。
--------------------------------------------------------------------------
--------------------------------------------------------------------------

我们使用它并将我们的 payload 写入 **textFileShell**。
--------------------------------------------------------------------------
现在你可以看到名为 dummy 的文件。我们现在将删除它。

--------------------------------------------------------------------------
我们运行了代码,现在让我们看看文件。

--------------------------------------------------------------------------
dummy 文件已被删除。

--------------------------------------------------------------------------
**第三个例子 (SHELL\_CODE -- TCP 连接)**
--------------------------------------------------------------------------
我们利用了相同的代码并运行了一个 shell 脚本,该脚本删除了一个名为 dummy 的文件。

--------------------------------------------------------------------------

这是我们的 payload 生成器。你也可以在这里看到 shellcode。
--------------------------------------------------------------------------
--------------------------------------------------------------------------

我们使用它并将我们的 payload 写入 **textFileShell**。
--------------------------------------------------------------------------
当我们运行代码时,它通过我们的 shellcode 监听了 TCP 端口 4444。


--------------------------------------------------------------------------
正如你所见,当我们 netcat 我们的监听端口时,我们可以访问所有内容。我们可以像下面的截图那样使用 **ls** 查看文件。我们可以删除文件。我们可以做任何事。

--------------------------------------------------------------------------
# 保护

我们使用 fgets() 函数来保护自己免受缓冲区溢出的侵害。这只是一个例子。我们可以使用多种方法进行保护。
# 来源
[[https://www.youtube.com/watch?v=1S0aBV-Waeo]{.underline}](https://www.youtube.com/watch?v=1S0aBV-Waeo)
[[http://www.therabb1thole.co.uk/tutorial/linux-64-bit-buffer-overflow-tutorial/]{.underline}](http://www.therabb1thole.co.uk/tutorial/linux-64-bit-buffer-overflow-tutorial/)
[[https://dl.packetstormsecurity.net/papers/attack/64bit-overflow.pdf]{.underline}](https://dl.packetstormsecurity.net/papers/attack/64bit-overflow.pdf)
[[https://stackoverflow.com/questions/15533889/buffer-overflows-on-64-bit]{.underline}](https://stackoverflow.com/questions/15533889/buffer-overflows-on-64-bit)
[[https://dhavalkapil.com/blogs/Buffer-Overflow-Exploit/]{.underline}](https://dhavalkapil.com/blogs/Buffer-Overflow-Exploit/)
[[https://bytesoverbombs.io/exploiting-a-64-bit-buffer-overflow-469e8b500f10]{.underline}](https://bytesoverbombs.io/exploiting-a-64-bit-buffer-overflow-469e8b500f10)
[[https://samsclass.info/127/proj/p13-64bo.htm]{.underline}](https://samsclass.info/127/proj/p13-64bo.htm)
[[https://blog.techorganic.com/2015/04/10/64-bit-linux-stack-smashing-tutorial-part-1/]{.underline}](https://blog.techorganic.com/2015/04/10/64-bit-linux-stack-smashing-tutorial-part-1/)
标签:64位系统, Exploit开发, Gets函数, meg, Web报告查看器, 二进制安全, 信息安全, 内存安全, 子域名枚举, 攻击示例, 栈溢出, 漏洞分析, 系统安全, 缓冲区溢出, 路径探测, 逆向工具