[漏洞利用] 软件安全第五章-漏洞利用

635 0
Honkers 2026-2-2 23:39:18 来自手机 | 显示全部楼层 |阅读模式

5.1 概念及示例

5.1.1 漏洞利用
  1. 概念:

    漏洞利用(exploit)是指针对已有的漏洞,根据漏洞的类型和特点而采取相应的技术方 案,进行尝试性或实质性的攻击

  2. 漏洞利用的手段

    1. shell是一个命令解释器,解释由用户输入的命令并将他们送到内核,现在shellcode已经是广义上的植入进程代码

    2. 漏洞利用的核心就是利用程序漏洞去劫持进程的控制权,实现控制流劫持,以便执行植 入的shellcode 或者达到其它的攻击目的。控制流劫持是一种危害性极大的攻击方式,攻击 者能够通过它来获取目标机器的控制权,甚至进行提权操作,对目标机器进行全面控制。

  3. Exploit的结构:

    Exploit 最终是需要执行shellcode 的,但Exploit 中并不仅仅是shellcode。Exploit要达到攻击目标,要做的工作更多,比如对应的 触发漏洞、将控制权转移到 shellcode 的指令一般均不相同,而且这些语句通常独立于shellcode 的代码。这些能实现特定目标的Exploit的有效载荷,称为Payload

总的来说:Exploit 是指利用漏洞进行攻击的动作;Shellcode 用来实现具体的功能; Payload 除了包含 shellcode 之外,还需要考虑如何触发漏洞并让系统或者程序去执行 shellcode。

5.1.2 覆盖邻接变量示例
5.1.3 代码植入示例
  1. 植入前:弄清楚程序有几个输入点,这些输入将最终会当作哪个函数的第几个参数读入到内存的那一个区域,哪一个输入会造成栈溢出,在复制到栈区的时候对这些数据有没有额外的限制等。

  2. 弹出MessagBox窗体

    1. 加载user32.dll

    2. 获取函数地址

    3. 压入参数

代码植入流程

  1. 获取函数入口地址:

    1. Dependency Walker

    2. 使用代码获取相关函数地址

  2. 编写函数调用汇编代码

  3. 注入shellcode代码

5.2 Shellcode 编写

除了手动编写 shellcode,可以利用 metasploit 框架下的 msfvenom 生成 shellcode,还有一些工具有助于获取shellcode,比如cobaltstrike等。

5.2.1 提取 Shellcode 代码

shellcode必须是机器码,如何得到是一个技术

  1. 使用C语言书写要执行的shellcode

  2. 转换成对应的汇编代码

  3. 根据汇编代码,找到对应地址中的机器码

5.2.2 Shellcode 编码

Shellcode 代码编制过程通常需要进行编码,因为:

  1. 字符集的差异。应用程序应用 平台的不同,可能的字符集会有差异,限制exploit的稳定性。

  2. 绕过坏字符。针对某个 应用,可能对某些“坏字符”变形或者截断而破坏exploit,比如strcpy函数对NULL字符 的不可接纳性,再比如很多应用在某些处理流程中可能会限制 0x0D(\r)、 0x0A(\n)或者 0x20(空格)字符。

  3. 绕过安全防护检测。有很多安全检测工具是根据漏洞相应的exploit 脚本特征做的检测,所以变形exploit在一定程度上可以“免杀”

shellcode编码方式

  1. 网页的shellcode,使用base64进行编码

  2. 二进制的可以采用加壳的思想,利用自定义编码的方法完成shellcode的编码,精心构造解码程序。如异或编码

异或编码:

==课本上重要例子==

5.3 Windows安全防护

  • 由于C、C++等高级程序语言在边界检查方面存在的不足,致使缓冲区溢出漏洞等多种软 件漏洞已成为信息系统安全的主要威胁之一,尤其对于使用广泛的Windwos操作系统及其应 用程序造成了极大的危害。

  • 为了能在操作系统层面提供对软件漏洞的防范,Windows 操作系 统自Vista版本开始,陆续提供了多种防范措施和手段,对于提高Windows操作系统抵御漏洞攻击起到了关键作用。

