It rather involved being on the other side of this airtight hatchway: Attacking another program by modifying its memory

Raymond Chen

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_ALL_ACCESS 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 Write­Process­Memory 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?

7 comments

Discussion is closed. Login to edit/delete existing comments.

  • Josiah Bills 0

    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?

    • Raymond ChenMicrosoft employee 2

      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).

    • Jamie Anderson 2

      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 that people have to walk down all the right hallways and can’t bust through walls) and ASLR (hiding the prize inside one of thousands of identical boxes) usually help. The bug submitter chose to disable those protections to give themselves a better chance.

    • Simon Farnsworth 0

      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 do foo, and yet an attacker was able to make it do foo.

      If the attacker did not have permission to do foo, then there would genuinely be a vulnerability – the attacker should not have been able to modify bar in step 3. But most of the report text is about obfuscating the fact that the attacker already has permission to do whatever it is that they make bar do, as well as permission to modify bar to do whatever it is the attacker wants it to do.

      And it’s really hard to come up with any form of “defense-in-depth” that still permits you to do something that you have been granted permission to do, but that stops you from doing it in a convoluted fashion.

      • Falcon 1

        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!

  • Neil Rashbrook 0

    I searched for Call Flow Guard and got articles on Control Flow Guard. Did it get renamed?

    • Raymond ChenMicrosoft employee 0

      No, I just misremembered what CFG stood for.

Feedback usabilla icon