aslr-guard icon indicating copy to clipboard operation
aslr-guard copied to clipboard

Attacker can corrupt code locator to target another function deterministically

Open hal-ler opened this issue 8 years ago • 4 comments

Cool example on how an attacker can use a code locator to deterministically call another function, while the binary is protected with ASLR-Guard. Please compile with -O0 to avoid the compiler aggressively optimizing the code away. I use memcpy to read and write the code locator without triggering the encryption/decryption/safe vault. Alternatively you can stop the code in the debugger and perform the memory corruption manually.

#include <stdio.h> #include <string.h>

struct TestClass { virtual void Good() { printf("Expected output.\n"); } };

struct BadClass { virtual void Bad() { printf("PWNED\n"); } };

int main() { TestClass instance; BadClass fakeInstance; void (TestClass::*f)() = &TestClass::Good; // Memory corruption unsigned long memAddress = reinterpret_cast<unsigned long>(&f); unsigned long oldValue; // Reading value without triggering decryption/safe vault memcpy(&oldValue, memAddress, 8); oldValue -= 0x18; // Writing value without triggering automatic encryption/safe vault memcpy(memAddress, &oldValue, 8); // Usage (instance.*f)(); return 0; }

hal-ler avatar Nov 03 '16 00:11 hal-ler

This "data-only" attack is indeed cool.

The assumption here is that the normal developer obtains the "displacement" of the member virtual function and saves it in unsafe memory. Attackers can modify the displacement to divert the virtual function call. I am not sure how common this case is in reality, given that member functions are supposed to be called via objects.

To mitigate such attacks, existing vtable protection techniques (such as your ACSAC'15 paper) can be used. Encrypting the displacement might be a candidate solution as well.

kengiter avatar Nov 03 '16 01:11 kengiter

The member function is still called via the object, see (instance.*f)();. I am not saying that this is a common use-case, but it is part of the C++ specifications. Also I know that Chrome actually uses function pointer to non-virtual members. These can also be overridden the same way actually to change the functionality to a virtual member-call instead.

You can change the example to include a void NonVirtualGood() in TestClass. Then use void (TestClass::*f)() = &TestClass::NonVirtualGood; and oldValue = 0x9 - 0x18; (the constants might be a bit off, I am doing the math from past experience).

hal-ler avatar Nov 03 '16 11:11 hal-ler

And the displacements themselves are not randomized, so any attacker with access to the binary on disk can figure out the right absolute and relative values to use.

Actually this attack is out of scope for vtable protection techniques, since the vtable pointer is unaffected. The attack targets the displacement, which is not known statically, so there is no way to enforce its validity in those schemas (besides bounds checking, but even then an attacker can target different virtual methods implemented by the same class).

I raised the issue since the attack is analogous to the jump table offset mentioned in the paper as being very important to protect.

hal-ler avatar Nov 03 '16 12:11 hal-ler

If the displacement is dereferenced through vtable, then randomizing entries in vtables would help mitigate such attacks. Anyway, this attack sounds very interesting, especially when targeting non-virtual members. I would say it is worth further investigation; if such displacements are common, this attack should be taken seriously.

kengiter avatar Nov 03 '16 13:11 kengiter