5.3.1 ASLR(地址空间分布随机化)
  • 地址空间分布随机化 ASLR(addressspace layout randomization)是一项通过将系统关 键地址随机化,从而使攻击者无法获得需要跳转的精确地址的技术。

  • Shellcode 需要调用一 些系统函数才能实现系统功能达到攻击目的,因为这些函数的地址往往是系统 DLL(如 kernel32. Dll)、可执行文件本身、栈数据或PEB(Process Environment Block,进程环境 块)中的固定调用地址,所以为shellcode的调用提供了方便。

1. 系统加载地址变化

使用 ASLR 技术的目的就是打乱系统中存在的固定地址,使攻击者很难从进程的内存空间中找到稳定的跳转地址。ASLR 随机化的关键系统地址包括: PE 文件(exe 文件和dll文件)映像加载地址、堆栈基址、堆地址、PEB和TEB(Thread Environment Block,线程环境块)地址等。

2. 编译器选项-DYNAMICBASE

VS 2005 及更高版本提供了选项/DYNAMICBASE,使用了该选项之后,编译后的程序每次运行时,其内部的栈等结构的地址都会被随机化。

5.3.2 GS Stack protection

编译器针对函数调用和返回时添加保护和检查功能的代码,在函数被调用时, 在缓冲区和函数返回地址增加一个32位的随机数security_cookie,在函数返回时,调用检 查函数检查security_cookie的值是否有变化。

5.3.3 DEP

数据执行保护DEP(data execute prevention)技术可以限制内存堆栈区的代码为不可 执行状态,从而防范溢出后代码的执行

  • 启用DEP机制后,DEP机制 将这些敏感区域设置不可执行的non-executable标志位

1. 软件DER
2. 硬件DEP
  • 需要CPU支持,需要CPU在页表增加一个保护位NX

5.3.4 SafeSEH
  • SEH(Structured Exception Handler)是 Windows 异常处理机制所采用的重要数据结构 链表。

  • 程序设计者可以根据自身需要,定义程序发生各种异常时相应的处理函数,保存在SEH 中。

  • 通过精心构造,攻击者通过缓冲区溢出覆盖SEH中异常处理函数句柄,将其替换为指向 恶意代码shellcode的地址,并触发相应异常,从而使程序流程转向执行恶意代码。

  • SafeSEH 就是一项保护 SEH 函数不被非法利用的技术。微软在编译器中加入了/SafeSEH 选项,采用该选项编译的程序将PE文件中所有合法的SEH异常处理函数的地址解析出来制成 一张SEH函数表,放在PE文件的数据块中,用于异常处理时候进行匹配检查。

原理:

  • 在该PE文件被加载时,系统读出该SEH函数表的地址,使用内存中的一个随机数加密, 将加密后的SEH函数表地址、模块的基址、模块的大小、合法SEH函数的个数等信息,放入 ntdll.dll 的 SEHIndex 结构中。

  • 在 PE 文件运行中,如果需要调用异常处理函数,系统会调用加解密函数解密从而获得SEH函数表地址,

    • 然后针对程序的每个异常处理函数检查是否在合法的SEH函数表中,如果没有则说明该函数非法,将终止异常处理。

    • 接着要检查异常处理句柄是否在栈上,如果在栈上也将停止异常处理。

  • 这两个检测可以防止在堆上伪造异常链和 把shellcode 放置在栈上的情况,最后还要检测异常处理函数句柄的有效性。 从Vista开始,由于系统PE文件在编译时都采用SafeSEH编译选项,因此以前那种通过 覆盖异常处理句柄的漏洞利用技术,也就不能正常使用了。

5.3.5 SEHOP

结构化异常处理覆盖保护 SEHOP(Structured Exception Handler Overwrite Protection)是微软针对 SEH 攻击提出的一种安全防护方案。SEH 攻击是指通过栈溢出或者 其他漏洞,使用精心构造的数据覆盖SEH上面的某个函数或者多个函数,从而控制EIP(控制程序执行流程)。

