I've recently read about D_FORTIFY_SOURCE and the changes it makes to vulnerable functions. I wished to mess around with it, and as such made a small test binary.
The test binaries source code is:
#include <stdio.h>
#include <stdlib.h>
//No ASLR/PIE
//Enable FORTIFY
//
//
//
void shell()
{
system("sh");
}
int flag = 0;
int main(int argc, char * argv[])
{
char buf [256];
fgets(buf, sizeof(buf), stdin);
printf(buf);
if(flag)
{
shell();
}
}
It wasn't meant to do anything, just to try out the protections on printf.
However, when I compiled the program, it didn't seem to be 'fortified'
The exact gcc command was:
gcc fsFORTIFIED.c -o fsFORTIFIED -m32 -no-pie -D_FORITFY_SOURCE=2 -O2
I made sure that the optimisation flag was set, yet the standard printf function remained. GDB gave the assembly of main:
0x080491c6 <+0>: lea ecx,[esp+0x4]
0x080491ca <+4>: and esp,0xfffffff0
0x080491cd <+7>: push DWORD PTR [ecx-0x4]
0x080491d0 <+10>: push ebp
0x080491d1 <+11>: mov ebp,esp
0x080491d3 <+13>: push esi
0x080491d4 <+14>: push ebx
0x080491d5 <+15>: push ecx
0x080491d6 <+16>: sub esp,0x120
0x080491dc <+22>: call 0x80490e0 <__x86.get_pc_thunk.bx>
0x080491e1 <+27>: add ebx,0x2e1f
0x080491e7 <+33>: mov eax,gs:0x14
0x080491ed <+39>: mov DWORD PTR [ebp-0x1c],eax
0x080491f0 <+42>: xor eax,eax
0x080491f2 <+44>: mov eax,DWORD PTR [ebx-0x8]
0x080491f8 <+50>: push DWORD PTR [eax]
0x080491fa <+52>: push 0x100
0x080491ff <+57>: lea esi,[ebp-0x11c]
0x08049205 <+63>: push esi
0x08049206 <+64>: call 0x8049050 <fgets@plt>
0x0804920b <+69>: mov DWORD PTR [esp],esi
0x0804920e <+72>: call 0x8049040 <printf@plt>
0x08049213 <+77>: add esp,0x10
0x08049216 <+80>: cmp DWORD PTR [ebx+0x2c],0x0
0x0804921d <+87>: jne 0x804923b <main+117>
0x0804921f <+89>: mov eax,DWORD PTR [ebp-0x1c]
0x08049222 <+92>: sub eax,DWORD PTR gs:0x14
0x08049229 <+99>: jne 0x8049242 <main+124>
0x0804922b <+101>: mov eax,0x0
0x08049230 <+106>: lea esp,[ebp-0xc]
0x08049233 <+109>: pop ecx
0x08049234 <+110>: pop ebx
0x08049235 <+111>: pop esi
0x08049236 <+112>: pop ebp
0x08049237 <+113>: lea esp,[ecx-0x4]
0x0804923a <+116>: ret
0x0804923b <+117>: call 0x80491a6 <shell>
0x08049240 <+122>: jmp 0x804921f <main+89>
0x08049242 <+124>: call 0x80492d0 <__stack_chk_fail_local>
Here, I expected a call to printf_chk@plt, but instead it had printf@plt. To make sure, I ran the program, and gave it the input %3$x,and it ran fine instead of halting.
My question is, why isn't GCC properly implementing D_FORITFY_SOURCE, even after the optimisation flag is set?
Help is appreciated
Make sure
-D_FORTIFY_SOURCE=2
is spelled correctly.(In this case, it wasn't:
-D_FORITFY_SOURCE=2
was the problem.)Unlike normal options, GCC can't catch typos in this for you.
-Dfoo=bar
simply defines a preprocessor macro (GCC manual), exactly the same as#D_FORTIFY_SOURCE 2
at the top of your file. (-D
is a standard compiler option that all compilers accept, not just GCC.) Only a few special macro names affect headers like stdio.h.For options like
-fstack-protector-strong
,-march=skylake
or-fno-math-errno
or whatever, GCC does only accept valid options it recognizes. But of course-Dfoo=bar
is valid for any foo, it just doesn't do anything because it's not the macro that glibc headers use in their#ifdef
s.The
-D
is spelled correctly; the rest of the option is just what's being defined.In ISO C, global-scope names that begin with an
_
1 are reserved for use by the implementation, which is why defining_FORTIFY_SOURCE
is allowed to be special. But GCC itself doesn't reject names like that. For GCC to do that, it would have to know all the names that the internals of glibc, MUSL, and other parts of "the C implementation" happen to use, and that would make it a pain to work on those headers.Footnote 1: the reserved-names rules are somewhat less broad than that, I'm simplifying.