Where to try to find bugs in software? It turns out, they are all over the place! In this installation of bug finding, the author researches translating from one language to another.
The first bug is in node-sass, a translator of the CSS precompiler. The bug is an integer underflow in a malloc size. The size used (for malloc) has a 1 added to it. Then, the original size is used for allocation. This results in a size of 0 being possible when a size of -1 is used.
The second bug was a vulnerability in
png-img in Nodejs. The png-img bindings use an initStorage function which is vulnerable to an integer overflow via multiplication. The bug is very straight forward; it is literally through the
height and
width of the PNG image. In fact, this same exact bug existed within a
JPEG parsing library used by Instagram.
This vulnerability ends up being a wildcopy: a vulnerability where a size is underflowed where a bunch of data gets written. As talked about in the Checkpoint article, this only results in a crash unless we can change the flow in execution before the crash occurs. In fact, we can! If there is NO more data in the stream of the PNG, the image exits.
The author mentions three important parts of an attack:
- How does the attack trigger the bug? PNG file. However, this is not a back & forth communication.
- What data does the attacker control? The bulk of it; it is a PNG file.
- What algorithms (flow of the program) can by influenced by the attacker?It is a linear heap overflow, which results in the ability to corrupt heap memory.
Node.js is compiled with stack canaries, Full RELRO and the NX bit, but without PIE and Fortify. Since the binary is non-interactive (only a single shot) and the binary is NOT compiled with PIE, overwriting data in the .bss section and jumping to code in the non-randomized .text section is a safe bet. The single shot is important to note: memory leaks are NOT possible in this scenario.
The next section goes into how the software interacts with the memory that is controllable by the user. This is how an attacker gets their foothold in a program. In particular, there is a section on the heap that contains function pointers (perfect). But... this did not actually work. They had corrupted code for the dynamic linker in the being used.
They make a really good point about real world exploit development: "As a developer, it is important to understand that attackers will explore vulnerabilities according to the resources and time they’re willing to invest in their exploitation. Even for a relatively straightforward vulnerability such as the png-img heap overflow, we see there is a distinct attacker calculus at play that weighs the pros and cons of various attack strategies against your code. Mitigations are considered from both a platform-specific and goal-oriented perspective.
Using this technique, they went with the corrupting of the
symbol table resolution process. The attack looks something similar to
RET-2-dl-resolve in order to resolve an arbitrary symbol. However, I am slightly confused because they mention
heap chunk for a link map being corrupted or in the runtime execution. In my experience, the symbol resolution process on x86_64 does not use the heap (but maybe whatever they are on does?).
Once they decide to use the symbol resolution process, it was all about finding pointers to dereference in the non-PIE binary to act properly. Additionally, many of the values for this process are relative offsets. So, by placing proper values on the heap for the corrupted linkmap, it was possible to gain control of the symbol resolution process to load an arbitrary symbol.
What symbol do we load? Well, of course, they just call system out of it. This is not the entire description of the exploit; it is just a high-level overview. The entire thing should be read in order to understand the full process.
Overall, this is a fairly crazy exploit that ended up NOT requiring any leaks in order to full off. I had never heard of the ret-2-dl-resolve before; this will be a good addition to the toolbox when leaks are not possible.