RSS Security

🔒
❌ About FreshRSS
There are new articles available, click to refresh the page.
Before yesterdayMcAfee Blogs

Fuzzing ImageMagick and Digging Deeper into CVE-2020-27829

30 June 2021 at 15:00

Introduction:

ImageMagick is a hugely popular open source software that is used in lot of systems around the world. It is available for the Windows, Linux, MacOS platforms as well as Android and iOS. It is used for editing, creating or converting various digital image formats and supports various formats like PNG, JPEG, WEBP, TIFF, HEIC and PDF, among others.

Google OSS Fuzz and other threat researchers have made ImageMagick the frequent focus of fuzzing, an extremely popular technique used by security researchers to discover potential zero-day vulnerabilities in open, as well as closed source software. This research has resulted in various vulnerability discoveries that must be addressed on a regular basis by its maintainers. Despite the efforts of many to expose such vulnerabilities, recent fuzzing research from McAfee has exposed new vulnerabilities involving processing of multiple image formats, in various open source and closed source software and libraries including ImageMagick and Windows GDI+.

Fuzzing ImageMagick:

Fuzzing open source libraries has been covered in a detailed blog “Vulnerability Discovery in Open Source Libraries Part 1: Tools of the Trade” last year. Fuzzing ImageMagick is very well documented, so we will be quickly covering the process in this blog post and will focus on the root cause analysis of the issue we have found.

Compiling ImageMagick with AFL:

ImageMagick has lot of configuration options which we can see by running following command:

$./configure –help

We can customize various parameters as per our needs. To compile and install ImageMagick with AFL for our case, we can use following commands:

$CC=afl-gcc CXX=afl=g++ CFLAGS=”-ggdb -O0 -fsanitize=address,undefined -fno-omit-frame-pointer” LDFLAGS=”-ggdb -fsanitize=address,undefined -fno-omit-frame-pointer” ./configure

$ make -j$(nproc)

$sudo make install

This will compile and install ImageMagick with AFL instrumentation. The binary we will be fuzzing is “magick”, also known as “magick tool”. It has various options, but we will be using its image conversion feature to convert our image from one format to another.

A simple command would be include the following:

$ magick <input file> <output file>

This command will convert an input file to an output file format. We will be fuzzing this with AFL.

Collecting Corpus:

Before we start fuzzing, we need to have a good input corpus. One way of collecting corpus is to search on Google or GitHub. We can also use existing test corpus from various software. A good test corpus is available on the  AFL site here: https://lcamtuf.coredump.cx/afl/demo/

Minimizing Corpus:

Corpus collection is one thing, but we also need to minimize the corpus. The way AFL works is that it will instrument each basic block so that it can trace the program execution path. It maintains a shared memory as a bitmap and it uses an algorithm to check new block hits. If a new block hit has been found, it will save this information to bitmap.

Now it may be possible that more than one input file from the corpus can trigger the same path, as we have collected sample files from various sources, we don’t have any information on what paths they will trigger at the runtime. If we use this corpus without removing such files, then we end up wasting time and CPU cycles. We need to avoid that.

Interestingly AFL offers a utility called “afl-cmin” which we can use to minimize our test corpus. This is a recommended thing to do before you start any fuzzing campaign. We can run this as follows:

$afl-cmin -i <input directory> -o <output directory> — magick @@ /dev/null

This command will minimize the input corpus and will keep only those files which trigger unique paths.

Running Fuzzers:

After we have minimized corpus, we can start fuzzing. To fuzz we need to use following command:

$afl-fuzz -i <mincorpus directory> -o <output directory> — magick @@ /dev/null

This will only run a single instance of AFL utilizing a single core. In case we have multicore processors, we can run multiple instances of AFL, with one Master and n number of Slaves. Where n is the available CPU cores.

To check available CPU cores, we can use this command:

$nproc

This will give us the number of CPU cores (depending on the system) as follows:

In this case there are eight cores. So, we can run one Master and up to seven Slaves.

To run master instances, we can use following command:

$afl-fuzz -M Master -i <mincorpus directory> -o <output directory> — magick @@ /dev/null

We can run slave instances using following command:

$afl-fuzz -S Slave1 -i <mincorpus directory> -o <output directory> — magick @@ /dev/null

$afl-fuzz -S Slave2 -i <mincorpus directory> -o <output directory> — magick @@ /dev/null

The same can be done for each slave. We just need to use an argument -S and can use any name like slave1, slave2, etc.

Results:

Within a few hours of beginning this Fuzzing campaign, we found one crash related to an out of bound read inside a heap memory. We have reported this issue to ImageMagick, and they were very prompt in fixing it with a patch the very next day. ImageMagick has release a new build with version: 7.0.46 to fix this issue. This issue was assigned CVE-2020-27829.

Analyzing CVE-2020-27829:

On checking the POC file, we found that it was a TIFF file.

When we open this file with ImageMagick with following command:

$magick poc.tif /dev/null

As a result, we see a crash like below:

