Iv. Stack Canaries
First look at the evolution history of Stack Canaries:
Stack Guard was the first to be implemented using the Canaries probe, which was released as an extension of GCC in 1997. The original version of Stack Guard uses 0x00000000 as canary Word. Although many people recommend that stack guard be included in GCC as part of GCC, it provides the protection of stacks. In practice, however, the GCC 3.x does not implement any stack protection.
GCC4.1 began with the introduction of the Stack-smashing Protection (SSP, also known as ProPolice), which implemented two functions:
stack protection by inserting canaries
The variable rearrangement mechanism, where the array in the local variable is placed at the high address of the stack, and other types of variables are placed on the low address of the stack, making it more difficult to overwrite other variables by overflow
There are several ways to generate canaries values:
Terminator Canaries
Because the vast majority of overflow vulnerabilities are caused by C string handlers that do not check array bounds, these strings are NULL as the terminating character. Choosing a character such as NULL, CR, LF, and so on as Canary word becomes a natural thing. For example, if Canary Word is 0x000aff0d, in order for the overflow to not be detected, an attacker would need to include 0x000aff0d in the overflow string and accurately calculate the location of the Canaries so that the Canaries does not appear to be altered. However, 0x00 in 0x000aff0d causes strcpy () to end replication to prevent the return address from being overwritten. And 0x0a will make the Get () end read. The inserted Terminator Canaries has created a lot of trouble for the attackers.
Random Canaries
This kind of canaries is randomly generated. And such random numbers are usually not read by the attacker. This random number is generated when the program is initialized and then saved in a memory page that is not insinuate to the virtual address space. This causes segment fault to be thrown when an attacker attempts to access memory that holds a random number through a pointer. But since the copy of this random number will eventually be saved as canary Word in the function stack, it is still possible for an attacker to get the value of canary Word through the function stack.
Random XOR Canaries
This canaries is obtained by a random number and all control information in the function stack, the return address through an XOR operation. In this way, the Canaries in the function stack or any control information and return address are modified to be detected.
A picture helps to understand:
Below, through debugging, explore the specific implementation of canaries in GCC. is still using the Ubuntu x86 environment, GCC version 4.9.2.
The program code used is as follows:
void func () { int i; Char buffer[]; 1 ; buffer[0'a';} int Main () { func (); return 0 ;}
Compile the program to open stack protection and remove stack protection separately:
[Email protected]:~/workdir/canaries$ gcc-fstack-protector-o demo_sp demo.c
[Email protected]:~/workdir/canaries$ gcc-fno-stack-protector-o DEMO_NOSP demo.c
Shows the disassembly code for the Func function, respectively.
No stack Protection code:
(gdb) Disass func
Dump of assembler code for function func:
0x080483eb <+0>: Push%EBP
0x080483ec <+1>: mov%esp,%ebp
0x080483ee <+3>: Sub $0x50,%esp
0x080483f1 <+6>: Movl $0x1,-0x4 (%EBP)
0x080483f8 <+13>: Movb $0x61,-0x44 (%EBP)
0X080483FC <+17>: Leave
0X080483FD <+18>: ret
End of assembler dump.
(GDB)
It is observed that the program compiled with the-fno-stack-protector option does not have any protection on the stack.
To open the Stack Protection code:
(GDB) Disass func
Dump of assembler code for function func:
0X0804843B <+0>: Push%EBP
0x0804843c <+1>: mov%esp,%ebp
0x0804843e <+3>: Sub $0x58,%esp
0x08048441 <+6>: mov%gs:0x14,%eax #读取gs寄存器, generating canaries
0x08048447 <+12>: mov%eax,-0xc (%EBP) #在栈底部写入Canaries
0x0804844a <+15>: Xor%eax,%eax
0x0804844c <+17>: Movl $0x1,-0x50 (%EBP)
0x08048453 <+24>: Movb $0x61,-0x4c (%EBP)
0x08048457 <+28>: mov-0xc (%EBP),%eax
0x0804845a <+31>: Xor%gs:0x14,%eax #函数返回前, check canaries value
0x08048461 <+38>: je 0x8048468 <func+45> #若未改变, jump to + 45 normal return
0x08048463 <+40>: Call 0x8048310 <[email protected]> #若发生栈溢出, execute __stack_chk_fail function
0x08048468 <+45>: Leave
0x08048469 <+46>: ret
End of assembler dump.
(GDB)
Where the value read from the GS register is random for each function call, we use GDB debug to verify:
(GDB) B *0x08048447
Breakpoint 4 at 0x8048447
(GDB) R
Starting program:/HOME/EZ/WORKDIR/CANARIES/DEMO_SP
Breakpoint 4, 0x08048447 in Func ()
(GDB) I r eax
EAX 0xa3850c00-1551561728
(GDB) R
Starting program:/HOME/EZ/WORKDIR/CANARIES/DEMO_SP
Breakpoint 4, 0x08048447 in Func ()
(GDB) I r eax
EAX 0xcc46de00-867770880
By tracing the __stack_chk_fail function, it can be found that its implementation is more complex. The general process is to call the __gi___fortify_fail function first, __gi___fortify_fail call the __libc_message function, __libc_message the last call Backtrace_and_maps and _ _gi_abort function, generate SIGABRT signal, and through __gi___libc_secure_getenv according to the system environment variables to decide whether to produce Coredump file, __gi_abort execution to the last Call _exit, program exit.
On the origin of the name of Canary, I found an interesting little story about the canary in the mine.
V. Summary
- All exp with arbitrary code execution ability to bypass the above protection mechanism, this article just take buffer overflow example, the method is universal;
- Exploit the key is to control the IP register, but do not have to directly cover, the kernel of a large number of OPS structure, virtual function table, exception handling functions, got, etc. are very good goals;
Linux_x86 NX and ASLR Bypass Technology (cont.)