1、申 请 I D:99118822
2、个人邮箱:lsy05505100@sina.com
3、原创技术文章:如何让你的shellcode在漏洞中跑起来
如何让你的shellcode在漏洞中跑起来 背景 不熟悉shellcode的小伙伴,本期可以先阅读安华金和数据库攻防实验室前期文章《windows缓冲区溢出原理(栈)》,做一下初步了解。shellcode 一词语起源于Aleph One 在Underground 发表的著名论文《SMASHING THE STACK FOR FUN AND PROFIT》。shellcode 是一段可执行特定功能的独立代码。它伴随缓冲区溢出攻击代码出现,在其中扮演劫持程序后执行恶意代码的角色。shellcode一般由汇编语言完成,功能有:窃取关键数据、上传木马病毒、打开后门等。在网上有很多经典的shellcode脚本,尽管如此,小伙伴们经常还是无法正确使用。 本文重点讲述如何让你的shellcode在漏洞中跑起来。文中实例采用Oracle经典漏洞tns_service_name_too_long。该漏洞被发现于oracle监听(TNS)上,由于数据库客户端向数据库监听(TNS)发送的连接串:"(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP) (HOST=#{数据库机器IP}(PORT=#{数据库机器端口})) (CONNECT_DATA=(SERVICE_NAME=#{数据库服务名}) (CID=(PROGRAM=MSF))))" 其中数据库服务名如果输入一个过长的串会造成缓冲区溢出。(建议使用脚本测试,不要用数据库客户端。因为数据库客户端会对过长的数据库服务名进行处理,不会形成缓冲区溢出)。本文为了防止不必要的危险,选取打开计算器的shellcode: 入侵成功会直接在目标机器上打开一个计算器 shellcode的问题 最基本的缓冲区溢出攻击串是如下图构造的: 首先通过NOP填充缓冲区,直到接近函数返回的地方。用shellcode所在第一个四字节地址值覆盖返回地址,覆盖RET,返回函数后进入shellcode。完成整个入侵过程。在整个过程中会出现很多风险导致shellcode执行失败。 其中shellcode有三个难点将决定你的入侵是否成功: 一.动态定位shellcode 问题 如上图所示,当我们可以用越界的字符完全控制返回地址后,需要将返回地址改写成shellcode在内存中的起始地址。由于大部分漏洞所在函数处于动态库中,因此即便在一台机器上,由于动态库在程序运行时的装入和卸载。会导shellcode地址出现变化。 如上图所示:shellcode 的地址发生了偏移,导致虽然控制了返回地址,但是并未跳入到shellcode中,无法执行shellcode代码。 场景1:无参数只shellcode发生偏移 为了解决这种shellcode偏移,咱们可以采用call/jmp sep的方式,来帮助跳入shellcode中。如图所示: file:///C:/Users/LIUSIC~1/AppData/Local/Temp/msohtmlclip1/01/clip_image002.pngSTACKE DATA | | | 用API中的jump/call esp 地址覆盖函数返回所指向地址
| 前栈帧(被覆盖)
| 当函数返回时,esp指向shellcode头地址,会直接从jump跳入esp中
| Ret(jump esp)
| | | |
在系统API中(windows下建议在kernel32.dll)查找jmp esp或call esp 所在地址。当函数返回(ret)的时候,ESP恰好指向栈帧中返回地址的后一个位置(shellcode都地址)正好准确跳入shellcode。 核心代码: buff = #在缓冲区中为了到ret而填充的NOP buff =#jmp esp from kernel32.dll buff =#shellcode sploit ="(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=#{rhost}(PORT=#{rport}))(CONNECT_DATA=(SERVICE_NAME=#{buff}) (CID=(PROGRAM=MSF))))"。 同样如果搜不到jmp/call esp 地址可以通过 push/pop return地址,利用原理类似。这是最经典、最常见的跳入shellcode方法 场景2:有参数shellcode偏移 如果shellcode 不但偏移了,而且在shellcode前有参数,导致shellcode 从esp变到了esp+8的位置,此时就不能单纯采用jmp/call esp需要配合使用pop ret。一个pop指令将弹出栈顶4字节,指针将指向esp+4。在运行一个pop指令,会从栈顶中再弹出4字节。此时esp将直接指向shellcode的其实地址。后面可以利用jmp esp 来完成跳入shellcode。 具体如下 file:///C:/Users/LIUSIC~1/AppData/Local/Temp/msohtmlclip1/01/clip_image002.pngSTACKE DATA | | | | | NOP EIP NOP ... NOP jmp esp shellcode =POPPOPRET 8字节 =esp now point here
| pop
| | |
EIP被pop pop ret 覆盖掉,ESP指向SHELLCODE偏移8字节。pop pop ret被执行,连续弹出8字节的栈。EIP又被jmp esp 地址覆盖,因此第二个跳转被执行,然后跳入shellcode。 核心代码 buff = #在缓冲区中为了到ret而填充的NOP buff=#pop pop ret 地址 buff =#jmp esp from kernel32.dll buff=#添加8个字节作为参数(本函数没有这个变量,手工添加上+) buff =#shellcode sploit ="(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=#{rhost}(PORT=#{rport}))(CONNECT_DATA=(SERVICE_NAME=#{buff}) (CID=(PROGRAM=MSF))))" 执行结果 场景3:大偏移esp 场景2中pop pop ret 是针对esp+8这种特殊情况的,对于shellcode偏移多个字节的情况,我们一般采用jmp[reg]+[offset]这种方式处理。(这种方式一样可以处理偏移8个字节只是jmp esp+8的地址不如pop pop ret 多)。 类似场景2先查找jmp esp+字节个数或大于字节个数的地址。利用该地址覆盖EIP.通过在shellcode 入口前添加NOP来调整jmp 跳过的范围,来确保jmp能跳入shellcode。 场景4一次移动32位ESP popad指令可以帮助我们跳转到shellcode,popad从栈中弹出DWRD数据,并赋予各通用寄存器,它会按照固定顺序加载各个寄存器。每次加载寄存器都会引起ESP的递增。一次popad会用掉ESP中的32字节,并按固定顺序pop如到各个寄存器中。同样可以用类似场景3的方式用nop帮助跳入shellcode中。 场景5模糊定位shellcode 前面4种场景都是在能够使用跳转指令精确定位shellcode的,而有些时候shellcode无法被精确定位,那么可以采用喷射的方式。淹没大片的内存区域,将shellcode 部署在一片nop之后。只要ret能命中这一片nop中任何一个。shellcode一样可以最终得到执行。如下图所示 二.shellcode 被破坏问题 在解决shellcode的定位问题后,接踵而来的就是把shellcode放在哪的问题。如果我们不把shellcode放入被入侵的缓冲区中,shellcode会覆盖掉更多前置的栈帧,这可能给系统带来不可预测的结果,也导致缓冲区溢出的破坏无法通过修复寄存器的值来修复。所以一般我们都把shellcode放入缓冲区中,于是我们需要在代码中jmp esp 后的esp区放入的是shellcode header 来引导处理器跳转到shellcode中。整体结构变为: 虽然这种方式有这么多利处,但同时也带来了一个隐患shellcode容易被破坏。函数返回后,栈被弹出虽然逻辑上ESP以上的内存空间中数据已经失效。但实际上这些数据并没有销毁。如果shellcode 中有压栈指令,向栈中写入数据。则压栈数据很可能会破坏shellcode。 file:///C:/Users/LIUSIC~1/AppData/Local/Temp/msohtmlclip1/01/clip_image005.png file:///C:/Users/LIUSIC~1/AppData/Local/Temp/msohtmlclip1/01/clip_image006.png..... | | | | | | | |
于是请注意在shellcode开始就大范围太高栈顶,把shellcode 藏在栈内保证shellcode 不被破坏。 三.注意shellcode选择的字符 在很多漏洞的利用场景中,shellcode的内容会受到限制。所有的字符串函数都会对NULL字节进行限制。通常我们选择特殊的指令来避免在shellcode中直接出现null字节,甚至有时候函数还要求shellcode必须为可见字符。除去软件的限制外,基于特征值的IDS系统也会对带有某些特征的shellcode进行拦截。这都是需要注意的地方。往往这部分我们采用的解决办法是给shellcode加壳。在编码后的shellcode前加入解码指令。帮助shellcode通过重重检查,直到在内存中运行的时候才解开,保证shellcode的不被拦截。 至此,相信只要你克服以上三点shellcode的问题,绝对可以修改出适合你自己漏洞的shellcode了。在下一期我们将和大家探讨如何优化你的shellcode。
|