A very quick background to help you answering my questions :
- Learning IDA Interactive Disassembler, the old free edition (too expensive for a hobbyist)
- 15 year Linux Sysadmin & DBA experience
- Hobbyist coder (C/ASM/Fortran/...)
- Not very knowledgeable about Windows & Microsoft in general
- not a security expert, but enough to be a sysadmin
After a lot of random disassembly of stuff here and there i quickly noticed they have a lot of common code. And at the very beginning of a lot of executable there is something that look like this :
lea eax, [ebp+SystemTimeAsFileTime] push eax ; lpSystemTimeAsFileTime call ds:GetSystemTimeAsFileTime mov eax, [ebp+SystemTimeAsFileTime.dwHighDateTime] xor eax, [ebp+SystemTimeAsFileTime.dwLowDateTime]
It's literally in pretty much every binary and a quickly found that it's something called by microsoft : "Buffer Security Check". And it's added automagically by the compiler using the /GS flag.
It's a stack canary used to detect stack buffer overflow :
This method works by placing a small integer, the value of which is randomly chosen at program start, in memory just before the stack return pointer. Most buffer overflows overwrite memory from lower to higher memory addresses, so in order to overwrite the return pointer (and thus take control of the process) the canary value must also be overwritten. This value is checked to make sure it has not changed before a routine uses the return pointer on the stack
And from the Microsoft documentation :
On functions that the compiler recognizes as subject to buffer overrun problems, the compiler allocates space on the stack before the return address. On function entry, the allocated space is loaded with a security cookie that is computed once at module load. On function exit, and during frame unwinding on 64-bit operating systems, a helper function is called to make sure that the value of the cookie is still the same. A different value indicates that an overwrite of the stack may have occurred. If a different value is detected, the process is terminated.
It's all good and everyone will live happily ever after.
However, i'm confused about the implementation itself. It's a bunch of XOR of : System time, processId, ThreadId, uptime, more time (perf counter) at process creation.
I'm not saying it's a terribly bad implementation (but at first glance i'm thinking it) since it's a lot of XOR and a single wrong bit will screw up the cookie entirely (unless there is an attack i'm not aware of) and brute forcing is 100% prevented (or is it ? dunno) since the process will be terminated when the guess is incorrect and a new cookie generated at next process launch.
Now i have a bunch of question :
- Why such a weird pseudo-random generation implementation ? Doesn't the OS have a reliable entropy source ? Is it portability related ? Some variant of Windows may not have an entropy source ?
- isn't processId and threadId known to everyone on the system anyway ? so why use it as entropy source ?
- It's all time related and launched at process startup, aren't services launch on boot (stuff that need the most protection) guaranteed to have low uptime & perfcounter and a known system time and therefore the weakest entropy ?
- Why isn't this cookie generation a system call anyway ? It's now unpatchable without recompilation.
Here is my commented disassembly :
push ebp ; prolog mov ebp, esp ; prolog sub esp, 14h ; prolog and [ebp+SystemTimeAsFileTime.dwLowDateTime], 0 and [ebp+SystemTimeAsFileTime.dwHighDateTime], 0 mov eax, __security_cookie push esi push edi mov edi, 0BB40E64Eh ; a global constant thingy for security cookie mov esi, 0FFFF0000h cmp eax, edi ; compare local security cookie ; with global security cookie. ; if security_cookie == 0BB40E64Eh then it's not initialized ; and it need to be initialized jz short generate_security_cookie
The generation is done only if it compare to a world-wide known constant "0BB40E64Eh". Isn't there a potential attack vector ? couldn't it be value local to the system ?
- Why bother make the generation conditional ?
- Is it even a security feature ? or just a debug mean to detect something went wrong with the stack ?
It's a lot of questions, it's my first step in this kind of stuff, but this is so confusing. But mainly : why not just call an OS entropy source ? I'm so confused...
Bonus question : If one have a mean to modify the binary (a single bit in the worldwide known constant ?) couldn't the whole /GS stuff could be bypassed ? and if it's a network open service you now have a service with a buffer overflow potential open to the world, right ?