How is the stack protection enforced in a binary?
On a linux box with an Intel CPU, lets say I compiled by binary with -fstack-protect-all.
Since it is not explicitly stated, it will be assumed that this refers to ELF binaries compiled using GCC with the -fstack-protector-all argument.
GCC Stack Protection Mechanisms
-fstack-protector-all
is an extension of -fstack-protector
:
-fstack-protector
Emit extra code to check for buffer overflows, such as stack smashing attacks. This is done by adding a guard variable to functions with vulnerable objects. This includes functions that call alloca, and functions with buffers larger than 8 bytes. The guards are initialized when a function is entered and then checked when the function exits. If a guard check fails, an error message is printed and the program exits.
-fstack-protector-all
Like
-fstack-protector
except that all functions are protected.
The aforementioned "guard variable" is commonly referred to as a canary:
The basic idea behind stack protection is to push a "canary" (a randomly chosen integer) on the stack just after the function return pointer has been pushed. The canary value is then checked before the function returns; if it has changed, the program will abort. Generally, stack buffer overflow (aka "stack smashing") attacks will have to change the value of the canary as they write beyond the end of the buffer before they can get to the return pointer. Since the value of the canary is unknown to the attacker, it cannot be replaced by the attack. Thus, the stack protection allows the program to abort when that happens rather than return to wherever the attacker wanted it to go.1
Now to the first set of questions:
How is this encoded into the binary? (Can I see this info using readelf? ). Is it encoded into every page/segment or is it added to the binary in one place and the loader picks it up?
The -fstack-protector-all
causes the compiler to generate code in which a guard variable is pushed to all functions and checked prior to return. In other words, additional machine instructions are generated that have to do with pushing, checking and popping the stack canary.
This can be illustrated using 2 example binaries produced from the same source, in which one was compiled with the -fstack-protector-all
argument and the other was not.
Source code:
int test(int i) {
return i;
}
int main(void) {
int x;
int i = 10;
x = test(i);
return x;
}
Function from binary compiled without -fstack-protector-all
:
$ objdump -dj .text test | grep -A7 "<test>:"
00000000004004ed <test>:
4004ed: 55 push %rbp
4004ee: 48 89 e5 mov %rsp,%rbp
4004f1: 89 7d fc mov %edi,-0x4(%rbp)
4004f4: 8b 45 fc mov -0x4(%rbp),%eax
4004f7: 5d pop %rbp
4004f8: c3 retq
Function from binary compiled with -fstack-protector-all
:
$ objdump -dj .text protected_test | grep -A20 "<test>:"
000000000040055d <test>:
40055d: 55 push %rbp
40055e: 48 89 e5 mov %rsp,%rbp
400561: 48 83 ec 20 sub $0x20,%rsp
400565: 89 7d ec mov %edi,-0x14(%rbp)
400568: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax <- get guard variable value
40056f: 00 00
400571: 48 89 45 f8 mov %rax,-0x8(%rbp) <- save guard variable on stack
400575: 31 c0 xor %eax,%eax
400577: 8b 45 ec mov -0x14(%rbp),%eax
40057a: 48 8b 55 f8 mov -0x8(%rbp),%rdx <- move it to register
40057e: 64 48 33 14 25 28 00 xor %fs:0x28,%rdx <- check it against original
400585: 00 00
400587: 74 05 je 40058e <test+0x31>
400589: e8 b2 fe ff ff callq 400440 <__stack_chk_fail@plt>
40058e: c9 leaveq
40058f: c3 retq
Since stack protection is implemented via assembly code, the results of the -fstack-protector
family of flags is best investigated using a disassembler like objdump
. readelf
technically shows this information, since the -x
argument produces a hex dump of the selected section.
readelf
hex dump of binary compiled without stack protection:
$ readelf -x .text test
Hex dump of section '.text':
0x00400400 31ed4989 d15e4889 e24883e4 f0505449 1.I..^H..H...PTI
0x00400410 c7c09005 400048c7 c1200540 0048c7c7 [email protected].. [email protected]..
0x00400420 f9044000 e8b7ffff fff4660f 1f440000 [email protected]..
0x00400430 b83f1060 0055482d 38106000 4883f80e .?.`.UH-8.`.H...
0x00400440 4889e577 025dc3b8 00000000 4885c074 H..w.]......H..t
0x00400450 f45dbf38 106000ff e00f1f80 00000000 .].8.`..........
0x00400460 b8381060 0055482d 38106000 48c1f803 .8.`.UH-8.`.H...
0x00400470 4889e548 89c248c1 ea3f4801 d048d1f8 H..H..H..?H..H..
0x00400480 75025dc3 ba000000 004885d2 74f45d48 u.]......H..t.]H
0x00400490 89c6bf38 106000ff e20f1f80 00000000 ...8.`..........
0x004004a0 803d910b 20000075 11554889 e5e87eff .=.. ..u.UH...~.
0x004004b0 ffff5dc6 057e0b20 0001f3c3 0f1f4000 ..]..~. ......@.
0x004004c0 48833d58 09200000 741eb800 00000048 H.=X. ..t......H
0x004004d0 85c07414 55bf200e 60004889 e5ffd05d ..t.U. .`.H....]
0x004004e0 e97bffff ff0f1f00 e973ffff ff554889 .{.......s...UH.
0x004004f0 e5897dfc 8b45fc5d c3554889 e54883ec ..}..E.].UH..H..
0x00400500 10c745f8 0a000000 8b45f889 c7e8dbff ..E......E......
0x00400510 ffff8945 fc8b45fc c9c3660f 1f440000 ...E..E...f..D..
0x00400520 41574189 ff415649 89f64155 4989d541 AWA..AVI..AUI..A
0x00400530 544c8d25 d8082000 55488d2d d8082000 TL.%.. .UH.-.. .
0x00400540 534c29e5 31db48c1 fd034883 ec08e855 SL).1.H...H....U
0x00400550 feffff48 85ed741e 0f1f8400 00000000 ...H..t.........
0x00400560 4c89ea4c 89f64489 ff41ff14 dc4883c3 L..L..D..A...H..
0x00400570 014839eb 75ea4883 c4085b5d 415c415d .H9.u.H...[]A\A]
0x00400580 415e415f c366662e 0f1f8400 00000000 A^A_.ff.........
0x00400590 f3c3 ..
readelf
hex dump of binary compiled with stack protection:
$ readelf -x .text protected_test
Hex dump of section '.text':
0x00400470 31ed4989 d15e4889 e24883e4 f0505449 1.I..^H..H...PTI
0x00400480 c7c05006 400048c7 c1e00540 0048c7c7 [email protected][email protected]..
0x00400490 90054000 e8b7ffff fff4660f 1f440000 [email protected]..
0x004004a0 b8471060 0055482d 40106000 4883f80e .G.`.UH-@.`.H...
0x004004b0 4889e577 025dc3b8 00000000 4885c074 H..w.]......H..t
0x004004c0 f45dbf40 106000ff e00f1f80 00000000 .].@.`..........
0x004004d0 b8401060 0055482d 40106000 48c1f803 .@.`.UH-@.`.H...
0x004004e0 4889e548 89c248c1 ea3f4801 d048d1f8 H..H..H..?H..H..
0x004004f0 75025dc3 ba000000 004885d2 74f45d48 u.]......H..t.]H
0x00400500 89c6bf40 106000ff e20f1f80 00000000 ...@.`..........
0x00400510 803d290b 20000075 11554889 e5e87eff .=). ..u.UH...~.
0x00400520 ffff5dc6 05160b20 0001f3c3 0f1f4000 ..].... ......@.
0x00400530 48833de8 08200000 741eb800 00000048 H.=.. ..t......H
0x00400540 85c07414 55bf200e 60004889 e5ffd05d ..t.U. .`.H....]
0x00400550 e97bffff ff0f1f00 e973ffff ff554889 .{.......s...UH.
0x00400560 e54883ec 20897dec 64488b04 25280000 .H.. .}.dH..%(..
0x00400570 00488945 f831c08b 45ec488b 55f86448 .H.E.1..E.H.U.dH
0x00400580 33142528 00000074 05e8b2fe ffffc9c3 3.%(...t........
0x00400590 554889e5 4883ec10 64488b04 25280000 UH..H...dH..%(..
0x004005a0 00488945 f831c0c7 45f00a00 00008b45 .H.E.1..E......E
0x004005b0 f089c7e8 a5ffffff 8945f48b 45f4488b .........E..E.H.
0x004005c0 55f86448 33142528 00000074 05e86efe U.dH3.%(...t..n.
0x004005d0 ffffc9c3 662e0f1f 84000000 00006690 ....f.........f.
0x004005e0 41574189 ff415649 89f64155 4989d541 AWA..AVI..AUI..A
0x004005f0 544c8d25 18082000 55488d2d 18082000 TL.%.. .UH.-.. .
0x00400600 534c29e5 31db48c1 fd034883 ec08e8f5 SL).1.H...H.....
0x00400610 fdffff48 85ed741e 0f1f8400 00000000 ...H..t.........
0x00400620 4c89ea4c 89f64489 ff41ff14 dc4883c3 L..L..D..A...H..
0x00400630 014839eb 75ea4883 c4085b5d 415c415d .H9.u.H...[]A\A]
0x00400640 415e415f c366662e 0f1f8400 00000000 A^A_.ff.........
0x00400650 f3c3 ..
Obviously this isn't very useful.
Discussion of pages and segments is not relevant since all the sections marked as executable in an ELF binary are mapped into the same segment (the text
segment).
How does the kernel/loader get this information when loading a page? How does it use it?
Use of -fstack-protector-all
has no bearing on section or segment permissions.
For info about the kernel program loader see:
- How programs get run
- How programs get run: ELF binaries
- How does the Linux kernel run a program
What is the x86 instruction used to set the page to read-only?
Segment permissions are set by the link editor: Segment Permissions
1. "Strong" stack protection for GCC
GCC's stack protection is software-based, and isn't related to DEP's hardware-based protection. When an OS enables DEP, all programs running on it (or some subset defined by the user) are automatically protected through hardware flags, regardless of any compiler flags used to build the binary.
When the stack protection flags are enabled in GCC, it provides protection by placing additional guards and checks when a function is called or returns to ensure that the program aborts rather than violate its integrity. This increases the executable size slightly and causes the code to run slightly slower (take more CPU time).
The loader nor the OS is aware of GCC's stack protection, nor does it need to be. As far as the OS is concerned, this is just normal executable code. The OS can provide DEP without stack protection enabled, and stack protection does not require DEP.
With DEP, if the IP (instruction pointer) lands outside of a CS (code segment), an exception is raised and the OS terminates the program. With GCC's stack protection, the stack is buffered with extra space that has certain values loaded, and checked to make sure that those buffered areas are not modified. If a function fails the guard check, the program is terminated to prevent further damage.