Last year, I wanted to check out some vulnerabilities that aren’t really common and came across CWE-789 which is for Uncontrolled Memory Allocation. As of May 1, 2020, there are 135,422 CVEs recorded in total, 929 CVEs recorded for vulnerabilities containing the keyword “memory consumption” and 399 CVEs recorded for vulnerabilities containing the keyword “memory allocation”:
This means the vulnerability classification selected builds up around only 0.98% of the whole CVE database.
While looking into potential vectors based from the examples given found in the CWE-789 page, I ended up listing down software applications that might have undiscovered vulnerabilities and found two zero-days labeled as CVE-2019-14941 and CVE-2019-15234.
The main thing that made me include SHAREIt in the list was the application has to communicate over a local/remote network and it’s a file transfer application so it definitely had some code where memory allocation was being done. It was just one of the perfect candidates to play around with since I had it installed in my machine and there was already familiarity with its functions.
Initially, viewing the libraries used by SHAREIt gives us the idea that the application was written in a .NET programming language:
Loading SHAREIt.exe in DotPeek confirms this:
To start tracing down the application’s code, we’ll need to know what port(s) the application is using for communication. This can be done using netstat:
Notice that SHAREIt uses TCP ports 25999 and 55283. By decompiling each library and searching for port 55283, it was found to be used at SHAREIt.Foundation -> CoreManager.cs:
Viewing this in the decompiled source code itself shows that port 55283 is used for the unsecure listener:
Notice that in line 120 above, a callback function is being called to handle the unsecure requests. Tracing this function should lead us to the following:
In the last line of the handler, an object CommandChannel is being instantiated passing in the client that made the request as argument. Tracing this object should lead us to the following:
The CommandChannel contains an eventhandler that processes the request through the ReceiveMessage method:
Reviewing the code, notice that there’s a function this.Receive(6) seen at line 151 where the byte array is being parsed as a packet header at line 163. If the packet header is of a general type, the body length from the header will then be used to receive another set of data.
Investigating more into the function this.Receive leads us to the following:
In line 193, the argument passed to the function is used to allocate memory. Going back to line 151, the initial memory allocation was 6 bytes because of this.Receive(6). The 6 bytes are then parsed as a packet header with the format below:
- The 1st byte (@index 0) represents the version
- The 2nd byte (@index 1) represents the packet type
- The 3rd up to 6th byes (@index 2-5) represents the body length
Again, going back to the receive method, notice that if the packet is of a general type, the application will proceed to get the body length and pass it in the function where the memory allocation happens:
Searching through the code base returns the following for the packet types:
Based from the index position of the packet types, a general packet can be represented by the number 1:
- Index 0 – HeartBeat
- Index 1 – General
- Index 2 – Signal
The function below can then be abused with the condition that the packet should be of a general type:
There are two possible exploitation scenarios that I can think of which can happen if a user-controlled input is used to allocate memory:
- Crash the application by entering a negative value. This is possible because in .NET, a system.overflow exception should return if you try to allocate memory with a negative size.
- Consume a large amount of memory by entering a really large value. In theory, the limitation for this is being able to allocate 2 GB of memory because for (1) the data type used is a signed integer which means the positive maximum is 2,147,483,647 and (2) the maximum imposed limit of an object in Microsoft’s CLR is 2 GB.
Unfortunately, #1 isn’t possible in this scenario because of the verification in line 191:
Item #2 however is possible and to develop the exploit, sending 6 bytes to the port could allocate a large amount of memory. To understand the exploit’s format better:
- The 1st byte (B0) can be any value since this is not used in the vulnerable code
- The 2nd byte (B1) should be a general type of packet so the value should be 1
- The remaining bytes are representing a signed integer (4 bytes) which is used to allocate memory if the packet type is general
Suppose we want to make the program allocate approximately 800 MB of memory, the byte array should be [0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x3F] because 0x3FFFFFFF is equivalent 822,083,583. Also note that the format of the integer bytes are in reverse because of the endianess.
The python exploit should be the following:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
header = “\x01\x01\xFF\xFF\xFF\x30”
Before running the exploit, notice that the application consumes about 36 MB of memory:
After running the exploit, the memory consumption is now about 840 MB:
This vulnerability is represented by CVE-2019-14941 (unsecure listener). For the secure listener, it is represented by CVE-2019-15234 and is exploitable through port 25999. While they are of the same vulnerability classification, separate CVE numbers were assigned because they exist in different parts of the code and 2 separate fixes should be implemented.
The secure listener was seen to be more complex than the unsecure listener. For example, instead of just 6 bytes, the packet header for the secure listener is 16 bytes and the format can be seen below with the highlighted code that represents the data length used for allocating memory:
The vulnerable code can only be reached if the flag had the value of 4,261,346,814 and the full message length was larger than 16:
Developing the exploit for CVE-2019-15234 in Python can be done as the following:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Allocate ~800 MB of memory (0x2FFFFFFF)
Again, 0x2FFFFFFF is approximately 800 MB while 0xFDFEFDFE should be the value of the flag (4,261,346,814).
Both exploits can be downloaded here. These vulnerabilities currently do not have any official fix so if you are not using SHAREIt for Windows, just close the application and don’t leave it running.
A big shout out to my awesome friends from HackStreetBoys!