Skip to content

Exercises related to Robust Design Patterns - Part 2

Stack

Leveraging stack Overflow

Exercice 1

In an interesting article of Arstechnica - How security flaws work: The buffer overflow the following is stated:

The problem of buffer flows is more serious: they often lead to code execution. This happens because those overflowed buffers won’t just overwrite data. They can also overwrite the other important thing kept on the stack—those return addresses. The return address controls which instructions the processor will execute when it’s finished with the current function; it’s meant to be some location within the calling function, but if it gets overwritten in a buffer overflow, it could point anywhere. If attackers can control the buffer overflow, they can control the return address; if they can control the return address, they can choose what code the processor executes next.

In the same article, the Morris Worm ( The buffer overflow - Blame C ) is explained. Have a go at this and explain:

  1. what was the main attack vector used?
  2. what leveraging means was part of the attack?
  3. what is the intrinsic challenge with buffer handling?
Solution
  1. The fingerd program listened for network connections on port 79 and using gets(). The function would spawn the finger program, passing it the username if there was one. The finger program was the one that did the real work of listing users or providing information about any specific user. fingerd was simply responsible for listening to the network and starting finger appropriately - but was the one the attacker leveraged to generate an overflow.
  2. The fact that no buffer size was controlled - nor boundary set - allowed attackers to easily generate a buffer overflow. In fact, gets() is a function that will read from standard input until the person at the keyboard presses return - no matter whether the entered data fits the buffer
  3. Generally, C has a fundamental weakness: buffers do not know their own size, and the language never validates the reads and writes performed on buffers, allowing them to overflow. This needs to be understood - and taken into account - by the developer. In addition, even though some of C’s string handling functions do take a parameter for the buffer size, they can do so in a way that still leads to errors and overflows. For instance, C offers a pair of siblings to strcat() and strcpy() called strncat() and strncpy(). The extra n in their names denotes that they take a size parameter, of sorts. But n is not, as many naive C programmers believe, the size of the buffer being written to; it is the number of characters from the source to copy.
  4. The Trick: The Payload Is the Shellcode
    The input string sent to fingerd was crafted to contain two things at once:
    • Padding bytes to fill the buffer up to the return address
    • The shellcode (raw machine instructions) itself, embedded directly in the overflow data
      The layout on the stack looked roughly like this:
       [ shellcode bytes ][ ... padding ... ][ new return address ]
               ^                                        |
               |________________________________________|
                 return address points back HERE
      
      So the overflowed buffer contained machine instructions (not a string in any meaningful sense), and the overwritten return address was set to point back into that same buffer on the stack.