工作原理

  1. SEH扩展的方式提供的,通过对程序中使用的SEH结构进行一些安全检测,来判断应用 程序是否受到了SEH攻击

  2. SEHOP的核心是检测程序栈中的所有SEH结构链表的完整性,SEHOP 针对下列条件进行检测,包括

    1. SEH结构都必须在栈上,最后一个SEH结构也必须在栈上;

    2. 所 有的SEH结构都必须是4字节对齐的;

    3. SEH结构中异常处理函数的句柄handle(即处理函数 地址)必须不在栈上;

    4. 最后一个 SEH 结构的 handle 必须是 ntdll!FinalExceptionHandler 函数,

    5. 最后一个SEH结构的next seh指针必须为特定值0xFFFFFFFF等。

  3. 当进行异常处理时,由系统接管进行异常处理,因此SEHOP由系统独立来完成,应用程 序不用做任何改变,只需要在操作系统中开启SEHOP防护功能即可。

5.4 漏洞利用技术进阶

5.4.1 地址定位技术
1. 静态shellcode地址的利用技术
  • 如果存在溢出漏洞的程序,是一个操作系统每次启动都要加载的程序,操作系统启动时为其分配的内存地址一般是固定的,则函数调用时分配的栈帧地址也是固定的。

  • 溢出后写入栈帧的shellcode 代码其内存地址也是静态不变的,所以可以直接将shellcode 代码在栈帧中的静态地址覆盖原有返回地址。

  • 在函数返回时,通过新的返回地址指向 shellcode 代码地址,从而执行shellcode 代码。在 shellcode 为静态地址时,缓冲区溢出 前后内存中栈帧的变化示意图参见下图。

2. 基于跳板指令的地址定位技术
  • 有些软件的漏洞存在于某些动态链接库中,这些动态链接库在进程运行时被动态加载, 因而在下一次这些动态链接库被重新装载到内存中,其在内存中的栈帧地址是动态变化的, 则植入的shellcode代码在内存中的起始地址也是变化的。

  • 此外,如果在使用 ASLR 技术的操作系统中,地址会因为引入的随机数每次发生变化。 在这种情况下,需要让溢出发生时,覆盖返回地址后新写入的返回地址能够自动定位到 shellcode 的起始地址。

  • 可以利用esp寄存器的特性实现。函数调用结束后,被调用函数的栈帧被释放,esp 寄存器中的栈顶指针此时指向返回地址在内存高地址方向的相邻位置, 不管有无溢出发生esp都是这种特性。

定位步骤如下:

  1. 找到内存中任意一个汇编指令jmp esp,这条指令执行后可跳转到esp寄存器 保存的地址,下面准备在溢出后将这条指令的地址覆盖返回地址。

  2. 设计好缓冲区溢出漏洞利用程序中的输入数据,使缓冲区溢出后,前面的填充内容为任意数据,紧接着覆盖返回地址的是jmp esp指令的地址,再接着覆盖与返回地址相 邻的高地址位置并写入shellcode代码。

  3. 函数调用完成后函数返回,根据返回地址中指向的jmp esp指令的地址去执行 jmp esp 操作,即跳转到esp寄存器中保存的地址,而函数返回后esp中保存的地址是与返 回地址相邻的高地址位置,在这个位置保存的是shellcode代码,则shellcode代码被执行。

3. 内存喷洒技术

内存喷射技术的代表是堆喷洒Heap spray,也称为堆喷洒技术,

  • 在shellcode的前面 加上大量的滑板指令(slide code),组成一个非常长的注入代码段。然后向系统申请大量内存,并且反复用这个注入代码段来填充。这样就使得内存空间被大量的注入代码所占据。攻 击者再结合漏洞利用技术,只要使程序跳转到堆中被填充了注入代码的任何一个地址,程序指令就会顺着滑板指令最终执行到shellcode代码。

