由于是等宽字体。。。
请复制在记事本上看。。。
附件包含原版和译版
bofs4kids.rar
____ __ __ ___ ____
| __ ) _ _ / _|/ _| ___ _ __ / _ \__ _____ _ __ / _| | _____ _____
| _ \| | | | |_| |_ / _ \ '__| | | | \ \ / / _ \ '__| |_| |/ _ \ \ /\ / / __|
| |_) | |_| | _| _| __/ | | |_| |\ V / __/ | | _| | (_) \ V V /\__ \
|____/ \__,_|_| |_| \___|_| \___/ \_/ \___|_| |_| |_|\___/ \_/\_/ |___/
_____ _ ___ _
| ___|__ _ __ | |/ (_) __| |___
| |_ / _ \| '__| | ' /| |/ _` / __|
| _| (_) | | | . \| | (_| \__ \
|_| \___/|_| |_|\_\_|\__,_|___/
by bob [www.dtors.net]
翻译:niseceric [nisec_eric@126.com]
[[译者序]]
本教程介绍的是缓冲区溢出的基本原理,和栈溢出的基本原理。本教程不包含指导写利
用程序的具体实现。本教程虽然面向Linux系统,但是作为原理介绍和缓冲区基本入门
还是非常容易理解的。(windows原理基本相同,只是在利用程序实现上不同)
本教程在文本格式下完成,利用等宽字体浏览可获得最佳的效果。
本教程原版本发布时间大致在2003-2004年,虽然有些老还是还是非常不错的。
本教程版权由原作者所有,译文和译者注解版权由译者niseceric所有。欢迎任意转载,
但请完整转载,包括作译者信息和本序。
2011-01-18 by niseceric
[[简介]]
本教程并非指导该如何编写一个利用程序(exloit),而是希望通过本教程让读者了解以
下几方面:
* 什么是缓冲区溢出(buffer overflow 简称BOF)
* 有哪几种缓冲区溢出方式
* 如何利用缓冲区溢出
* 如何鉴别缓冲区溢出
本文不需要读者有任何C或者ASM知识,我将尽可能为你做出解释。(译者注:最好还是
有一点C和linux基本知识的)
本教程目标在于让读者能更好理解缓冲区溢出。如果读者想要编写自己的缓冲区溢出利用
程序,或者寻找提交现有的BUG,那么本教程将会是很好的起步点。在教程的最好我会给
出一些优秀的参考文章,希望他们能帮助你更好理解本主题。(译者注:链接已经失效)
希望你能愉快地享受本教程,让我们开始吧~
[[术语解释]]
首先,我们不得不先列举令人厌恶的专有名词,以便你在阅读中遇到不会感到迷惑。
利用程序(exploit): 顾名思义,就是利用程序中的漏洞的程序,它能让我们执行任意
原本不应该被执行的程序。
缓冲区(buffer): 存有多个相同类型的数据的一片内存块。
堆(heap): 程序为变量保留的空间(可以使用malloc()来使用堆)。
栈(stack): 程序储存临时信息的地方(例如调用的函数的返回地址或者临时(local)
变量)。
栈帧指针(SFP): Stack Frame Point 栈(stack)的开始地址。(译者注:就是栈底,
不是栈顶哦,一般存在寄存器%EBP中,SFP保存前一个%EBP(调用
者的)。)
返回地址(RET): 当我们调用一个函数,系统将保存函数调用时继续执行的地址就是返
回地址。当函数调用结束后,系统将返回这个地址,继续运行程序。
(坏家伙笑笑:如果我们改变这个地址,我们将它指向我们想要指向
的代码地址,来执行任意程序)(译者注:一般RET保存的是调用函
数时候寄存器%EIP的值)
[[缓冲区溢出是什么?]]
想想如果我们将一品脱的啤酒倒入只能容纳半品脱的杯子中会发生什么?
啤酒满出,将流得到处都是。我们浪费了啤酒。
用计算机行话来解释:
缓冲区溢出就是往缓冲区写入了超过缓冲区承载能力的数据。如果覆盖了特定的内存指
针,我们就能执行任意的代码。
我们来举个例子,下面c程序会证明缓冲区溢出是怎么发生的。
---------------------------------我是代码分割线--------------------------
/*
* beer.c
*/
void main() {
char pint[10];
char half_pint[1];
memset(pint,0x41,10);
strcpy(half_pint,pint);
}
---------------------------------我是代码分割线--------------------------
本程序用来模拟先前我们举的向小杯子(容积1/10品脱)灌一品脱的啤酒的例子。
我将逐行向不懂c语言的读者解释代码的意思。
void main(){ -- 这是我们的主程序入口
char pint[10]; -- 这是我们第一个变量pint(空的啤酒瓶),请留意这个变量的大小
char half_pint[1]; -- 这是我们第二个变量(小杯子),注意它只有啤酒的1/10
memset(pint,0x41,10); -- memset为pint填充了10个字母a(0x41是a的acsii码)
想象一下,这就是我们为啤酒瓶灌满啤酒a
strcpy(half_pint,pint); -- 现在我们尝试把一整瓶就倒入小杯子,当然美味的啤酒
溢出了 :(
} -- 主程序结束
很容易理解吧^_^ 我们只是简单地向变量塞入过多的数据。
由于溢出,栈帧指针(SFP)和函数返回地址(RET)都被“我们的啤酒”覆盖了 :) 。
这意味者我们的返回地址变成了0x41414141。在这个例子中,它会引起错误(它已经不在
程序处理空间中,我们等下解释)。
既然我们已经知道我们能覆盖返回地址(RET),那么我们能就可以利用这个特点。
为了达到目的,我们必须将它(RET)指向我们的新地址,可以执行任意代码的地方。
奇妙吧~
有一点我必须明确下,在绝大多数情况下不要盲目尝试溢出程序,除非这个程序由root
拥有而且具有suid权限标志。
你也许被上面这句话弄糊涂了,简单来说,程序必须能够使你临时获得root权限。
SUID? suid权限标志就是给程序执行普通权限无法执行的执行能力。举例来说:普通用
户无法读取/etc/passwd文件,但是如果使用/usr/bin/passwd程序,我们就能够获得修改
passwd文件的权限(当然这里只能修改自己的密码)。
(译者注:Linux下拥有suid标志的程序在运行时候可以获得程序拥有者的权限,我们就
能得到root权限啦)
[[不同类型的缓冲区溢出]]
主要有两种著名的缓冲区溢出,一个基于栈(stack),另一个基于堆(heap)。
基于栈的缓冲区溢出更加常见,但是现在出现了栈上不可执行代码的保护措施,将阻止基
于栈的攻击。所以才发展了基于堆的攻击,绕过了栈的保护措施。
不幸的是堆溢出并不是很容易理解,所以为了使本教程简化,我们下面将讨论限制于基于
栈的缓冲区溢出。
我已经给出了栈的定义,下面我将向你展示一个栈上缓冲区如何溢出的实例。
下文将更加深入细节,可能有些难以理解,但请继续坚持下去 :)
让我们重新写一个有漏洞的程序~
---------------------------------我是代码分割线--------------------------
/* for testing purposes change the function from bob to main */
void bob(){
char beer[10];
gets(beer);
}
---------------------------------我是代码分割线--------------------------
栈的工作方式很简单。遵循后进先出原则(LIFO: Last in, First off.)。明白没有?
想象乐高积木,每次搭积木,我们总是将新的积木放在最顶上,当要拆积木时候,都是从
最顶上开始拆。这可能听上去有些令人迷惑,事实上它真的很简单。
这就是栈组织数据的方法。
回到我们的程序,程序的目的就是需要我们的输入,如果用户输入超过15个字符,程序就
会溢出,引发段错误。
我们看看是如何执行的?
当我们的主程序调用函数bob()的时候,bob函数首先会在栈上给beer[]变量分配空间。如下
译者注: 内存地址
+++++++++++++++++++++ <-- 栈顶 小
+ beer[] + <-- 给beer[]变量分配的空间 |
+++++++++++++++++++++ |
+ + <-- SFP在RET之前 v
+ Return Address + <-- RET bob函数的返回地址 大
+++++++++++++++++++++
+ +
+ +
^^^ 初始栈看上去像这样。
现在我们的目标在于向beer[]写入恶意的代码。
同时溢出beer[],覆盖RET返回地址。
我们想要的是将RET地址写为beer[]的地址,这样当bob()函数调用完成时候,我们的恶意
代码将被执行。
让我们来看看现在栈的样子:
译者注:
+++++++++++++++++++++ <-- 栈顶
+ Malicious Code + <-- 恶意代码开始
+++++++++++++++++++++
+ +
+ Address of beer[] + <-- 返回函数指向恶意代码的地址
+++++++++++++++++++++
+ +
+ +
这样bob()将会返回beer[]处继续执行代码。
你还醒着么?看到这里我想你一定学到了什么吧~
[[结束语]]
差不多了。
我已经给你展示了缓冲区的基本的概况,和如何利用以及栈的工作原理。
有许多方法可以防止栈溢出攻击,例如确保当你涉及操作栈时使用了安全的函数。
同时也确保你提供了处理存在潜在危险的用户自定义输入。
找出潜在的缓冲区溢出漏洞很简单。在linux上,绝大多数都是开源程序,你可以利用
像FlawFinder之类的程序来检测源代码。
FlawFinder程序检测一些不进行边界检测的库函数比如strcpy() strcat() sprintf()
vsprintf() scanf() getc() 和 getchar()。
你可以从www.packerstormsecurity.org下载到FlawFinder程序。
感谢阅读,希望你已经从本教程中学到了一些东西~ :)
[[联系方式]]
原作者邮箱: bob@dtors.net
原作者主页: http://bob.dtors.net
[[参考资料]]
http://www.11a.nu/stack/stack-smash.txt
http://www.11a.nu/stack/heaptut.txt
http://www.11a.nu/stack/exploit.txt
http://www.11a.nu/stack/adv.overflow.paper.txt
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!