翻译自:https://hackernoon.com/binary-exploitation-eli5-part-1-9bc23855a3d8

0x00-前言

在本系列文章中,我将详细介绍不同类型的二进制漏洞,解释它们是什么,它们如何工作,它们背后的技术以及一些防御他们的手段。在本系列文章中,我将尽力用任何人(从初学者到1337 h4x0r(hacker))都能理解的方式解释这些攻击,防御,技术和概念。
请注意:同时我将添加一些关键的必备知识部分,希望对这些攻击做出更多技术性的解释来帮助我们更容易理解,但本系列文章将不会介绍精通二进制漏洞攻击方式这个领域的所有信息/概念/技术。
本文将介绍以下内容:
0×01.必备知识:应用程序内存
0x02.必备知识:堆栈
0x03.必备知识:函数调用和返回
0x04.攻击:堆栈缓冲区溢出
0x05.攻击:return-to-libc(ret2libc)攻击

0x01-必备知识:应用程序内存

执行程序时,应用程序会被加载到内存中,但是计算机内存是有限的,因此,在将内容加载到内存时,必须非常小心它们是否会覆盖其他的应用程序。因此,计算机使用一种名为虚拟内存的概念,可以使用早期的21世纪早期电视节目Drake and Josh的场景来完美地总结,其中Drake和Josh在寿司店的工作是将寿司装到盒子里:
在这个场景中,Drake和Josh的这份工作需要他们将通过传送带传来的寿司装进盒子里。此外,虽然所有寿司盒子看起来完全相同,但至关重要的每个盒子只能包含一种寿司。
那么,让我们分解这个类比并将其与虚拟内存的概念联系起来:
寿司传送带:正如我上面所说,计算机必须非常小心和准确地将应用程序数据放在内存中,以免被覆盖。虽然计算机可以简单地将应用程序小心地放在物理内存中,但这最终会导致问题,因为应用程序片段会迅速填满整个空间。在上面的例子中,可以将各个寿司看作应用程序分配的应用程序片段或内存块,而整个寿司集合(每个盒子装6个寿司)可以看作应用程序本身。
Drake和Josh:为了避免单种的寿司填满传送带的问题,Drake和Josh将它们装进单独的盒子里,然后允许它们沿着传送带向下移动。就像Drake和Josh一样,您的计算机也会将应用程序组织并设置到容器中,叫做虚拟内存位置。这些虚拟内存位置(或虚拟地址空间)让应用程序相信它可以完全控制整个内存范围。但是,当应用程序调用某个位置或尝试在其虚拟地址空间内分配内存而不是被授予对任意物理内存的访问权限时,计算机CPU(中央处理单元)中非常重要的一小块硬件称为MMU (内存管理单元)将应用程序的调用的位置映射到物理内存的特定区域,并帮助进行任何的内存操作。此内存映射允许计算机通过集中的查找表的方式来组织和处理具有动态内存要求的多个应用程序。

同样需要注意的是虽然应用程序的所有代码都包含在其虚拟地址空间中,但应用程序通常使用动态链接库(DLL),例如libc或kernel32。这些DLL只是外部(不存储在应用程序的地址空间中)系统应用程序或从自定义应用程序中导入的。以下面的代码为例:

正如您所看到的,在这个6行程序中,我实际上没有定义printf是什么。但是,这个程序仍然没有问题的输出“Hello World”。 这是因为printf函数是libc中定义的系统函数,它是标准的C库。在编译过程中,libc从外部链接到可执行文件。在Linux系统上,您可以使用ldd命令查看程序的共享库依赖项。

如果您正在看上面的截图并想知道0xb7e99000是什么意思,它是内存中libc库的地址。内存地址以十六进制格式表示。请单击此处以获取有关十六进制的更多信息。

0x02-必备知识:堆栈

堆栈是一个大型数据结构,用于在运行时存储应用程序信息和数据。可以通过以下类比简单地解释堆栈的功能:
鲍勃是一家高档餐厅的洗碗工,每天晚上鲍勃都有一堆盘子要洗。此外,整晚上一旦有桌子被清理,就将有更多的盘子添加到鲍勃的洗碗池里。如果鲍勃从一堆盘子中取出不是顶部的盘子,那么这个盘子以上的盘子都会掉落并且摔坏。
现在不是鲍勃和洗碗池而是一台计算机和一堆数据对象。每当数据压入到堆栈,它就会被添加到堆栈的顶部,无论何时数据弹出堆栈都从堆栈的顶部移出。也就是后进先出(LIFO:Last In First Out)的机制。
程序使用堆栈来保存各种各样的东西,比如函数指针(函数在内存中的位置)和变量。

0x03 - 必备知识:函数调用和返回

看看下面的代码:

在此代码段中,我们看到定义add函数需要2个整型参数A,B。在主函数中,我们调用add函数,将1传入到A,2传入到B。如果我们将这些代码分解为它的底层机器语音的代码,我们会看到:

如您所见,当使用参数调用函数时,程序首先将两个参数都压入堆栈,然后执行调用语句。这个调用语句重定向到程序指令指针(指令指针就像你用来跟踪你正在读取哪个单词的小铅笔。指令指针总是指向即将执行的指令(即将要执行的指令))到被调用函数的地址。但是,在导航到被调用函数之前,call语句将其下面的一条指令的地址推送到堆栈,这样当添加函数返回时,它就会知道从哪里开始继续处理。函数应该返回的位置的地址称为函数返回指针。