滑板指令:

  • 滑板指令是由大量的NOP空指令0x90填充组成的指令序列,当遇到这些NOP指令的时候

  • CPU指令指针会一个指令接一个指令的执行下去,中间不做任何具 体操作,直到“滑”过最后一个滑板指令后,接着执行这些指令后面的其他指令,往往后面接着的是shellcode 代码。

  • Shellcode 的正常执行,需要从shellcode 的第一条指令开始。

  • 前面加上滑板指令之后,程序跳转后只要命中滑板指令中的任何一个,就可以保证它后面接 着的shellcode 能成功执行。

  • 滑板指令除了利用NOP指令填 充外,也逐渐开始使用更多的类NOP指令,譬如0x0C、0x0D等。

Heap Spray

  • Heap Spray 用于针对浏览器漏洞的攻击较多,尤其是网页木马应用较多。Heap Spray 技术通过使用类NOP指令来进行覆盖,对shellcode地址的跳转准确性要求不高了,从而增 加了缓冲区溢出攻击的成功率。然而,Heap Spray会导致被攻击进程的内存占用非常大,计算机无法正常运转,因而容易被察觉。

  • 它一般配合堆栈溢出攻击,不能用于主动攻击,也不能保证成功。针对Heap Spray,对于windows系统比较好的系统防范办法是开启DEP功能, 即使被绕过,被利用的概率也会大大降低。

5.4.2 API函数自搜索技术
1. 通用型Shellcode的编写逻辑
  1. 总结需要用到的函数,以及其所在的dll中

  2. 定位dll文件的位置(难)

  3. 定位dll文件的导出表

  4. 搜索需要定位的目标函数(难)

  5. 找到函数的地址,完成shellcode的编写

2. API函数自搜索技术
  1. 定位 kernel32.dll

    1. 首先通过段选择字FS在内存中找到当前的线程环境块TEB

    2. 线程环境块偏移地址为0x30的地址存放着指向进程环境块PEB的指针。

    3. 进程环境块中偏移地址为0x0c的地方存放着指向PEB_LDR_DATA结构体的指针,其 中,存放着已经被进程装载的动态链接库的信息。

    4. PEB_LDR_DATA 结构体偏移位置为 0x1C 的地址存放着指向模块初始化链表的头指针 InInitializationOrderModuleList。

    5. 模块初始化链表InInitializationOrderModuleList中按顺序存放着PE装入运行时 初始化模块的信息,第一个链表结点是ntdll.dll,第二个链表结点就是kernel32.dll。

    6. 找到属于kernel32.dll的结点后,在其基础上再偏移0x08就是kernel32.dll在内 存中的加载基地址。

  2. 定位 kernel32.dll 的导出表

    1. 从kernel32.dll 加载基址算起,偏移0x3c的地方就是其PE头的指针。

    2. PE 头偏移0x78的地方存放着指向函数导出表的指针。

    3. 获得导出函数偏移地址(RVA)列表、导出函数名列表:

      • 导出表偏移0x1c处的指针指向存储导出函数偏移地址(RVA)的列表。

      • 导出表偏移0x20处的指针指向存储导出函数函数名的列表。

  3. 搜索定位目标函数:遍历两个函数相关的列表,获得所需函数的入口地址

    • 函数的 RVA 地址和名字按照顺序存放在上述两个列表中,我们可以在名称列表中 定位到所需的函数是第几个,然后在地址列表中找到对应的RVA。

    • 获得 RVA 后,再加上前边已经得到的动态链接库的加载地址,就获得了所需 API 此刻在内存中的虚拟地址,这个地址就是最终在ShellCode中调用时需要的地址。

  1. <img alt="" src="https://i-blog.csdnimg.cn/direct/1fa734ba15714ad4b1b50fadaf6ee48e.png" />
复制代码
5.4.3 返回导向编程

DEP 技术可以限制内存堆栈区的代码为不可执行状态,从而防范溢出后代码的执行,已 经成为Windows的重要保护措施,但是它依然可以被绕过。

  • 支持硬件DEP的CPU会拒绝被标记为不可执行的(NX)内存页的代码,目的是防止攻击者将恶意代码注入的另一个程序执行。尤其是基于栈溢出漏洞。

  • 由于DEP 栈上的shellcode 将不会被执行。这对漏洞利用意味着什么?当我们尝试在启用DEP的内存 执行代码,程序将会返回一个访问冲突“STATUS_ACCESS_VIOLATION (0xc0000005) ”