As is clear from the above log, the program was trying to read 1 byte past allocated heap buffer and therefore ASAN caused this crash. This can atleast lead to a  ImageMagick crash on the systems running vulnerable version of ImageMagick.

Understanding TIFF file format:

Before we start debugging this issue to find a root cause, it is necessary to understand the TIFF file format. Its specification is very well described here: http://paulbourke.net/dataformats/tiff/tiff_summary.pdf.

In short, a TIFF file has three parts:

  1. Image File Header (IFH) – Contains information such as file identifier, version, offset of IFD.
  2. Image File Directory (IFD) – Contains information on the height, width, and depth of the image, the number of colour planes, etc. It also contains various TAGs like colormap, page number, BitPerSample, FillOrder,
  3. Bitmap data – Contains various image data like strips, tiles, etc.

We can tiffinfo utility from libtiff to gather various information about the POC file. This allows us to see the following information with tiffinfo like width, height, sample per pixel, row per strip etc.:

There are a few things to note here:

TIFF Dir offset is: 0xa0

Image width is: 3 and length is: 32

Bits per sample is: 9

Sample per pixel is: 3

Rows per strip is: 1024

Planer configuration is: single image plane.

We will be using this data moving forward in this post.

Debugging the issue:

As we can see in the crash log, program was crashing at function “PushQuantumPixel” in the following location in quantum-import.c line 256:

On checking “PushQuantumPixel” function in “MagickCore/quantum-import.c” we can see the following code at line #256 where program is crashing:

We can see following:

  • “pixels” seems to be a character array
  • inside a for loop its value is being read and it is being assigned to quantum_info->state.pixel
  • its address is increased by one in each loop iteration

The program is crashing at this location while reading the value of “pixels” which means that value is out of bound from the allocated heap memory.

Now we need to figure out following:

  1. What is “pixels” and what data it contains?
  2. Why it is crashing?
  3. How this was fixed?

Finding root cause:

To start with, we can check “ReadTIFFImage” function in coders/tiff.c file and see that it allocates memory using a “AcquireQuantumMemory” function call, which appears as per the documentation mentioned here:

https://imagemagick.org/api/memory.php:

“Returns a pointer to a block of memory at least count * quantum bytes suitably aligned for any use.

The format of the “AcquireQuantumMemory” method is:

void *AcquireQuantumMemory(const size_t count,const size_t quantum)

A description of each parameter follows:

count

the number of objects to allocate contiguously.

quantum

the size (in bytes) of each object. “

In this case two parameters passed to this function are “extent” and “sizeof(*strip_pixels)”

We can see that “extent” is calculated as following in the code below:

There is a function TIFFStripSize(tiff) which returns size for a strip of data as mentioned in libtiff documentation here:

http://www.libtiff.org/man/TIFFstrip.3t.html

In our case, it returns 224 and we can also see that in the code mentioned above,  “image->columns * sizeof(uint64)” is also added to extent, which results in 24 added to extent, so extent value becomes 248.

So, this extent value of 248 and sizeof(*strip_pixels) which is 1 is passed to “AcquireQuantumMemory” function and total memory of 248 bytes get allocated.

This is how memory is allocated.

“Strip_pixel” is pointer to newly allocated memory.

Note that this is 248 bytes of newly allocated memory. Since we are using ASAN, each byte will contain “0xbe” which is default for newly allocated memory by ASAN:

https://github.com/llvm-mirror/compiler-rt/blob/master/lib/asan/asan_flags.inc

The memory start location is 0x6110000002c0 and the end location is 0x6110000003b7, which is 248 bytes total.

This memory is set to 0 by a “memset” call and this is assigned to a variable “p”, as mentioned in below image. Please also note that “p” will be used as a pointer to traverse this memory location going forward in the program:

