Sunday, June 5, 2016

Second Order Buffer OverFlow Attacks (SOBOF): Attacking underlying components


The C programming language is still prevalent in computing today. It may be tempting to think that interpreted languages like Java and Ruby have taken over, but this belief is unfounded or at least limited in scope. Even interpreted, Type-safe (in the sense of defining 'undefined' behavior as Exceptions), languages have had their applications ripped open by a buffer overflow. Not in a buffer they contained directly. No these errors exist in the core of the system. In the Kernel hooks and Drivers that every program reliant on features of the Operating System use.



While going over an example problem set for a C Buffer Overflow a student asked me why it was still important to know about buffer overflows when we would soon be moving all of our code into Java? Since I enjoy helping all of my fellow researcher (and aspiring researchers) I want to share the answer that I formulated for him.

The broad stroke of the answer is simply stated above. Even these type safe languages rely on vulnerable components. The details are a bit more complex though. Lets work backward

First an example snippet of a program with a Buffer Overflow (from Maryland University's Software Security Course material).

fptr  ptrs[3] = { NULL, get_wisdom, put_wisdom };

int main(int argc, char *argv[]) {

  while(1) {
      char  buf[1024] = {0};
      int r;
      fptr p = pat_on_back;
      r = write(outfd, greeting, sizeof(greeting)-sizeof(char));
      if(r < 0) {
        break;
      }
      r = read(infd, buf, sizeof(buf)-sizeof(char));
      if(r > 0) {
        buf[r] = '\0';
        int s = atoi(buf);
        fptr tmp = ptrs[s];
        tmp();
      } else {
        break;
      }
  }

  return 0;
}

If your C is rusty what that program is intended to do is allow a user to select a function from a command line menu. pointers to the start of the functions are stored in the ptrs[] buffer. Already you can smell the bad idea on this one....but lets roll with it. The program then creates a temporary function pointer from the selected index in the pointers array, and calls that function with tmp(). Obviously since there is no code doing bounds checking this is going to be trivial to overflow. Effectively we can look up the address of any pointer to any function simply by finding the difference in their addresses and dividing it by the size of a pointer (in x86 it is 4 bytes)

Now, suppose for a moment that instead of this program taking input directly from a user, it is part of a networkControl.DLL. It takes input from another program by calling a setMode function. In fact any program that can hook a DLL could potentially use this library to make the OS networking features work for it.

Let's take a Python application that loads this DLL as an example.
#create an INET, STREAMing socket
serversocket = socket.socket(
    socket.AF_INET, socket.SOCK_STREAM)
#bind the socket to a public host,
# and a well-known port
serversocket.bind((socket.gethostname(), 80))
#become a server socket
serversocket.listen(5) 
while 1:
    #accept connections from outside
    (clientsocket, address) = serversocket.accept()
    #now do something with the clientsocket
    #in this case, we'll pretend this is a threaded server
    ct = client_thread(clientsocket)
    ct.run()
    chunks = []
    bytes_recd = 0
    while bytes_recd < MSGLEN:
       chunk = self.sock.recv(min(MSGLEN - bytes_recd, 2048))
       if chunk == '':
            raise RuntimeError("socket connection broken")
       chunks.append(chunk)
       bytes_recd = bytes_recd + len(chunk) 
    lib = ctypes.WinDLL('networkControl.dll')
    #lib = ctypes.WinDLL('full/path/to/networkControl.dll')
    func = lib['setMode'] # Loads the vulnerable interface function
    value = func(''.join(chunks)) #feeds socket input to the vulnerable interface function

 In theory, anyone who can send information on the open socket has full access to the vulnerable C program. This type of secondary compromise is exactly what attackers have to be on the look out for. The only way you would know that this Python program is vulnerable to a stack overflow is if you were to follow it's execution back through the DLL and you were able to spot  this (fairly glaring when you think about it) security hole.

This is just one simple to follow example too. If you start to look for holes in the C underlying your system you may find that alot of programs you believed were secure by syntactic inheritance are not!

No comments:

Post a Comment