1. ROP Return-oriented programming(返回导向编程)基本思想

ROP 基本的思想是借助已经存在的代码块(也叫配件,Gadget),这些配件来自程序已经加载的模块。我们可以在已加载的模块中找到一些以retn结尾的配件,把这些配件的地址布 置在堆栈上,当控制EIP并返回时候,程序就会跳去执行这些小配件,而这些小配件是在别 的模块代码段,不受DEP的影响。

  1. ROP 通过ROP链(retn)实现有序汇编指令的执行

  2. ROP 链由一个个ROP小配件(Gadget,相当于一个小节点)组成。

  3. ROP 小配件由“目的执行指令+retn指令组成”

可以达到的作用:

  1. 调用相关API关闭或绕过DEP保护

  2. 实现地址跳转

  3. 调用相关API将shellcode写入不受DEP保护的可执行内存

5.4.4 绕过其他安全防护
1. 绕过GS安全机制

GS安全机制

  • 增加cookie

  • 对栈中变量排序:将字符串缓冲区分配在栈帧的最高地址上,因此,当字符串缓冲区溢出,就不 能覆盖本地变量了

有一部分函数没有得到保护,比如:结构成员因为互操作性问题不能重新排列,因此包含缓冲区的时候,缓冲区溢出就可以将之后的成员覆盖控制

2. ASLR缺陷和绕过方法

脆弱性

  1. 为了减少虚拟地址空间的碎片,操作系统把随机加载库文件的地址限制为8位, 即地址空间为 256,而且随机化发生在地址前两个最有意义的字节上;

  2. 很多应用程序和 DLL 模块并没有采用/DYNAMICBASE的编译选项;

  3. 很多应用程序使用相同的系统DLL文件, 这些系统DLL加载后地址就确定下来了,对于本地攻击,攻击者还是很容易就能获得所需要 的地址,然后进行攻击。

通过攻击未开启地址随机化的模块(作为跳板)、堆喷洒技术、部分返回地址覆盖法等

返回地址覆盖法

  • 在ASLR中,虽然模块加载基地址发生变 化,但是各模块的入口点地址的低字节不变,只有高位变化。

  • 对于地址0x12345678,其中5678 部分是固定的,如果存在缓冲区溢出,可以通过memcpy对后两个字节进行覆盖,可以将其设 置为0x12340000~0x1234FFFF 中的任意一个值。

  • 如果通过strcpy进行覆盖,因为strcpy会 复制末尾的结束符0x00,那么可以将 0x12345678 覆盖为 0x12345600,或者 0x12340001 ~ 0x123400FF。部分返回地址覆盖,可以使得覆盖后的地址相对于基地址的距离是固定的,可以从基地址附近找可以利用的跳转指令。

3. SEH保护机制缺陷和绕过方法
  • SafeSEH 是一种非常有效的漏洞利用防护机制,如果一个进程加载的所有模板都采用 /SafeSEH 编译后的 PE 文件,覆盖SafeSEH 获得漏洞利用就基本不可能。

  • 当一个进程中存在 一个不是/SafeSEH 编译的 DLL 或者库文件的时候,整个 SafeSEH 机制就可能失效。因为 /SafeSEH 编译选项需要.NET的编译器支持,现在仍有大量第三方库和程序没有使用该编译器 编译或者没有启动/SafeSEH选项。

绕过方法

  1. 利用未开启SafeSEH的模板作为跳板绕过

  2. 利用加载模块之外的地址进行绕过

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

中国红客联盟公众号

联系站长QQ:5520533

admin@chnhonker.com
Copyright © 2001-2026 Discuz Team. Powered by Discuz! X3.5 ( 粤ICP备13060014号 )|天天打卡 本站已运行