0x04 - 攻击:堆栈缓冲区溢出

在深入了解堆栈缓冲区溢出及其工作原理的技术细节之前,让我们先看一下快速,易于理解的类比:
过去爱丽丝和鲍勃经常约会,但爱丽丝最终与鲍勃分手了。随着时间的推移,爱丽丝继续生活,但鲍勃无法从极度的悲伤中走出来。现在,爱丽丝准备嫁给罗伯特哈克曼,他是鲍勃的情敌。鲍勃变成了一个令人毛骨悚然的怪人,他通过秘密访问爱丽丝的电子邮件帐户,监视爱丽丝的所有婚礼计划。鲍勃看到爱丽丝聘请了一位著名的婚礼蛋糕设计师,设计师希望爱丽丝根据自己的口味偏好修改部分食谱。设计师给爱丽丝一个推荐的成分列表,但他说他可以做任何她想吃的。Bob打开了设计师电子邮件附带的文件,发现食谱的自定义行看起来像:
...然后,我们将通过添加______为糖霜添加味道。在那之后,我们将添加一些巧克力....
Bob注意到如果你在行中输入“Banana”,文本将如下所示:
...然后,我们将通过添加香蕉来增加糖霜的味道。在那之后,我们将添加一些巧克力......
但是,如果Bob将“草莓”输入该行,则文本将如下所示:
...然后,我们将通过添加草莓来增加糖霜的味道,我们将添加一些巧克力......
鲍勃意识到这将是破坏爱丽丝婚礼的完美方式,他所要做的就是用他恶心的版本覆盖其余的食谱!在爱丽丝的婚礼当天,设计师揭开他制作的蛋糕发现它是用冷冻蛋黄酱制成并且被虫子覆盖着的!
堆栈缓冲区溢出(就像Bob的攻击一样)会覆盖开发人员不打算覆盖的数据,从而可以完全控制程序及其输出。
那么,现在让我们在现实世界中看到它。看一下来自exploit-exercises.com的以下代码:

在上面的函数中,我们看到创建了一个名为buffer的字符类型数组,其大小为64.然后,我们看到modified变量设置为0和以buffer变量作为参数调用的gets函数。最后,我们看到一个IF语句,检查modified是不是0.显然,在这段代码中没有地方把modified这个变量设置为0以外的任何数,那么我们如何更改呢?
好吧,我们先来看看获取功能文档:


如您所见,gets函数只接受用户输入。但是,该函数不检查用户输入是否实际适合我们存储的数据结构(在本例中为缓冲区),因此,我们能够溢出数据结构并影响堆栈上的其他变量/数据。此外,由于我们知道所有变量都存储在堆栈中,并且我们知道modified变量的值是多少(0),我们所要做的就是输入足够的数据来覆盖modified变量。我们来看一个图:

如您所见,如果恶意用户输入太多文本,他们可以覆盖modified变量和堆栈上的任何其他内容,包括返回指针。这意味着如果恶意代理能够控制程序堆栈,它们实际上可以控制整个程序并使其执行任何他们想要的操作。他们可以简单地将堆栈上函数返回指针覆盖掉并且指向携带某个恶意payload的自定义指针。

0x05.攻击:return-to-libc(ret2libc)攻击

在我们讨论return-to-libc(ret2libc)攻击之前,让我们花点时间更深入地讨论libc。
(从第0x01节)我们知道,libc是标准的C库。这意味着它包含C编程语言中包含的所有通用系统函数。现在,如果恶意用户能够控制程序执行其中一些功能,该怎么办?
嗯,这就是ret2libc。对于ret2libc的后果的一个完美类比可能是黑客帝国系列。回想一下经典的“Guns, lots of guns”场景。操作员TANK,能够完全绕过并重新编译黑客帝国,使得大量枪支出现整个空间。
你可以想到像这样返回libc,我们能够控制黑客帝国(标准C库)并使它做任何我们想做的事情。
在它的基础上,ret2libc攻击实际上是基于堆栈缓冲区溢出。回想一下我在第0x04节末尾所说的内容,如果恶意代理可以覆盖堆栈上的数据,它们可以简单地覆盖返回指针以指向libc中的特定函数,并通过它传递任何payload必要的参数。
用于ret2libc攻击的最常见功能之一是系统函数。我们来看看它的文档:

如您所见,system命令只执行shell命令(shell是linux命令行)。此外,如果我们读上面的描述,我们可以看到system只执行/bin/sh -c <command>(/bin/sh是实际的shell命令),并且命令通过参数传递给函数。
因此,我们所要做的就是获取易受攻击的程序机器的命令行访问权限,首先将“/bin/sh”作为参数推送到堆栈,然后将函数返回或调用指针替换为系统函数的内存地址,以便/bin/sh作为函数参数被调用,最后启动shell并授予我们对系统的完全访问权限。

0x06 - 第1部分结论

在本文中,我们讨论了:
1.虚拟内存以及应用程序如何在内存处理
2.动态链接库和libc
3.堆栈
4.如何调用函数以及函数返回的工作机制
5.堆栈缓冲区溢出
6.返回libc(ret2libc)攻击
我希望这篇文章很有帮助。

点击收藏 | 1 关注 | 2
  • 动动手指,沙发就是你的了!
登录 后跟帖