TLDR; TPEditor v1.96 does not perform bound checking when copying “boot page” (.sta) file content to memory. The finding can be exploited to gain arbitrary code execution upon file opening. Greetz to @mufinnnnnnn who paired up with me on the review. It was a highly collaborative effort to discover and exploit the bug. [ZDI Link]
This bug was a bit interesting. VirtualAlloc() is used to allocate memory and returns a pointer to an allocation. However, we can trigger a int32 overflow to request a large allocation size, causing VirtualAlloc() to fail and return NULL. Additionally, an int32 offset defined in the STA file gets added to our VirtualAlloc() address, which is where the contents of the file are written too. As we can control both values, we can reference any 32 bit address reliably and write data to. The 32bit program did not have ASLR enabled which made exploitation trivial.
The following C pseudo code demonstrates the vulnerability:
1: FILE *staFileHandler; //File handler is valid and already points to 0x200 location in .sta file being loaded. 2: size_t x; 3: size_t y; 4: size_t allocSize; 5: void *memoryAllocation; 6: 7: fread(&x,4,1, staFileHandler); 8: fread(&y,4,1, staFileHandler); 9: allocSize = y - x; 10: memoryAllocation = VirtualAlloc( 0,allocSize,0x3000,4); 11: fread(memoryAllocation+x,1,allocSize, staFileHandler);
On line 9, user controlled data can force a size allocation to wrap, resulting in a large value.
On line 10, the large value is passed to VirtualAlloc()‘s size parameter, resulting in the call to fail, returning a value of 0.
On line 11, the rest of the .sta file content is copied to a user controllable address through fread(). The address is controllable as user input controls the variable x, and memoryAllocation can be forced to 0. Additionally, as allocSize is a large value due to wrapping, the rest of the content in the .sta file is copied to the user controllable address.
In the disclosed proof of concept, opening the .tpe file will cause the crafted .sta file to load automatically, which in turn exploits the finding to launch Windows Calculator. The exploit works by leveraging the finding to overwrite a function pointer used by TPEdit application for bitmap painting. The pointer is updated to point to a memory address which will contain shellcode loaded from a .tpe file. When the .tpe file is rendered, the updated function pointer is called, which results in the shellcode to be executed.
Following is the disassembly of the TPEdt110.exe binary, in which the finding exists: