How to Read Return Address Even With Canary
Stack canaries or security cookies are tell-tale values added to binaries during compilation to protect disquisitional stack values like the Return Arrow against buffer overflow attacks. If an incorrect canary is detected during certain stages of the execution flow, such as right before a return (RET), the program volition be terminated. Their presence makes exploitation of such vulnerabilities more difficult. Only not impossible.
In this blog post, we will be discussing:
- What stack canaries await like
- What kinds of stack canaries can be found
- When compilers add them
- How they can be circumvented
Nosotros volition be looking at 32 and 64 bit binaries, assembly (though no fluency is expected), /GS. For this article, we will be using a simple C program on a 32 flake Linux system.
Prelim – buffer overflows
Earlier we discuss stack canaries, we must first introduce buffer overflows. This form of attacks makes use of unsafe functions (usually in C or C++) that allow writing of capricious content exterior a designated surface area of memory.
Consider the following snippets of lawmaking. The functionality of the programme is not important, we are mainly interested in the execution flow in memory.
# | C | Assembly |
1 two 3 4 5 six 7 8 9 | int master(void) { askUser(); <rest of main function> } void askUser(void) { char name[100]; gets(name); … } | <function prologue main> 0x0804850c call 0x0804851f <askUser> 0x08048511 <balance of main function> … 0x0804851f <office prologue askUser> 0x08048524 <classify room for name[100]> … 0x0804859b call 0x080483a0 <gets@plt> |
Tabular array i. Example plan
This is a simple C program that has a chief() part and an askUser() part. The main() role calls askUser(), which in plough has a local variable chosen name[] of size 100 into which a user input is beingness read through gets().
When a function is called in a compiled binary (come across line ii), the address of the adjacent instruction inside principal() will first be pushed onto the stack. This will serve as the Return Arrow. When the return (RET) teaching is chosen at the stop of askUser(), the return arrow will be popped off the stack and placed into the instruction pointer (EIP in 32 scrap architecture). If an assailant tin overwrite this Return Pointer, they tin can redirect the execution period of the program, oft to a location the attacker desires.
Such overwrites are possible when a library function called within askUser() does not perform correct bounds checking, often in string operations. Functions such as gets() and strcpy() practice not perform any bounds checking during their operation. The read() function takes a size equally argument, simply does not check if this size corresponds to the size of the buffer where the data is written to.
If a user inputs more characters than the buffer can contain (in this example 100), the gets() function volition keep on writing outside name[]'due south memory space. Effigy 1 and 2 brandish this in the Gnu Debugger (GDB). For the purposes of this commodity, we will not hash out the Saved Frame Arrow (SFP), which serves to restore the Base Pointer (EBP) to the calling function'due south stack frame. Overwriting only the Saved Frame Pointer tin can also lead to an exploitable condition. In the example of Figure i and ii, a buffer of 28 bytes is foreseen for the second input from the user. As this input is read through the vulnerable gets() office, a large input tin can overwrite the Render Pointer on the stack.
The double arrows in Figure 2 indicate the position of the Return Pointer on the stack. Earlier the overwrite (light-green arrows), information technology is pointing into the master() function but afterward the telephone call to askUser(). The backtrace command (bt) shows the call chain pointing into an address in main().
After the overwrite, the Return Arrow was overwritten with our arbitrary B'southward (ASCII hex value 42, as shown on Figure 2). The call concatenation shows 42'due south every bit the render pointer. This overwrite is possible considering of a call to the gets() office, which does non perform bounds checking on the input from the user.
Stack canaries - intro
1 way to prevent the stack-based buffer overflow above from being successful, is introducing a stack canary simply before the SFP and the RP. This token value will be added by the compiler and serve as a alert that the SFP and RP may be overwritten. Figure 3 displays this schematically.
In the debugger, this would look like Figure 4. The orange boxes indicates the canary, the xanthous boxes bear witness the Return Pointer. The canary will alter at every run of the plan, making for a difficult to predict value. The Null byte at the first of the canary (Intel processors utilize little endian, and so the last byte shown will be the first written) would nowadays a trouble for many string operations to print.
Canaries in 64 fleck programs follow the same principles, but volition use the extra 32 bits for entropy. In Figure 5, nosotros see the canary in orangish and the Return Pointer in yellow.
Stack canaries – their check
Stack canaries volition be checked for their value just before the return to the calling part, which is the moment at which the aggressor volition gain command over the educational activity pointer as their overwritten value for the return pointer is loaded into the instruction pointer.
Programs compiled with canaries will look different inside debugger, with added instructions merely before the function epilogue and the subsequent return.
# | Assembly – no canary | Assembly – with canary |
1 two 3 4 5 half dozen 7 8 | <within askUser part> 0x0804856e exit 0x0804856f ret | <inside askUser function> 0x080485d9 mov eax, <canary> 0x080485dc xor eax, <right canary val> 0x080485e3 je 0x080485ea 0x080485e5 phone call <__stack_chk_fail@plt> 0x080485ea exit 0x080485eb ret |
Table 2. Plan assembly without and with canary introduced.
Equally nosotros can see in Table 2, the compiled version of the askUser role contains an extra check in lines half-dozen and 7. This cheque will copy the current canary value from the stack to the EAX register (line 2), XOR it against the stored correct value (line 3), and spring to the function epilogue if the values are equal (line 4). If the bank check fails, the stack_chk_fail function volition be called (line 5). This will terminate the program, without the aggressor ever gaining command of the Return Pointer and subsequently the Instruction Pointer.
Stack canaries – types
At that place are different types of stack canaries that a compiler tin add to a programme. Table 3 contains an overview of the near common types and how they offering protection.
Type | Example | Protection |
Zip canary | 0x00000000 | 0x00 |
Terminator canary | 0x00000aff | 0x00, 0x0a, 0xff |
Random canary | <any 4-byte value> | Usually starts with 0x00 |
Random XOR canary | Usually starts with 0x00 | |
64-bit canary | <8 bytes> | |
Custom canary |
Table 3. Types of canaries.
Many buffer overflow vulnerabilities are acquired by string operations such equally gets(), strcpy(), read(). Strings in C are commonly terminated using a single Nothing byte (0x00). An attacker would not exist able to use such a byte in their payload through a string operation to reconstruct the canary. The 0x0a byte represents a line feed, unremarkably also terminating string operations. 0xff corresponds to an End Of File (EOF), terminating certain string operations equally well.
The cipher canary would be the simplest for the compiler programmer to implement. It places four NULL bytes just before the SFP and RP. Equally this is a predictable value, an attacker may all the same be able to featherbed the canary. The read() function, which is vulnerable to buffer overflows, does allow NULL bytes to be written.
The terminator canary introduces two more hex values that attempt to terminate string operations, 0x0a and 0xff. These values are once more anticipated, and tin exist bypassed with relative ease nether the right atmospheric condition.
A random canary will offer better protection. Information technology usually consists of a Goose egg byte followed past 3 random bytes. The NULL byte would attempt to cease string operations, while the 3 random bytes volition brand the canary less predictable to the attacker.
The random XOR canary volition be like the random canary, except it volition exist XOR'ed confronting a not-static value in the program (usually the Base Pointer EBP). As operating systems nowadays run with Address Space Layout Randomization (ASLR) activated, EBP will not be static across runs of the program. This adds an extra layer of randomization to the cookie, making it hard to predict this value.
Application of stack canaries
The Linux C compiler gcc currently contains the Stack Smashing protector, which will introduce a random canary if /dev/urandom is available. In the absenteeism of that source of random data, it will revert to a terminator canary. gcc only introduces canaries in specific cases. Functions with buffers over eight bytes and calls to alloca(), the function that allows for allotment of memory space on the stack, volition be protected by a canary. The programmer can introduce canaries for all functions with the –fstack-protector-all compiler flag, but this will probable hinder performance of the program.
The cookie value is generated at the offset of the process and volition remain the same during the running of the programme. Whatever forks of the process volition contain the same cookie value. This offers some bypass opportunities, as we will discuss subsequently.
Microsoft Visual Studio has the /GS flag that introduces canaries – named Security Cookies by Microsoft[1]- to vulnerable functions. This option became available as of Visual Studio 2003, and is switched on by default since Visual Studio 2005. It volition add a cookie for buffers over 4 bytes and that are non a pointer, and structures over 8 bytes that do not contain a pointer.
[ane] https://docs.microsoft.com/en-...
Stack canary bypasses
There are multiple ways in which stack canaries tin be bypassed. The first technique involves leaking out the cookie value through a retentiveness leak vulnerability. Format string vulnerabilities are splendid for this purpose. This can work against all types of canaries, with the possible exception of the Random XOR type. Furthermore, equally the standard random canary added by the Gnu C compiler gcc does not XOR against EBP, information technology remains static during the execution of the plan. Come across Effigy 6. The format string payload is in the yellow box, the canary in the orangish box.
If the canary is of the zippo or terminator kind, information technology may not be effective in preventing certain writes. As discussed before, the read() function volition permit us to write null bytes to a buffer, effectively disabling the security the null bytes should add. Furthermore, if multiple buffers can be written sequentially, the attacker may take employ of null termination of strings in C to write the required Nothing bytes into the canary position.
In some occasions, animal forcing the canary may be successful. When using a random canary on a 32 bit system, the canary volition have 24 bits of entropy. This is due to the viii $.25 (1 byte) being used for the Zilch byte. ii^24 possibilities of randomisation makes xvi.777.216 possible canary values. In a local privilege escalation exploit, xvi million guesses could well exist within the bounds of a beast strength assault.
On 64 bit systems, that entropy increases to 2^56 or 7.twenty * x^xvi possibilities. This would be less feasible.
However, our guessing can be steered a bit. If nosotros guess the canary byte by byte, we volition be able to discern when nosotros have guessed the right value. This is possible because an incorrect estimate for a byte volition generate a stack smashing error, where a right byte gauge will yield no such fault. The maximum number (worst case) of guesses will remain 2^24, but the average number will subtract.
The canary tin can also exist avoided if the attacker tin can overwrite a buffer and and so cause an exception to occur within the same function. On Windows systems, this would trigger the structured exception handler (SEH) to intervene to solve the exception. If the SEH arrow is also overwritten, this tin can also lead to control over the instruction pointer. The SEH routine will not check canary values before handing execution to the handler.
Protection against stack canary bypasses
There is no silver bullet solution to protecting against stack canary bypasses. If a program has a memory leak vulnerability, the attacker could leak out the cookie value. Just the random XOR'ed canary would offer protection against this. The Furthermore, if an aggressor tin can trigger an exception earlier the cookie is checked, code execution can still be obtained. The writeability of certain bytes (Nil, LF, EOF) will depend on the vulnerable part.
Before we motility into the most meaning protection against stack canary bypasses, it should be considered whether a program needs to be written in a language that expects the developer to do memory direction. For some applications, this will certainly be the case. C and C++ offer college computing performance compared to college level languages. However, for some applications, these conditions are not fulfilled. Using languages that have care of memory management will greatly subtract the likelihood of a canary existence bypassed.
By far the greatest protection against canary bypassing lies in the very reason of the canary'southward existence. Stack canaries were invented to forbid buffer overflow (BOF) vulnerabilities from being exploited. This BOF is the root problem that needs to exist addressed. To brand a pocket-size analogy, an umbrella can protect against getting wet, just simply non walking in the pelting will exercise that much more than efficiently.
Buffer overflow vulnerabilities occur when no bounds checking is beingness washed on buffer operations. Functions such equally gets() and strcpy() exercise no such bounds checking. This is why they accept been deprecated and replaced by fgets() and strlcpy(). These functions conduct correct bounds checking and, if correctly used, remove the buffer overflow vulnerability. Googling will effortlessly yield a list of insecure C functions and their safer variants.
When inspecting import address tables of Windows executables or Procedure Linkage Tables on Linux executables, ane tin can easily find many programs still importing these unsafe functions rather than their replacement variants. While there may be good technical reasons to use these unsafe variants rather than their replacement variant, the ubiquity of these dangerous functions raises the question of necessity.
This issue is non unlike the continuing need to repeat that patching systems is vital, yet still many environments remain insufficiently patched. The use of unsafe functions has been deprecated a long time ago, and this advice should definitely be adhered to.
Michiel Lemmens
@mchllmmns
Standing on the shoulders of giants:
Source: https://www.sans.org/blog/stack-canaries-gingerly-sidestepping-the-cage
0 Response to "How to Read Return Address Even With Canary"
Post a Comment