A security vulnerability report arrived that took the following form:
There is a security vulnerability in XYZ.DLL. This DLL contains a function pointer stored in memory. An attacker can modify this function pointer and gain arbitrary code execution.
The proof of concept went like this:
auto processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, victimProcessId); void* value = new_value_for_function_pointer; void* victimAddress = address_of_variable_in_victim_address_space; WriteProcessMemory(processHandle, victimAddress, &value, sizeof(value), &actual);
It is evident from this proof of concept that we are already on the other side of this airtight hatchway: PROCESS_
gives you total control over the victim process. If you wanted to gain control over it, just inject a thread and go to town! No need to hunt around for a function pointer you can overwrite to point to some other function, and the presumably arrange for that other function to do something unexpected when it is called.
The finder explained that the WriteProcessMemory
was just a way to simulate a write-what-where vulnerability. The real attack would be to find a write-what-where vulnerability somewhere in the victim process, and then leverage that to overwrite the function pointer in XYZ.DLL.
That still assumes that you’re on the other side of the airtight hatchway. If you have gained write-what-where control over the process, then you can overwrite a return address onto the stack or overwrite an object’s vtable pointer to point to a constructed fake vtable. There’s nothing specific about XYZ.DLL. Any component that uses function pointers will do.¹
The real vulnerability is the component that has a write-what-where vulnerability. That’s the guy that let you into the airtight hatchway. But once you get there, you’ve already won.
Bonus chatter: Address Space Layout Randomization (ASLR) and Control Flow Guard (CFG) make these types of attacks harder to carry out remotely. In particular, this finder quietly disabled Call Flow Guard protection as part of their proof of concept so that their overwritten function pointer would be used without validation.
¹ Unless your point is that all function pointers are a security vulnerability?
I searched for Call Flow Guard and got articles on Control Flow Guard. Did it get renamed?
No, I just misremembered what CFG stood for.
It doesn’t seem relevant in this case, but a term I have heard from security people a lot is defense-in-depth. The idea, as I understand it, is to put defenses in on the other side of the airtight hatchway to make it harder for attackers who have gotten their to do their thing. Have you ever run into an “other-side-of-the-airtight-hatchway” vulnerability that was patched on the basis of this principle?
In this context, it's worth fully understanding what makes an "other-side-of-the-airtight-hatchway” vulnerability an "other-side-of-the-airtight-hatchway” vulnerability, and not a different sort of vulnerability.
"Other-side-of-the-airtight-hatchway” vulnerabilities all take the following form:
1. Attacker has permission to do foo.
2. There exists a component bar which cannot do foo unless modified by someone with permission to do foo.
3. The attacker modifies bar to do foo.
4. Claim a vulnerability in bar, since it's not supposed to be able to...
Sometimes it’s not even really obfuscated – I remember one about a scripting object that could supposedly be tricked into overwriting some files on a web server.
The first step of the “attack” was to find a web server with a world-writable directory that executes scripts as SYSTEM!
Defense in depth basically means having multiple layers of protection, so that if someone gets through one layer there's another to hinder them. Think of it as adding more walls and locked doors around a secure room.
In this case, `PROCESS_ALL_ACCESS` basically means that you don't have to break through those doors; you have a skeleton key and can just walk right through any door that's thrown at you.
As the Bonus Chatter notes, CFG (making sure...
There is a lot of defense in depth: Stack canaries, ASLR, CFG, pointer authentication… They are secondary defenses that mitigate the impact of a vulnerability, but the underlying vulnerability still is addressed (assuming it is a valid vulnerability).