软件漏洞分析入门.docx
- 文档编号:9636470
- 上传时间:2023-02-05
- 格式:DOCX
- 页数:61
- 大小:54.54KB
软件漏洞分析入门.docx
《软件漏洞分析入门.docx》由会员分享,可在线阅读,更多相关《软件漏洞分析入门.docx(61页珍藏版)》请在冰豆网上搜索。
软件漏洞分析入门
软件漏洞分析入门
1引子
Tobetheapostrophewhichchanged“Impossible”into“I’mpossible”
——failwest
凉风有讯,秋月无边。
您是否梦想过能够像电影上演的那样黑进任意一台机器远程操控?
您的梦想是否曾经被书店里边满架子的反黑,防毒,擒木马的扫盲书强暴的体无完肤?
从今天开始,准备陆续发一系列关于软件漏洞方面基础知识的帖子,包括软件漏洞的研究价值,研究方法,堆栈利用的基础知识,shellcode的调试方法,漏洞调试方法,漏洞分析,漏洞挖掘,软件安全性测试等等,此外还将介绍一些metasploit架构和fuzz测试方面的入门知识。
软件漏洞分析,利用,发掘是当今安全技术界中流砥柱级别话题,如果您关注过blackhat或者defcon之类的顶级安全技术峰会的话,就知道我不是在吹牛了。
可惜的是这方面的中文资料很少,偶尔有一篇比较优秀的文章但又不够系统,目前为止也没有形成像破解技术这样的讨论风气,菜鸟们在黑灯瞎火的夜晚瞎折腾,没有交流和指导,兴趣就像被拔了气弥儿芯的车胎,很快就泄气了。
虽然漏洞分析与利用与破解在技术上各有侧重点,但逆向基础是共同的。
以我个人的经验,能做crack的朋友只要稍加进修就能入门。
就算没有任何汇编基础和逆向经验的朋友也不用担心,因为这个系列的文章将完全面向菜鸟,只要会C语言,跟着文章用ollydbg调试几次连猜带蒙的也应该能够上手。
今天我们暂时不谈堆栈这些技术细节,先让我们从比较宏观的地方着手。
如果您经历过冲击波蠕虫病毒的攻击话,应该明白操作系统出现漏洞时的后果。
漏洞往往是病毒木马入侵计算机的突破口。
如果掌握了漏洞的技术细节,能够写出漏洞利用(exploit),往往可以让目标主机执行任意代码。
软件漏洞的技术细节是非常宝贵的资料,尤其是当软件漏洞对应的官方补丁尚未发布时,只有少数攻击者秘密的掌握漏洞及其利用方法,这时往往可以通过漏洞hack任意一台internet上的主机!
这种未被公开的漏洞被称作zeroday(0day)。
可以把0day理解成未公开的系统后门。
由于0day的特殊性质和价值,使得很多研究者和攻击者投身于漏洞挖掘的行列。
一个0day漏洞的资料根据其影响程度的不同,在黑市上可以卖到从几千元到几十万元不等的价钱。
因此0day一旦被发现往往会被当作商业机密,甚至军事机密~~~~如果把冲击波蠕虫的shellcode从原先的一分钟倒计时关机改为穷凶极恶的格式化硬盘之类~~~~~那么花一百万买这样一个电子炸弹可比花一百万买一枚导弹来得划算~~~~~~试想一下某天早上起来发现全国的windows系统都被格式化,计算机系统完全瘫痪造成的影响和一颗导弹在城市里炸个坑造成的影响哪个更严重?
在今天这一讲的最后,让我们回顾一下几个可能曾经困惑过您的问题:
我从不运行任何来历不明的软件,为什么还会中病毒?
如果病毒利用重量级的系统漏洞进行传播,您将在劫难逃。
因为系统漏洞可以引起计算机被远程控制,更何况传播病毒。
横扫世界的冲击波蠕虫,slamer蠕虫等就是这种类型的病毒。
如果服务器软件存在安全漏洞,或者系统中可以被RPC远程调用的函数中存在缓冲区溢出漏洞,攻击者也可以发起“主动”进攻。
在这种情况下,您的计算机会轻易沦为所谓的“肉鸡”。
我只是点击了一个URL链接,并没有执行任何其他操作,为什么会中木马?
如果您的浏览器在解析HTML文件时存在缓冲区溢出漏洞,那么攻击者就可以精心构造一个承载着恶意代码的HTML文件,并把其链接发给您。
当您点击这种链接时,漏洞被触发从而导致HTML中所承载的恶意代码(shellcod)被执行。
这段代码通常是在没有任何提示的情况下去指定的地方下载木马客户端并运行。
此外,第三方软件所加载的ActiveX控件中的漏洞也是被“网马”所经常利用的对象。
所以千万不要忽视URL链接。
Word文档、PowerPoint文档、Excel表格文档并非可执行文件,他们会导致恶意代码的执行吗?
和html文件一样,这类文档本身虽然是数据文件,但是如果Office软件在解析这些数据文件的特定数据结构时存在缓冲区溢出漏洞的话,攻击者就可以通过一个精心构造的word文档来触发并利用漏洞。
当您在用office软件打开这个word文档的时候,一段恶意代码可能已经悄无声息的被执行过了。
好,第一讲暂时结束,如果您有兴趣,不妨关注一下这个系列,说不定听完几讲之后会深深的爱上这个门技术。
标题:
【技术专题】软件漏洞分析入门_2_初级栈溢出A_初识数组越界
作者:
failwest
时间:
2007-12-13,10:
27
链接:
2_初级栈溢出_A
Tobetheapostrophewhichchanged“Impossible”into“I’mpossible”
——failwest
今夜月明星稀
本想来点大道理申明下研究思路啥的,看到大家的热情期待,稍微调整一下讲课的顺序。
从今天开始,将用3~4次给大家做一下栈溢出的扫盲。
栈溢出的文章网上还是有不少的(其实优秀的也就两三篇),原理也不难,读过基本上就能够明白是怎么回事。
本次讲解将主要集中在动手调试方面,更加着重实践。
经过这3~4次的栈溢出扫盲,我们的目标是:
领会栈溢出攻击的基本原理
能够动手调试简易的栈溢出漏洞程序,并能够利用漏洞执行任意代码(最简易的shellcode)
最主要的目的其实是激发大家的学习兴趣——寡人求学若干年,深知没有兴趣是决计没有办法学出名堂来的。
本节课的基本功要求是:
会C语言就行(大概能编水仙花数的水平)
我会尽量用最最傻瓜的文字来阐述这些内存中的二进制概念。
为了避免一开始涉及太多枯燥的基础知识让您失去了兴趣,我并不提倡从汇编和寄存器开始,也不想用函数和栈开头。
我准备用一个自己设计的小例子开始讲解,之后我会以这个例子为基础,逐步加码,让它变得越来越像真实的漏洞攻击。
您需要的就是每天晚上看一篇帖子,然后用十分钟时间照猫画虎的在编译器里把例子跟着走一遍,坚持一个星期之后您就会发现世界真奇妙了。
不懂汇编不是拒绝这门迷人技术的理由——今天的课程就不涉及汇编——并且以后遇到会随时讲解滴
所以如果你懂C语言的话,不许不学,不许说学不会,也不许说难,哈哈
开场白多说了几句,下面是正题。
今天我们来一起研究一段暴简单无比的C语言小程序,看看编程中如果不小心出现数组越界将会出现哪些问题,直到这个单元结束您能够用这些数组越界漏洞控制远程主机。
#include
#definePASSWORD"1234567"
intverify_password(char*password)
{
intauthenticated;
charbuffer[8];//addlocalbufftobeoverflowed
authenticated=strcmp(password,PASSWORD);
strcpy(buffer,password);//overflowedhere!
returnauthenticated;
}
main()
{
intvalid_flag=0;
charpassword[1024];
while
(1)
{
printf("pleaseinputpassword:
");
scanf("%s",password);
valid_flag=verify_password(password);
if(valid_flag)
{
printf("incorrectpassword!
\n\n");
}
else
{
printf("Congratulation!
Youhavepassedtheverification!
\n");
break;
}
}
}
对于这几行乱简单无比的程序,我还是稍作解释。
程序运行后将提示输入密码
用户输入的密码将被程序与宏定义中的“1234567”比较
密码错误,提示验证错误,并提示用户重新输入
密码正确,提示正确,程序退出(真够傻瓜的语言)
所谓的漏洞在于verify_password()函数中的strcpy(buffer,password)调用。
由于程序将把用户输入的字符串原封不动的复制到verify_password函数的局部数组charbuffer[8]中,但用户的字符串可能大于8个字符。
当用户输入大于8个字符的缓冲区尺寸时,缓冲区就会被撑暴——即所谓的缓冲区溢出漏洞。
缓冲区给撑暴了又怎么样?
大不了程序崩溃么,有什么了不起!
此话不然,如果只是导致程序崩溃就不用我在这里浪费大家时间了。
根据缓冲区溢出发生的具体情况,巧妙的填充缓冲区不但可以避免崩溃,还能影响到程序的执行流程,甚至让程序去执行缓冲区里的代码。
今天我们先玩一个最简单的。
函数verify_password()里边申请了两个局部变量
intauthenticated;
charbuffer[8];
当verify_password被调用时,系统会给它分配一片连续的内存空间,这两个变量就分布在那里(实际上就叫函数栈帧,我们后面会详细讲解),如下图
2_1.JPG
变量和变量紧紧的挨着。
为什么紧挨着?
当然不是他俩关系好,省空间啊,好傻瓜的问题,笑:
)
用户输入的字符串将拷贝进buffer[8],从示意图中可以看到,如果我们输入的字符超过7个(注意有串截断符也算一个),那么超出的部分将破坏掉与它紧邻着的authenticated变量的内容!
在复习一下程序,authenticated变量实际上是一个标志变量,其值将决定着程序进入错误重输的流程(非0)还是密码正确的流程(0)。
下面是比较有趣的部分:
当密码不是宏定义的1234567时,字符串比较将返回1或-1(这里只讨论1,结尾的时候会谈下-1的情况)
由于intel是所谓的大顶机,其实就是内存中的数据按照4字节(DWORD)逆序存储,所以authenticated为1时,内存中存的是0x01000000
如果我们输入包含8个字符的错误密码,如“qqqqqqqq”,那么字符串截断符0x00将写入authenticated变量
这溢出数组的一个字节0x00将恰好把逆序存放的authenticated变量改为0x00000000。
函数返回,main函数中一看authenticated是0,就会欢天喜地的告诉你,ohyeah密码正确!
这样,我们就用错误的密码得到了正确密码的运行效果
下面用5分钟实验一下这里的分析吧。
将代码用VC6.0编译链接,生成可执行文件。
注意,是VC6.0或者更早的编译器,不是7.0,不是8.0,不是.net,不是VS2003,不是VS2005。
为什么,其实不是高级的编译器不能搞,是比较难搞,它们有特殊的GS编译选项,为了不给咱们扫盲班增加负担,所以暂时飘过,用6.0!
按照程序的设计思路,只有输入了正确的密码”1234567”之后才能通过验证。
程序运行情况如下:
2_2.JPG
要是输入几十个字符的长串,应该会崩溃。
多少个字符会崩溃?
为什么?
卖个关子,下节课慢慢讲。
现在来个8个字符的密码试下:
2_3.JPG
注意为什么01234567不行?
因为字符串大小的比较是按字典序来的,所以这个串小于“1234567”,authenticated的值是-1,在内存里将按照补码存负数,所以实际村的不是0x01000000而是0xffffffff。
那么字符串截断后符0x00淹没后,变成0x00ffffff,还是非0,所以没有进入正确分支。
总结一下,由于编程的粗心,有可能造成程序中出现缓冲区溢出的缺陷。
这种缺陷大多数情况下会导致崩溃,但是结合内存中的具体情况,如果精心构造缓冲区的话,是有可能让程序作出设计人员根本意向不到的事情的
本节只是用一个字节淹没了邻接变量,导致了程序进入密码正确的处理流程,使设计的验证功能失效。
其实作为cracker,大家可能会说这有什么难的,我可以说出一堆方法做到这一点:
直接查看PE,找出宏定义中的密码值,得到正确密码
反汇编PE,找到爆破点,JZJNZ的或者TESTEAX,EAX变XOREAX,EAX的在分支处改它一个字节
……
但是今天介绍的这种方法与crack的方法有一个非常重要的区别,非常非常重要~~
就是~~~我们是在程序允许的情况下,用合法的输入数据(对于程序来说)得到了非法的执行效果(对于程序员来说)——这是hack与crack之间的一个重要区别,因为大多数情况下hack是没有办法直接修改PE的,他们只能通过影响输入来影响程序的流程,这将使hack受到很多限制,从某种程度上讲也更加困难。
这个区别将在后面几讲中得到深化,并被我不断强调。
好了,今天的扫盲课程暂时结束,作为栈溢出的开场白,希望这个自制的漏洞程序能够给您带来一点点帮助。
顺便预告一下下一讲的内容:
初级溢出B:
将讲述函数调用时怎样和系统栈配合的,然后在本讲的基础上淹没栈帧寄存器,直接改变程序流程
初级溢出C:
手把手的教你写一段超简单的shellcode(可执行的机器代码),并把这段代码做为密码输入,最后引导程序跳去执行这段代码
下次再见:
)
标题:
【技术专题】软件漏洞分析入门_3_初级栈溢出B_系统栈原理
作者:
failwest
时间:
2007-12-14,00:
13
链接:
第3讲初级栈溢出B
Tobetheapostrophewhichchanged“Impossible”into“I’mpossible”
——failwest
小荷才露尖尖角
扫盲班第三讲开课啦!
上节课我们用越过数组边界的一个字节把邻接的标志变量修改成0,从而突破了密码验证程序。
您实验成功了吗?
没有的话回去做完实验在来听今天的课!
有几个同学反映编译器的问题,我还是建议用VC6.0,因为它build出来的PE最适合初学者领会概念。
而且这门课动手很重要,基本上我的实验指导都是按VC6.0来写的,用别的build出来要是有点出入,实验不成功的话会损失学习积极性滴——实验获得的成就感是学习最好的动力。
另外在回帖中已经看到不少同学问了一些不错的问题:
如果变量之间没有相邻怎么办?
如果有一个编译器楞要把authenticated变量放在buffer[8]数组前边咋办?
今天的课程将部分回答这些问题。
今天基本没有程序和调试(下一讲将重新回归实践),主要是一些理论知识的补充。
听课的对象是只用C语言编过水仙花数的同学。
如果你不是这样的同学,可以飘过本讲,否则你会说我罗嗦滴像唐僧~~~~我的目标就是一定要让你弄明白,不管多罗嗦,多俗气,多傻瓜的方法,呵呵
找工作滴同学也可以看看这部分,很可能会对面试有帮助呦。
根据我个人无数次的面试经验,会有很多考官饶有兴趣的问你学校课本上从来不讲的东东,比如堆和栈的区别,什么样的变量在栈里,函数调用是怎么实现的,参数入栈顺序,函数调用时参数的值传递、地址传递的原理之类。
学完本节内容,您将对高级语言的执行原理有一个比较深入的认识。
此外,这节课会对后面将反复用到的一些寄存器,指令进行扫盲。
不要怕,就几个,保管你能弄懂。
最后,上次提意见说图少的同学注意了,这节课的配套图示那叫一个多啊。
所以还是那句话,不许不学,不许学不会,不许说难,呵呵
我们开始吧!
根据不同的操作系统,一个进程可能被分配到不同的内存区域去执行。
但是不管什么样的操作系统、什么样的计算机架构,进程使用的内存都可以按照功能大致分成以下四个部分:
代码区:
这个区域存储着被装入执行的二进制机器代码,处理器会到这个区域来取指并执行。
数据区:
用于存储全局变量等。
堆区:
进程可以在堆区动态的请求一定大小的内存,并在用完之后归还给堆区。
动态分配和回收是堆区的特点
栈区:
用于动态的存储函数之间的调用关系,以保证被调用函数在返回时恢复到母函数中继续执行
注意:
这种简单的内存划分方式是为了让您能够更容易地理解程序的运行机制。
《深入理解计算机系统》一书中有更详细的关于内存使用的论述,如果您对这部分知识有兴趣,可以参考之
在windows平台下,高级语言写出的程序经过编译链接,最终会变成各位同学最熟悉不过的PE文件。
当PE文件被装载运行后,就成了所谓的进程。
01.JPG
图1
如果把计算机看成一个有条不紊的工厂的话,那么可以简单的看成是这样组织起来的:
CPU是完成工作的工人;
数据区,堆区,栈区等则是用来存放原料,半成品,成品等各种东西的场所;
存在代码区的指令则告诉CPU要做什么,怎么做,到哪里去领原材料,用什么工具来做,做完以后把成品放到哪个货舱去;
值得一提的是,栈除了扮演存放原料,半成品的仓库之外,它还是车间调度主任的办公室。
程序中所使用的缓冲区可以是堆区、栈区、甚至存放静态变量的数据区。
缓冲区溢出的利用方法和缓冲区到底属于上面哪个内存区域密不可分,本讲座主要介绍在系统栈中发生溢出的情形。
堆中的溢出稍微复杂点,我会考虑在中级班中给予介绍
以下内容针对正常情况下的大学本科二年级计算机水平或者计算机二级水平的读者,明白栈的飘过即可。
从计算机科学的角度来看,栈指的是一种数据结构,是一种先进后出的数据表。
栈的最常见操作有两种:
压栈(PUSH),弹栈(POP);用于标识栈的属性也有两个:
栈顶(TOP),栈底(BASE)
可以把栈想象成一摞扑克牌:
PUSH:
为栈增加一个元素的操作叫做PUSH,相当于给这摞扑克牌的最上面再放上一张;
POP:
从栈中取出一个元素的操作叫做POP,相当于从这摞扑克牌取出最上面的一张;
TOP:
标识栈顶位置,并且是动态变化的。
每做一次PUSH操作,它都会自增1;相反每做一次POP操作,它会自减1。
栈顶元素相当于扑克牌最上面一张,只有这张牌的花色是当前可以看到的。
BASE:
标识栈底位置,它记录着扑克牌最下面一张的位置。
BASE用于防止栈空后继续弹栈,(牌发完时就不能再去揭牌了)。
很明显,一般情况下BASE是不会变动的。
内存的栈区实际上指的就是系统栈。
系统栈由系统自动维护,它用于实现高级语言中函数的调用。
对于类似C语言这样的高级语言,系统栈的PUSH,POP等堆栈平衡细节是透明的。
一般说来,只有在使用汇编语言开发程序的时候,才需要和它直接打交道。
注意:
系统栈在其他文献中可能曾被叫做运行栈,调用栈等。
如果不加特别说明,我们这里说的栈都是指系统栈这个概念,请您注意与求解“八皇后”问题时在自己在程序中实现的数据结构区分开来。
我们下面就来探究一下高级语言中函数的调用和递归等性质是怎样通过系统栈巧妙实现的。
请看如下代码:
intfunc_B(intarg_B1,intarg_B2)
{
intvar_B1,var_B2;
var_B1=arg_B1+arg_B2;
var_B2=arg_B1-arg_B2;
returnvar_B1*var_B2;
}
intfunc_A(intarg_A1,intarg_A2)
{
intvar_A;
var_A=func_B(arg_A1,arg_A2)+arg_A1;
returnvar_A;
}
intmain(intargc,char**argv,char**envp)
{
intvar_main;
var_main=func_A(4,3);
returnvar_main;
}
这段代码经过编译器编译后,各个函数对应的机器指令在代码区中可能是这样分布的:
02.JPG
图2
根据操作系统的不同、编译器和编译选项的不同,同一文件不同函数的代码在内存代码区中的分布可能相邻也可能相离甚远;可能先后有序也可能无序;但他们都在同一个PE文件的代码所映射的一个“区”里。
这里可以简单的把它们在内存代码区中的分布位置理解成是散乱无关的。
当CPU在执行调用func_A函数的时候,会从代码区中main函数对应的机器指令的区域跳转到func_A函数对应的机器指令区域,在那里取指并执行;当func_A函数执行完闭,需要返回的时候,又会跳回到main函数对应的指令区域,紧接着调用func_A后面的指令继续执行main函数的代码。
在这个过程中,CPU的取指轨迹如下图所示:
03.JPG
图3
那么CPU是怎么知道要去func_A的代码区取指,在执行完func_A后又是怎么知道跳回到main函数(而不是func_B的代码区)的呢?
这些跳转地址我们在C语言中并没有直接说明,CPU是从哪里获得这些函数的调用及返回的信息的呢?
原来,这些代码区中精确的跳转都是在与系统栈巧妙地配合过程中完成的。
当函数被调用时,系统栈会为这个函数开辟一个新的栈帧,并把它压入栈中。
这个栈帧中的内存空间被它所属的函数独占,正常情况下是不会和别的函数共享的。
当函数返回时,系统栈会弹出该函数所对应的栈帧。
04.JPG
图4
如图所示,在函数调用的过程中,伴随的系统栈中的操作如下:
在main函数调用func_A的时候,首先在自己的栈帧中压入函数返回地址,然后为func_A创建新栈帧并压入系统栈
在func_A调用func_B的时候,同样先在自己的栈帧中压入函数返回地址,然后为func_B创建新栈帧并压入系统栈
在func_B返回时,func_B的栈帧被弹出系统栈,func_A栈帧中的返回地址被“露”在栈顶,此时处理器按照这个返回地址重新跳到func_A代码区中执行
在func_A返回时,func_A的栈帧被弹出系统栈,main函数栈帧中的返回地址被“露”在栈顶,此时处理器按照这个返回地址跳到main函数代码区中执行
注意:
在实际运行中,main函数并不是第一个被调用的函数,程序被装入内存前还有一些其他操作,上图只是栈在函数调用过程中所起作用的示意图
每一个函数独占自己的栈帧空间。
当前正在运行的函数的栈帧总是在栈顶。
WIN32系统提供两个特殊的寄存器用于标识位于系统栈栈顶的栈帧:
ESP:
栈指针寄存器(extendedstackpointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的栈顶
EBP:
基址指针寄存器(extendedbasepointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的底部
寄存器对栈帧的标识作用如下图所示:
05.JPG
图5
函数栈帧:
ESP和EBP之间的内存空间为当前栈帧,EBP标识了当前栈帧的底部,ESP标识了当前栈帧的顶部。
在函数栈帧中一般包含以下几类重要信息:
局部变量:
为函数局部变量开辟内存空间。
栈帧状态值:
保存前栈帧的顶部和底部(实际上只保存前栈帧的底部,前栈帧的顶部可以通过堆栈平衡计算得到),用于在本帧被弹出后,恢复出上一个栈帧。
函数返回地址:
保存当前函数调用前的“断点”信息,也就是函数调用前的指令位置,以便函数返回时能够恢复
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 软件 漏洞 分析 入门
![提示](https://static.bdocx.com/images/bang_tan.gif)