Later on we see that there is a call to “TIFFReadEncodedPixels” which reads strip data from TIFF file and stores it into newly allocated buffer “strip_pixels” of 248 bytes (documentation here: http://www.libtiff.org/man/TIFFReadEncodedStrip.3t.html):

To understand what this TIFF file data is, we need to again refer to TIFF file structure. We can see that there is a tag called “StripOffsets” and its value is 8, which specifies the offset of strip data inside TIFF file:

We see the following when we check data at offset 8 in the TIFF file:

We see the following when we print the data in “strip_pixels” (note that it is in little endian format):

So “strip_pixels” is the actual data from the TIFF file from offset 8. This will be traversed through pointer “p”.

Inside “ReadTIFFImage” function there are two nested for loops.

  • The first “for loop” is responsible for iterating for “samples_per_pixel” time which is 3.
  • The second “for loop” is responsible for iterating the pixel data for “image->rows” times, which is 32. This second loop will be executed for 32 times or number of rows in the image irrespective of allocated buffer size .
  • Inside this second for loop, we can see something like this:

  • We can notice that “ImportQuantumPixel” function uses the “p” pointer to read the data from “strip_pixels” and after each call to “ImportQuantumPixel”, value of “p” will be increased by “stride”.

Here “stride” is calculated by calling function “TIFFVStripSize()” function which as per documentation returns the number of bytes in a strip with nrows rows of data.  In this case it is 14. So, every time pointer “p” is incremented by “14” or “0xE” inside the second for loop.

If we print the image structure which is passed to “ImportQuantumPixels” function as parameter, we can see following:

Here we can notice that the columns value is 3, the rows value is 32 and depth is 9. If we check in the POC TIFF file, this has been taken from ImageWidth and ImageLength and BitsPerSample value:

Ultimately, control reaches to “ImportRGBQuantum” and then to the “PushQuantumPixel” function and one of the arguments to this function is the pixels data which is pointed by “p”. Remember that this points to the memory address which was previously allocated using the “AcquireQuantumMemory” function, and that its length is 248 byte and every time value of “p” is increased by 14.

The “PushQuantumPixel” function is used to read pixel data from “p” into the internal pixel data storage of ImageMagick. There is a for loop which is responsible for reading data from the provided pixels array of 248 bytes into a structure “quantum_Info”. This loop reads data from pixels incrementally and saves it in the “quantum_info->state.pixels” field.

The root cause here is that there are no proper bounds checks and the program tries to read data beyond the allocated buffer size on the heap, while reading the strip data inside a for loop.

This causes a crash in ImageMagick as we can see below:

Root cause

Therefore, to summarize, the program crashes because:

  1. The program allocates 248 bytes of memory to process strip data for image, a pointer “p” points to this memory.
  2. Inside a for loop this pointer is increased by “14” or “0xE” for number of rows in the image, which in this case is 32.
  3. Based on this calculation, 32*14=448 bytes or more amount of memory is required but only 248 in actual memory were allocated.
  4. The program tries to read data assuming total memory is of 448+ bytes, but the fact that only 248 bytes are available causes an Out of Bound memory read issue.

How it was fixed?

If we check at the patch diff, we can see that the following changes were made to fix this issue:

Here the 2nd argument to “AcquireQuantumMemory” is multiplied by 2 thus increasing the total amount of memory and preventing this Out of Bound read issue from heap memory. The total memory allocated is 496 bytes, 248*2=496 bytes, as we can see below:

Another issue with the fix:

A new version of ImageMagick 7.0.46 was released to fix this issue. While the patch fixes the memory allocation issue, if we check the code below, we can see that there was a call to memset which didn’t set the proper memory size to zero.

Memory was allocated extent*2*sizeof(*strip_pixels) but in this memset to 0 was only done for extent*sizeof(*strip_pixels). This means half of the memory was set to 0 and rest contained 0xbebebebe, which is by default for ASAN new memory allocation.

This has since been fixed in subsequent releases of ImageMagick by using extent=2*TIFFStripSize(tiff); in the following patch:

https://github.com/ImageMagick/ImageMagick/commit/a5b64ccc422615264287028fe6bea8a131043b59#diff-0a5eef63b187504ff513056aa8fd6a7f5c1f57b6d2577a75cff428c0c7530978

Conclusion:

Processing various image files requires deep understanding of various file formats and thus it is possible that something may not be exactly implemented or missed. This can lead to various vulnerabilities in such image processing software. Some of this vulnerability can lead to DoS and some can lead to remote code execution affecting every installation of such popular software.

Fuzzing plays an important role in finding vulnerabilities often missed by developers and during testing. We at McAfee constantly fuzz various closed source as well as open source software to help secure them. We work very closely with various vendors and do responsible disclosure. This shows McAfee’s commitment towards securing the software and protecting our customers from various threats.

We will continue to fuzz various software and work with vendors to help mitigate risk arriving from such threats.

We would like to thank and appreciate ImageMagick team for quickly resolving this issue within 24 hours and releasing a new version to fix this issue.

The post Fuzzing ImageMagick and Digging Deeper into CVE-2020-27829 appeared first on McAfee Blogs.

Analyzing CVE-2021-1665 – Remote Code Execution Vulnerability in Windows GDI+

28 June 2021 at 19:44
Consejos para protegerte de quienes intentan hackear tus correos electrónicos

Introduction

Microsoft Windows Graphics Device Interface+, also known as GDI+, allows various applications to use different graphics functionality on video displays as well as printers. Windows applications don’t directly access graphics hardware such as device drivers, but they interact with GDI, which in turn then interacts with device drivers. In this way, there is an abstraction layer to Windows applications and a common set of APIs for everyone to use.

Because of its complex format, GDI+ has a known history of various vulnerabilities. We at McAfee continuously fuzz various open source and closed source software including windows GDI+. Over the last few years, we have reported various issues to Microsoft in various Windows components including GDI+ and have received CVEs for them.

In this post, we detail our root cause analysis of one such vulnerability which we found using WinAFL: CVE-2021-1665 – GDI+ Remote Code Execution Vulnerability.  This issue was fixed in January 2021 as part of a Microsoft Patch.

What is WinAFL?

WinAFL is a Windows port of a popular Linux AFL fuzzer and is maintained by Ivan Fratric of Google Project Zero. WinAFL uses dynamic binary instrumentation using DynamoRIO and it requires a program called as a harness. A harness is nothing but a simple program which calls the APIs we want to fuzz.

A simple harness for this was already provided with WinAFL, we can enable “Image->GetThumbnailImage” code which was commented by default in the code. Following is the harness code to fuzz GDI+ image and GetThumbnailImage API:

 

As you can see, this small piece of code simply creates a new image object from the provided input file and then calls another function to generate a thumbnail image. This makes for an excellent attack vector and can affect various Windows applications if they use thumbnail images. In addition, this requires little user interaction, thus software which uses GDI+ and calls GetThumbnailImage API, is vulnerable.

Collecting Corpus:

A good corpus provides a sound foundation for fuzzing. For that we can use Google or GitHub in addition to further test corpus available from various software and public EMF files which were released for other vulnerabilities. We have generated a few test files by making changes to a sample code provided on Microsoft’s site which generates an EMF file with EMFPlusDrawString and other records:

Ref: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-emfplus/07bda2af-7a5d-4c0b-b996-30326a41fa57

Minimizing Corpus:

After we have collected an initial corpus file, we need to minimize it. For this we can use a utility called winafl-cmin.py as follows:

winafl-cmin.py -D D:\\work\\winafl\\DynamoRIO\\bin32 -t 10000 -i inCorpus -o minCorpus -covtype edge -coverage_module gdiplus.dll -target_module gdiplus_hardik.exe -target_method fuzzMe -nargs 2 — gdiplus_hardik.exe @@

How does WinAFL work?

WinAFL uses the concept of in-memory fuzzing. We need to provide a function name to WinAFL. It will save the program state at the start of the function and take one input file from the corpus, mutate it, and feed it to the function.

It will monitor this for any new code paths or crashes. If it finds a new code path, it will consider the new file as an interesting test case and will add it to the queue for further mutation. If it finds any crashes, it will save the crashing file in crashes folder.

The following picture shows the fuzzing flow:

Fuzzing with WinAFL:

Once we have compiled our harness program, collected, and minimized the corpus, we can run this command to fuzz our program with WinAFL:

afl-fuzz.exe -i minCorpus -o out -D D:\work\winafl\DynamoRIO\bin32 -t 20000 —coverage_module gdiplus.dll -fuzz_iterations 5000 -target_module gdiplus_hardik.exe -target_offset 0x16e0 -nargs 2 — gdiplus_hardik.exe @@

Results:

We found a few crashes and after triaging unique crashes, and we found a crash in “gdiplus!BuiltLine::GetBaselineOffset” which looks as follows in the call stack below:

As can be seen in the above image, the program is crashing while trying to read data from a memory address pointed by edx+8. We can see it registers ebx, ecx and edx contains c0c0c0c0 which means that page heap is enabled for the binary. We can also see that c0c0c0c0 is being passed as a parameter to “gdiplus!FullTextImager::RenderLine” function.

Patch Diffing to See If We Can Find the Root Cause

To figure out a root cause, we can use patch diffing—namely, we can use IDA BinDiff plugin to identify what changes have been made to patched file. If we are lucky, we can easily find the root cause by just looking at the code that was changed. So, we can generate an IDB file of patched and unpatched versions of gdiplus.dll and then run IDA BinDiff plugin to see the changes.

We can see that one new function was added in the patched file, and this seems to be a destructor for BuiltLine Object :

We can also see that there are a few functions where the similarity score is < 1 and one such function is FullTextImager::BuildAllLines as shown below:

Now, just to confirm if this function is really the one which was patched, we can run our test program and POC in windbg and set a break point on this function. We can see that the breakpoint is hit and the program doesn’t crash anymore:

Now, as a next step, we need to identify what has been changed in this function to fix this vulnerability. For that we can check flow graph of this function and we see something as follows. Unfortunately, there are too many changes to identify the vulnerability by simply looking at the diff:

The left side illustrates an unpatched dll while right side shows a patched dll:

  • Green indicates that the patched and unpatched blocks are same.
  • Yellow blocks indicate there has been some changes between unpatched and patched dlls.
  • Red blocks call out differences in the dlls.

If we zoom in on the yellow blocks we can see following:

We can note several changes. Few blocks are removed in the patched DLL, so patch diffing will alone will not be sufficient to identify the root cause of this issue. However, this presents valuable hints about where to look and what to look for when using other methods for debugging such as windbg. A few observations we can spot from the bindiff output above:

  • In the unpatched DLL, if we check carefully we can see that there is a call to “GetuntrimmedCharacterCount” function and later on there is another call to a function “SetSpan::SpanVector
  • In the patched DLL, we can see that there is a call to “GetuntrimmedCharacterCount” where a return value stored inside EAX register is checked. If it’s zero, then control jumps to another location—a destructor for BuiltLine Object, this was newly added code in the patched DLL:

So we can assume that this is where the vulnerability is fixed. Now we need to figure out following:

  1. Why our program is crashing with the provided POC file?
  2. What field in the file is causing this crash?
  3. What value of the field?
  4. Which condition in program which is causing this crash?
  5. How this was fixed?

EMF File Format:

EMF is also known as enhanced meta file format which is used to store graphical images device independently. An EMF file is consisting of various records which is of variable length. It can contain definition of various graphic object, commands for drawing and other graphics properties.

Credit: MS EMF documentation.

Generally, an EMF file consist of the following records:

  1. EMF Header – This contains information about EMF structure.
  2. EMF Records – This can be various variable length records, containing information about graphics properties, drawing order, and so forth.
  3. EMF EOF Record – This is the last record in EMF file.

Detailed specifications of EMF file format can be seen at Microsoft site at following URL:

https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-emf/91c257d7-c39d-4a36-9b1f-63e3f73d30ca

Locating the Vulnerable Record in the EMF File:

Generally, most of the issues in EMF are because of malformed or corrupt records. We need to figure out which record type is causing this crash. For this if we look at the call stack we can see following:

We can notice a call to function “gdiplus!GdipPlayMetafileRecordCallback

By setting a breakpoint on this function and checking parameter, we can see following:

We can see that EDX contains some memory address and we can see that parameter given to this function are: 00x00401c,0x00000000 and 0x00000044.

Also, on checking the location pointed by EDX we can see following:

If we check our POC EMF file, we can see that this data belongs to file from offset: 0x15c:

By going through EMF specification and manually parsing the records, we can easily figure out that this is a “EmfPlusDrawString” record, the format of which is shown below:

In our case:

Record Type = 0x401c EmfPlusDrawString record

Flags = 0x0000

Size = 0x50

Data size = 0x44

Brushid = 0x02

Format id = 0x01

Length = 0x14

Layoutrect = 00 00 00 00 00 00 00 00 FC FF C7 42 00 00 80 FF

String data =

Now that we have located the record that seems to be causing the crash, the next thing is to figure out why our program is crashing. If we debug and check the code, we can see that control reaches to a function “gdiplus!FullTextImager::BuildAllLines”. When we decompile this code, we can see something  like this:

The following diagram shows the function call hierarchy:

The execution flow in summary:

  1. Inside “Builtline::BuildAllLines” function, there is a while loop inside which the program allocates 0x60 bytes of memory. Then it calls the “Builtline::BuiltLine”
  2. The “Builtline::BuiltLine” function moves data to the newly allocated memory and then it calls “BuiltLine::GetUntrimmedCharacterCount”.
  3. The return value of “BuiltLine::GetUntrimmedCharacterCount” is added to loop counter, which is ECX. This process will be repeated until the loop counter (ECX) is < string length(EAX), which is 0x14 here.
  4. The loop starts from 0, so it should terminate at 0x13 or it should terminate when the return value of “GetUntrimmedCharacterCount” is 0.
  5. But in the vulnerable DLL, the program doesn’t terminate because of the way loop counter is increased. Here, “BuiltLine::GetUntrimmedCharacterCount” returns 0, which is added to Loop counter(ECX) and doesn’t increase ECX value. It allocates 0x60 bytes of memory and creates another line, corrupting the data that later leads the program to crash. The loop is executed for 21 times instead of 20.

In detail:

1. Inside “Builtline::BuildAllLines” memory will be allocated for 0x60 or 96 bytes, and in the debugger it looks as follows:

2. Then it calls “BuiltLine::BuiltLine” function and moves the data to newly allocated memory:

3. This happens in side a while loop and there is a function call to “BuiltLine::GetUntrimmedCharacterCount”.

4. Return value of “BuiltLine::GetUntrimmedCharacterCount” is stored in a location 0x12ff2ec. This value will be 1 as can be seen below:

5. This value gets added to ECX:

6. Then there is a check that determines if ecx< eax. If true, it will continue loop, else it will jump to another location:

7. Now in the vulnerable version, loop doesn’t exist if the return value of “BuiltLine::GetUntrimmedCharacterCount” is 0, which means that this 0 will be added to ECX and which means ECX will not increase. So the loop will execute 1 more time with the “ECX” value of 0x13. Thus, this will lead to loop getting executed 21 times rather than 20 times. This is the root cause of the problem here.

Also after some debugging, we can figure out why EAX contains 14. It is read from the POC file at offset: 0x174:

If we recall, this is the EmfPlusDrawString record and 0x14 is the length we mentioned before.

Later on, the program reaches to “FullTextImager::Render” function corrupting the value of EAX because it reads the unused memory:

This will be passed as an argument to “FullTextImager::RenderLine” function:

Later, program will crash while trying to access this location.

Our program was crashing while processing EmfPlusDrawString record inside the EMF file while accessing an invalid memory location and processing string data field. Basically, the program was not verifying the return value of “gdiplus!BuiltLine::GetUntrimmedCharacterCount” function and this resulted in taking a different program path that  corrupted the register and various memory values, ultimately causing the crash.

How this issue was fixed?

As we have figured out by looking at patch diff above, a check was added which determined the return value of “gdiplus!BuiltLine::GetUntrimmedCharacterCount” function.

If the retuned value is 0, then program xor’s EBX which contains counter and jump to a location which calls destructor for Builtline Object:

Here is the destructor that prevents the issue:

Conclusion:

GDI+ is a very commonly used Windows component, and a vulnerability like this can affect billions of systems across the globe. We recommend our users to apply proper updates and keep their Windows deployment current.

We at McAfee are continuously fuzzing various open source and closed source library and work with vendors to fix such issues by responsibly disclosing such issues to them giving them proper time to fix the issue and release updates as needed.

We are thankful to Microsoft for working with us on fixing this issue and releasing an update.

 

 

 

 

The post Analyzing CVE-2021-1665 – Remote Code Execution Vulnerability in Windows GDI+ appeared first on McAfee Blogs.

Analyzing and Identifying Issues with the Microsoft Patch for CVE-2018-8423

28 August 2019 at 15:06

Introduction

As of July 2019, Microsoft has fixed around 43 bugs in the Jet Database Engine. McAfee has reported a couple of bugs and, so far, we have received 10 CVE’s from Microsoft. In our previous post, we discussed the root cause of CVE-2018-8423. While analyzing this CVE and patch from Microsoft, we found that there was a way to bypass it which resulted in another crash. We reported it to Microsoft and it fixed it in the January 19 patch Tuesday. This issue was assigned CVE-2019-0576. We recommend our users to install proper patches and follow proper patch management policy and keep their windows installations up to date.

In this post we will do the root cause analysis of CVE-2019-0576. To exploit this vulnerability, an attacker needs to use social engineering techniques to convince a victim to open a JavaScript file which uses an ADODB connection object to access a malicious Jet Database file. Once the malicious Jet Database file is accessed, it calls the vulnerable function in msrd3x40.dll which can lead to exploitation of this vulnerability.

Background

As mentioned in our previous post, CVE-2018-8423 can be triggered using a malicious Jet Database file and, as per the analysis, this issue was in the index number field. If the index number was too big the program would crash at the following location:

Here, ecx contains the malicious index number. On applying the Microsoft patch for CVE-2018-8423 we can see that, on opening this malicious file, we get the following error which denotes that the issue is fixed, and the crash does not occur anymore:

 

Analyzing the Patch

We decided to dig deeper and see exactly how this issue was patched. On analyzing the “msrd3x40!TblPage::CreateIndexes” function, we can see that there is a check to see if “IndexNumber” is greater than 0xFF, or 256, as can be seen below:

 

Here, the ecx which contains the index number has the malicious value of “00002300” and it is greater than 0xFF. If we see the code, there is a jump instruction. If we follow this jump instruction, we reach the following location:

We can see that there is a call to the “msrd3x40!Err::SetError” function, meaning the malicious file will not be parsed if the index value is greater than 0xFF and the program will give the error message “Unrecognized database format” and terminate.

Finding Another Issue with the Patch

By looking at the patch, it was obvious that program will terminate if the index value is greater than 0xFF, but we decided to try it with an index value “00 00 00 20” which is less than 0xFF, and we got another crash in the function “msrd3x40!Table::FindIndexFromName”, as can be seen below:

Finding the Root Cause of the New Issue

As we know, if we give any index value which is less then 0xFF, we get a crash in the function “msrd3x40!Table::FindIndexFromName”, so we decided to analyze it further to find out why that is happening.

The crash is at the following location:

It seems that program is trying to access location “[ebx+eax*4+574h]” but it is not accessible, meaning it is an Out of Bound Read issue.

This crash looks familiar as it was also seen in CVE-2018-8423, except that it was an Out of Bound Write, while this seems to be an Out of Bound Read. If we look at eax it contains “0055b7a8” which, when multiplied by 4, becomes a very large value.

If we look at the file it looks like this:

As can be seen in below image, if we parse this file, this value of “00 00 00 20” (in little endian from the above image), denotes the number of an index whose name is “ParentIDName”:

Looking at the debugger at the point of the crash, it seems that ebx+574h points to a memory location and eax contains an index value which is getting multiplied by 4. Now we need to figure out the following:

  1. What will be the value of eax that will cause the crash? We know that it should be less than 0xFF. But what would be the lowest value?
  2. What is the root cause of this issue?

On setting a breakpoint on “msrd3x40!Table::FindIndexFromName” and changing the index number to “0000001f”, (which does not cause a crash but helps with the debugging and understanding the program flow) we can see that edx contains the pointer to an index name which, in this case, is “ParentIdName”:

Debugging further we can see that the eax value comes from [ebp] and the ebp value comes from [ebx+5F4h] as can be seen below:

When we look at “ebx+5F4” we can see the following:

We can see that “ebx+5F4” contains the index number for all the indexes in the file. In our case the file has two indexes and their number are “00 00 00 01” and “00 00 00 1f”. If we carefully review the memory we can figure out that the maximum number of indices which can be stored here are 0x20, or 32:

Start location: 00718d54

Each index number is 4 bytes long. So 0x20*4 + 00718d54 = 00718DD4

After this, if we look at ebx+574+4, we can see that it contains the pointer to index names:

So, the overall memory structure is like this:

There are only 0x80 or 128 bytes available to save index name pointer at location EBX+574. Each pointer gets saved at an index number location, i.e. for index number 1 it will be saved at EBX+574+1*4, the location for index number 2 will be saved at EBX+574+2*4 and so on. (index number starts from 0).

In this case, if we give an index number which is more than 31, the program will overwrite data past 0x80 bytes, which will be at the start of the EBX+5F4 location, which is the index number from the malicious file. So, in this case, if we give the value “00 00 00 20” instead of “00 00 00 1f”, it will overwrite the index number at the EBX+5F4 location, as can be seen below:

Now the program tries to execute this instruction in “msrd3x40!Table::FindIndexFromName’

Mov ecx, dword ptr [ebx+eax*4+574h]

Here, eax contains the index number which should be “00 00 00 01” but, since it is overwritten by “0055b7a8” which is a memory address, on multiplying it with 4, it becomes a huge number and then 574h is getting added to it. So, if that memory area does not exist and the program tries to read from that memory, we get an access violation error.

So, to answer the questions we had:

  1. Any value which is less then 0xFF and greater then 0x31 will cause a crash if the resulting memory location from [ebx+eax*4+574h] is not accessible.
  2. The root cause is that an index number is getting overwritten by a memory location, causing invalid memory access in this case.

How is it Fixed by Microsoft in the Jan 19 Patch?

We again decided to analyze the patch to see how this issue was fixed. As is clear from the analysis, any value which is greater than or equal to 0x20 or 32 still causes a crash so, ideally, the patch should be checking this. Microsoft has added this check in the Jan 19 patch release, as can be seen below:

As can be seen in the above image, eax hold the index value here and it is compared with 0x20. If it is more than or equal to 0x20 the program jumps to location 72fe1c00. If we go to that location, we can see the following:

As can be seen in the above image, it calls the destructor and then calls msrd3x40!Err::SetError function and returns. So, the program will display a message saying, “Unrecognized database format” and then terminate.

Conclusion

We reported this issue to Microsoft in October 2018 and it fixed this issue in the Jan 19 patch Tuesday. It was assigned CVE-2019-0576 to this issue. We recommend our users keep their Windows installations up to date and install vendor patches on a regular basis.

McAfee Coverage:

McAfee Network Security Platform customers are protected from this vulnerability by Signature IDs 0x45251700 – HTTP: Microsoft JET Database Engine Remote Code Execution Vulnerability (CVE-2018-8423) and 0x4525890 – HTTP: Microsoft JET Database Engine Remote Code Execution Vulnerability (CVE-2019-0576).

McAfee AV detects the malicious file as BackDoor-DKI.dr .

McAfee HIPS, Generic Buffer Overflow Protection (GBOP) feature will often cover this, depending on the process used to exploit the vulnerability.

References

The post Analyzing and Identifying Issues with the Microsoft Patch for CVE-2018-8423 appeared first on McAfee Blogs.

Jet Database Engine Flaw May Lead to Exploitation: Analyzing CVE-2018-8423

30 July 2019 at 15:53

In September 2018, the Zero Day Initiative published a proof of concept for a vulnerability in Microsoft’s Jet Database Engine. Microsoft released a patch in October 2018. We investigated this flaw at that time to protect our customers. We were able to find some issues with the patch and reported that to Microsoft, which resulted in another vulnerability, CVE-2019-0576, which was fixed on 8-Jan-2018 (Microsoft Jan 2019 Patch Tuesday).

The vulnerability exploits the Microsoft Jet Database Engine, a component used in many Microsoft applications, including Access. The flaw allows an attacker to execute code to escalate privileges or to download malware. We do not know if the vulnerability is used in any attacks; however, the proof of concept code is widely available.

Overview

To exploit this vulnerability, an attacker needs to use social engineering techniques to convince a victim to open a JavaScript file which uses an ADODB connection object to access a malicious Jet Database file. Once the malicious Jet database file is accessed, it calls the vulnerable function in msrd3x40.dll which can lead to exploitation of this vulnerability.

Although the available proof of concept causes a crash in wscript.exe, any application using this DLL is susceptible to the attack.

The following error message indicates the vulnerability was successfully triggered:

The message shows an access violation occurred in the vulnerable DLL. This vulnerability is an “out-of-bounds write,” which can be triggered via OLE DB, the API used to access data in many Microsoft applications. This type of vulnerability indicates that data can be written outside of the intended buffer, resulting in a crash. The cause of the crash is the maliciously crafted Jet database file. The file exploits an index field in the Jet database file format with an unexpectedly large number, resulting in an out-of-bounds write and, ultimately, the preceding crash.

The following diagram provides a high-level view of how the exploit works:

Exploit in Action

The proof of concept code contains one JavaScript file (poc.js), which calls a second file (group1). This is the Jet database file. By running poc.js through wscript.exe, we can trigger the crash.

As we see in the preceding image, we can review debug information to determine the function that crashes is “msrd3x40!TblPage::CreateIndexes.” Furthermore, we can determine that the program is trying to write data and failing. Specifically, we can see that the program is using the “esi” register to write to the location [edx+ecx*4+574h], but that location is not accessible.

We need to understand how this location is constructed to provide clues to the root cause. The debug information shows that register ecx contains the value 0x00002300. Edx is a pointer to memory that we will see again later. Finally, they are added together with an offset of 574 hexadecimal bytes to reference the memory location. From this information, we can guess the type of data that is stored there. It appears to be an array in which each variable is 4 bytes long and starts at the location edx+574h. While tracking the program, we determined the value 0x00002300 comes from the proof-of-concept file group1.

We know that the program attempts to write out of bounds and we know where the attempt occurs. Now we need to determine why the program attempts to write at that location. We investigate the user-provided data of 0x00002300 to understand its purpose. To do this we must understand the Jet database file.

Analyzing the Jet Database File

Many researchers have extensively analyzed the Jet database file structure. Some of the details of previous work can be found at the following links:

To summarize, a Jet database file is organized as a collection of pages, as shown in the following image:

The header page contains various information related to the file:

After the header come 126 bytes, RC4 encrypted, with the specific key 0x6b39dac7, which is the same for every JetDB file. Comparing the key value with the proof-of-concept file, we can identify that group1 is a Jet Version 3 file.

Further examination leads to a Table Definition Pages section, which describes various data structures for a table. (Click here for details.)

The table definition data has various fields, including two of note: Index Count and Real Index Count.

We can determine the value of these in our proof-of-concept file. When we check this with the group1 file, we see following:

There are total of two indexes in the Index Count. When we parse both indexes we see the familiar value of 0x00002300:

Our offending value 0x00230000 is the index number for index2 in the table. This index seems rather large and leads to the crash. Why does it crash the program? Further parsing the file, we find the names of the two indexes:

Debugging

With a debugger attached, we can see that first program calls the function “msrd3x40!operator new.” This allocates memory that stores the memory pointer address in eax:

After the memory is allocated, the program creates the new index:

This index number is used later in the execution. The function msrd3x40!Index::Restore copies that index number to the index address + 24h. This process is repeated in a loop for all indexes. First it calls the “new” operator, which allocates the memory. It then creates an index on that address and moves the index number to the base address of the index +24h. We see this move in the following code, which shows the malicious index value copied to newly created index:

Once successfully moved, the function msrd3x40!NamedObject::Rename is called and copies the index name value to the index address +40h:

If we look at the esi register, we see it points to the address of the index. The ecx register has a value of [esi+24h], which is the index number:

After a few more instructions, we can observe the original crash instructions. Edx points to the memory location. Ecx contains a very large number from the file group1. The program tries to access memory at location [edx+ecx*4+574h], which will cause the out-of-bounds write and the program crashes:

What is happening with the data the program tries to write? If we watch the instructions, we see that program tries to write the value of esi to [edx+ecx*4+574]. If we print esi or the previous value, we see that it contains the index name ParentIdName, which we saw in group1:

 

Ultimately, the program crashes while trying to process ParentIDName with a very large index number. The logic:

  • Allocate the memory and get the pointer to the start of the memory location.
  • From the start of memory location +574h, the program saves pointers to index names with each occupying 4 bytes multiplied by the index number mentioned in the file.

If the index number is very large, as in this case, and no validation is done, then the program will try to write out of bounds and crash.

Conclusion

This is a logic error and such errors are sometimes hard to catch. Many developers take extra precautions to avoid these types of bugs in their code. It is even more unfortunate when these bugs lead to serious security issues such as with CVE-2018-8423. When these issues are discovered and patched, we recommend applying the vendor patch as soon as possible to reduce your security risks.

Microsoft patches can be downloaded and installed from the following locations for respective CVEs:

CVE-2018-8423

https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2018-8423

CVE-2019-0576

https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2019-0576

McAfee Detection:

McAfee Network Security Platform customers are protected from this vulnerability by Signature IDs 0x45251700 – HTTP: Microsoft JET Database Engine Remote Code Execution Vulnerability (CVE-2018-8423) and 0x4525890 – HTTP: Microsoft JET Database Engine Remote Code Execution Vulnerability (CVE-2019-0576).

McAfee AV detects malicious file as BackDoor-DKI.dr .

McAfee HIPS, GBOP (Generic Buffer Overflow Protection) feature might cover this, depending on the process used to exploit the vulnerability.

We thank Steve Povolny of McAfee’s Advanced Threat Research team, and Bing Sun and Imran Ebrahim of McAfee’s Hybrid Gateway Security team for their support and guidance with this analysis.

 

References

The post Jet Database Engine Flaw May Lead to Exploitation: Analyzing CVE-2018-8423 appeared first on McAfee Blogs.

❌