Exploiting CVE-2018-5093 on Firefox 56 and 57 – PART1: controlling the instruction pointer

POST I 12:00 pm, 5th July

Context

The purpose of this project is to obtain an initial access during Red Team exercise by gaining code execution through the exploitation of a vulnerability.

This first article describes the first steps of the exploit development regarding the CVE-2018-5093 explaining how to take advantage of the integer underflow vulnerability on Firefox 56&57 to control the instruction pointer when opening a specifically crafted web page.


The development of the proof of concept was done under a Windows 10 with 21H2 version environment and with the help of WINDBG debugger.

Currently, there is not any article with a functional exploit which was published. That is the reason why, the development of this exploit was performed with the help of the ExodusIntel’s write-up.

To understand this paper, you need to have some knowledge of assembly with the usage of the stack and the heap as well as some famous techniques in browser exploits like Heap Spraying.


Vulnerability analysis

The vulnerability was found in the WebAssembly code in the xul.dll library and reported in 2018 to Mozilla. This vulnerability is fixed in Firefox 58 and 59 since 2020.

In the Figure 1, as a proof of concept, here is the easy way to trigger the vulnerability.


Figure 1: proof of concept


This vulnerability is triggered when the get() function of the Table object is called. In fact, when the get() function is called, a call of the getImpl() function is executed. However, in the getImpl() function, there is a check of the index entered in the argument of the get() function. It is the ToNonWrappingUint32() function which checks if the index is between 0 and table.length() – 1, as we can see in the Figure 2.


Figure 2: underflow vulnerability


Triggering the bug

As presented in the screenshot above, when we create the Table object, we can specify a table size of 0 with the initial component in the Figure 1, so table.length() can be equal to 0. Then, when table.length() is equal to 0, the value table.length() – 1 is -1, but the function ToNonWrappingUint32() has the arguments type unsigned int 32, so the -1 value becomes the maximum value of the unsigned int 32, it is an underflow, so now the function cannot check the value of the index and all numbers coded in 32 bits can be used as an index.

In fact, a signed integer on 8 bits, that is equal to -1, has binary representation like following: 1111 1110.

So, the program will interpret this as the unsigned integer 254, which corresponds by the maximum value coded on 8 bits – 1.


Read more here. 


Subscribe to our Newsletters

Info Message: By continuing to use the site, you agree to the use of cookies. Privacy Policy Accept