I wanted to write a simple program that would let me test out some basic stack exploits. Here’s a very simple example that should crash because of stack corruption.
#include <string.h> int test_bof() { char buffer[10]; strcpy(buffer, "aaaaaaaaaaaaaaaaaaaaaaaaaaa"); } int main(int argc, char** args) { test_bof(); return 0; }
When I ran the program, instead of an access violation, I got this error:
Unhandled exception at 0x00412059 in SimpleStackBof.exe:
Stack cookie instrumentation code detected a stack-based buffer overrun.
Interesting. I enabled the Assembler Output (C++ > Output Files > Assembler Output /FAs
) and looked at the generated assembly code.
_TEXT SEGMENT _buffer$ = -16 ; size = 10 __$ArrayPad$ = -4 ; size = 4 test_bof PROC ; COMDAT ; 5 : { push ebp mov ebp, esp sub esp, 80 ; 00000050H mov eax, DWORD PTR ___security_cookie xor eax, ebp mov DWORD PTR __$ArrayPad$[ebp], eax push ebx push esi push edi ; 6 : char buffer[10]; ; 7 : strcpy(buffer, "aaaaaaaaaaaaaaaaaaaaaaaaaaa"); push OFFSET ??_C@_0BM@CINAKCFB@aaaaaaaaaaaaaaaaaaaaaaaaaaa?$AA@ lea eax, DWORD PTR _buffer$[ebp] push eax call _strcpy add esp, 8 ; 8 : } pop edi pop esi pop ebx mov ecx, DWORD PTR __$ArrayPad$[ebp] xor ecx, ebp call @__security_check_cookie@4 mov esp, ebp pop ebp ret 0 _test_bof ENDP
First thing to notice is that there’s a magic 4-byte stack variable called __$ArrayPad$
that wasn’t in the C code. Also notice that the buffer[10]
variable has been aligned and so it actually takes up 12 bytes instead of the 10 I requested. This will be important later when crafting an exploit payload. One thing that struck me was how much stack space the compiler gave itself (sub esp, 80
). It’s kind of a weird number too – doesn’t even seem aligned. I don’t know.
The next thing that’s different is that after the usual stack preamble, it’s grabbing a value from the address __security_cookie
, xor’ing with the frame base pointer and then saving in __$ArrayPad$
. The rest of the code looks pretty normal until just before the return. The value at __$ArrayPad$
is put into ecx
and then xor’d with the current frame base pointer value. Then @__security_check_cookie@4
is called. Here’s the disassembled version of @__security_check_cookie@4
.
--- f:\dd\vctools\crt\crtw32\misc\i386\secchk.c 004112D0 3B 0D 00 60 41 00 cmp ecx,dword ptr ds:[416000h] 004112D6 75 02 jne failure (04112DAh) 004112D8 F3 C3 rep ret 004112DA E9 6C FD FF FF jmp ___report_gsfailure (041104Bh)
So what’s really going on here. A secret value __security_cookie
(ds:[416000h]) is read and xor’d with the value of the frame base pointer. That value is stored on the stack at __$ArrayPad$
, right above the saved address of the previous frame pointer which is right above the very important saved eip
(instruction pointer) value (important because that’s the address that execution will jump to when ret is executed). The function code runs as it normally would but before returning, a check is added. The value of __$ArrayPad$
is read and xor’d again with the frame base register. The result is in ecx
. Then the security check routine runs and compares ecx
with the original __security_cookie
value. If they are the same then there was no stack frame corruption detected. If the ecx
is different than __security_cookie
, that means that either ebp
was modified or more likely there was a stack buffer overflow that overwrote the original value of __$ArrayPad$
. This canary value is checked before the postamble runs and crashes the program if the stored frame pointer or eip
may have been compromised.
If you take any number A, and xor it with another number B you’ll get C.
If you then take C and xor it with B, you’ll get A.
E.g. in binary: 101 ^ 011 => 110 and 110 ^ 011 => 101, the original value.
That’s great for security but I’m trying to write an insecure program for demonstration so we need to turn off the stack cookie. In the properties for the project set C++ -> Code Generation -> Security Check to Disable Security Check /GS-
. Here’s the regenerated assembly (abridged) with the Security Check disabled.
_buffer$ = -12 ; size = 10 _test_bof PROC ; COMDAT ; 5 : { push ebp mov ebp, esp sub esp, 76 ; 0000004cH push ebx push esi push edi ... pop edi pop esi pop ebx mov esp, ebp pop ebp ret 0
Notice that the magic stack variable is gone and the preamble/postamble no longer contain the security cookie setup or check. Now it should be ready to exploit. Let’s run it.
Unhandled exception at 0x61616161 in SimpleStackBof.exe:
0xC0000005: Access violation reading location 0x61616161.
Error reading location 0x61616161 (ASCII ‘aaaa’) that’s more like it.