Mapping The Null Page With Python
Posted on Thu 28 June 2018 in Python
I've previously blogged on injecting Python into native processes. What started out as an experiment for determining the feasibility of implementing the "migrate" functionality for the Python Meterpreter went dormant until recently. When a proof of concept DoS exploit came out for CVE-2018-0833 I used it as an opportunity to look into recent bypasses for the NULL page mitigation that was added to Windows. Spoiler alert: it didn't work out.
NULL Page Mitigation Background*
The NULL page mitigation was added to Windows 8 by Microsoft. The idea is to prevent the successful exploitation of vulnerabilities which involved an attacker mapping the NULL page and placing some (usually) malformed structure there. A vulnerability would then be triggered causing the address 0 / the NULL page to be dereferenced as if it contained the structure. By preventing this page from being allocated at all (or in the case of EMET pre-allocating it and effectively squatting on it) an attacker can not control the data at the mission critical 0 address.
There is however a bypass for this mitigation that can be used on modern 32-bit systems. Google's Project Zero released the details of a vulnerability in Windows 10 Creator's update and outlined how the NTVDM.exe process could be leveraged to control the NULL page. There's another excellent write up on the topic available here. Basically it was identified that the NTVDM.exe process (which is used to support 16-bit programs) is permitted to allocate and use the NULL page.
Now this vulnerability is a NULL pointer dereference, so to explore the possibility of using it as a Local Privilege Escalation (LPE) exploit I looked into the aforementioned NULL page mitigation bypass. Since the NTVDM.exe process would need to be infected to control the NULL page, it would typically require a Dynamic Link Library (DLL) to be written. In this past, I've always written these in C, but it is a time consuming process involving a loop of writing code, compiling, and executing. I've been using my mayhem library over the past few years to prototype these traditionally C exploits in Python before writing the final product in C. This allows me to confirm that a vulnerability meets the criteria I would like before investing the time to write it in C. That criteria is usually whether or not it even can be exploited to accomplish something useful and if the process to do so is reliable.
This situation was unique as the exploit would have to be running within the NTVDM.exe process so it seemed like a good idea to revisit the capabilities of Python injection. To meet these requirements, the original proof of concept tool was updated with additional functionality. In addition to loading a script from disk (in this case the exploit), I added an initial stub to setup the environment. This stub which loads and executes the user-specified script also setups up a connection back to the injecting process via a named pipe and redirects stdout and stderr through it. This allows the injecting process to receive the details of anything printed out to either of these streams, including standard print() calls as well as stack traces.
With this additional functionality, a script which would write to the NULL page was able to be injected into NTVDM.exe to aid in testing the practical exploitation of this vulnerability.
In the case of CVE-2018-0833, it turned out to be unpractical to reliably control the NULL page. This was due to the fact that the vulnerability was relating to the SMB service and would not necessarily be triggered in the context of the process initiating the connection as I had hoped. Nevertheless, the improvements made to the python_injector.py utility will be helpful in future, similar situations.