assemblystack-overflowcpu-registersvirtual-functionsseh

Overwriting stack exception handler (SEH) in buffer overflow / Assembly MOV


I am trying to follow along an explanation of bypassing stack cookies by overwriting the stack exception handler (SEH) located here: https://www.corelan.be/index.php/2009/09/21/exploit-writing-tutorial-part-6-bypassing-stack-cookies-safeseh-hw-dep-and-aslr/

PDF link to the same page: https://repo.zenk-security.com/Reversing%20.%20cracking/%20Bypassing%20Stack%20Cookies,%20SafeSeh,%20HW%20DEP%20and%20ASLR.pdf

The section I have a question about is under Stack cookie bypass demonstration 2 : Virtual Function call of the website or page 30 of the PDF

Initial state of registers:

pic1 initial

The post then says:

...these 4 instructions are executed, attempting to load the address of the function into eax…

 0040104D  |. 8B45 F0        MOV EAX,DWORD PTR SS:[EBP-10]
 00401050  |. 8B10           MOV EDX,DWORD PTR DS:[EAX]
 00401052  |. 8B4D F0        MOV ECX,DWORD PTR SS:[EBP-10]
 00401055  |. 8B02           MOV EAX,DWORD PTR DS:[EDX]

The end result of these 4 instructions is

after

and then CALL EAX would be called, executing the modified function pointer address in EAX.

My question:

How do EAX and EDX point to the overwritten string after the 4 mentioned instructions?

My understanding is the after the first instruction,

 MOV EAX,DWORD PTR SS:[EBP-10]

EBP (0012FF6C) minus 10 (hex?) equals 0012FF5C. Value at location 0012FF5C is 0012FF78. 0012FF78 is the location of the SEH further up in the stack passed as a parameter. So EAX = 0012FF78. So far so good, since 0012FF78 is the address of the pointer to the SEH further up the stack.

Second instruction,

 MOV EDX,DWORD PTR DS:[EAX]

EDX is set to the value at the location pointed to by EAX. Value at location 0012FF78 is 0040211C. EDX = 0040211C?

Third instruction,

 MOV ECX,DWORD PTR SS:[EBP-10]

ECX is set to 0012FF78 which is the same as the first instruction but with ECX instead of EAX. Does this instruction matter for this context?

Fourth instruction,

 MOV EAX,DWORD PTR DS:[EDX]

EAX is set to the value referenced by EDX (0040211C). EAX = ??? whatever is at that location in memory.

My computed EAX (???) and EDX (0040211C) do not match the post's listed after values of EAX (42424242) and EDX (0012FF78).

What went wrong with my interpretation? Shouldn't EDX == ECX? Thanks.


Solution

  • Yes, it seems there is a mistake

    Theory

    The method described bypasses the GS protection by diverting the execution flow before the function returns (and checks the canary).
    This is accomplished through the tainting of the vtable of a C++ object allocated further up in the stack by the mean of a buffer overflow.

    The layout of a C++ object with a virtual member is augmented with a hidden pointer to the vtable.
    This pointer is the first member of the class laid out in memory, see this.

    The object is allocated somewhere on the stack but a pointer to it is stored at [ebp-10h] (effective address 0x12FF5C), you can see this by looking at the instruction 10 00401010 894df0 mov dword ptr [ebp-10h],ecx (more on this below) found on page 27 of the PDF where the victim function's disassembly is shown.

    With MOV EAX,DWORD PTR SS:[EBP-10] we have eax holding the pointer to the C++ object i.e.this since we are inside a member function and [ebp-10h] = original ecx (see below). eax = 12FF78h (ptr to this C++ object).
    With MOV EDX,DWORD PTR DS:[EAX] we have edx holding the pointer to this[0] i.e. this.vtable. edx = 40211ch (ptr to this C++ object's vtable). With MOV EAX,DWORD PTR DS:[EDX] we have eax holding the pointer to this.vtable[0] ie &bar. eax = 401070h (ptr to bar).

    The MOV ECX,DWORD PTR SS:[EBP-10] just moves this into ecx, this is the thiscall (no pun intended) calling convention.
    This is also why I recognized that [ebp-10h] was this and not a pointer to another object.

    Implementation

    After the copy the stack is (exactly as you posted):

    The stack after the copy, the vtable is not corrupted

    We can see that the C++ object has not been touched, the copied buffer is too short.
    The attacker would need one more DWORD, containing a pointer inside the buffer where they crafted a new vtable.