0

It is stated here:

A terminator canary contains NULL(0x00), CR (0x0d), LF (0x0a) and EOF (0xff) -- four characters that should terminate most string operations, rendering the overflow attempt harmless.

I know the null (0x00) can help prevent strcpy, strncpy, stpcpy, and strcat. Also LF (0x0A) can work for gets and fgets.

What are 0x0D and 0xFF good for stopping?

Seanoseanohay
  • 341
  • 4
  • 19
  • 1
    `0x0d` == `\r` == CR is the line terminator in the Apple world. – DrKoch Aug 07 '15 at 06:04
  • @DrKoch It ***was*** the line terminator, about 15 years ago. Not any more. – r3mainer Aug 07 '15 at 06:10
  • As noted in the quoted text, 0xFF is EOF, so some programs will think that the end of the file has been reached and stop processing. – Raymond Chen Aug 07 '15 at 06:13
  • 2
    EOF is actually -1 (but some programs think it is 0xff). – Thomas Dickey Aug 07 '15 at 08:56
  • 1
    @ThomasDickey 0xff is -1 if you interpret it as a signed 8 bit integer in two's complement representation.( a `char ` in many common C implementation) – nos Aug 07 '15 at 15:14
  • @nos I noticed this in my tests that when I print the bytex (%02x) all of the other bytes will show up like x0A, x0D, x00. But FF will show up as 0xffffffff. So I believe you are correct. – Seanoseanohay Aug 07 '15 at 15:23
  • `0xFF` is good for breaking weak code as in `char ch; while ((ch = fgetc(stdin)) != EOF) foo(ch);`. `int ch;` should have been used as `fgetc()` returns values in the `unsigned char` range and `EOF` and `char` cannot distinguish all those values. – chux - Reinstate Monica Aug 07 '15 at 15:40
  • Part of the answer lies here in this question: http://stackoverflow.com/questions/15484106/reaching-eof-with-fgets however that doesn't address the semantics of 0x0D or 0xff. These are going to be entirely context dependent. It could be that they are legacy signals for EOF/EOL, however to know we'd need to know a specific implementation. – Daniel Aug 07 '15 at 16:09

1 Answers1

1

The purpose of the "canary" is to detect when a buffer has been overrun. It's placed just before the return address of the stack, after any buffers allocated in the current stack frame. If its value changes then the stack checking code know buffer has been overrun and aborts the program before it can do any damage.

The problem with this is that if the attacker overwrites the canary with the same value as it had before then buffer overrun won't be detected. To make this more difficult either a random number is used as the canary, so the attacker can't predict it, or the special "terminator canary" value you're asking about is used. The byte values that make up the terminator canary were chosen because they will terminate various copy operation used by programs. If the these values are in the string ("shellcode") the attacker uses to try overwrite the return value with then most code that would copy the string will stop before overwriting the return value.

Here are examples of memory copying operations that would be terminated before over writing the return value if the terminate canary appears in the source input:

NUL terminatation

char buf[10];

strcpy(buf, src);

The example above of the most common case. Any operation copying a C string with stop a the first NUL (0) byte in the source string.

EOL termination

char buf[10];

gets(buf);

Using gets is common beginner mistake and doesn't normally appear in production code, but it's not hard to write more sophisticated code that reads a line but doesn't take care to not overflow the buffer. What marks the end-of-line depends on the convention. The usual Unix convention is to use a single line feed character (LF, 0x0A), but Windows uses the carriage return and line feed (CR LF, 0x0D 0x0A) sequence. Since the terminator canary contains both the CR and LF byte values, both EOL terminators are present in the canary. Any operation copying a single line will stop before overwriting the return value.

Broken EOF termination

char buf[10];
char *dest = buf;

while(1) {
    char c = getchar();
    if (c == EOF) {
        break;
    }
    *dest++ = c;
}

How the EOF terminator works here is harder to see and explain than in other two examples. In addition to the buffer overflow bug, this code contains another bug that leads to 0xFF being interpreted as EOF. Like using gets, this is also a rookie mistake, but one that's more common in production code. The bug is caused by using char c instead int c.

The value returned by getchar is actually an int rather than char. This makes it possible to distinguish valid byte values from special EOF return value, which on most systems is -1. Byte value read from the file are returned as unsigned char values cast to int. So the byte value '\xFF' in the file is returned as the int value 255. When this is assigned to char variable c its truncated from (on most systems these days) a 32-bit signed integer value to an 8-bit signed integer value. This turns 32-bit signed integer value 255 into the 8-bit signed integer value -1. This conversion also turns the 32-bit signed integer value -1 (EOF) into the 8-bit signed integer value -1.

Since both '\xFF' and EOF end up being converted to -1, they both end up comparing as equal to EOF. This means that in the example code above when either value is returned by getchar the loop will terminate. Any code that makes this sort of mistake will stop copying at the first '\xFF' byte in the source input. However code that uses getchar, getc, fgetc or a similar function correctly, assigning the return value to an int, will continue to copy past any '\xFF' bytes.

Summary

The terminator canary makes it so an attacker can't exploit a given buffer overrun in code, if the overrunning code uses a NUL (0), EOL (0x0D and/or 0x0A) or a broken EOF (0xFF) comparison to terminate the copy. My guess is that most case of buffer overruns are made unexploitable by NUL being in the terminator canary. Many of the remainder would be protected by the EOL characters, while the broken EOF byte probably doesn't have much applicability at all.

Ross Ridge
  • 38,414
  • 7
  • 81
  • 112