• LOGIN
  • No products in the cart.

Return Oriented Programming

Introduction

 

    1. Since 1988, the Morris Worm stack overflow has been a nightmare for developers. Several countermeasures have been created to avoid this kind of attack. Compilers are pioneers in developing such techniques.
    2. Sadly, few programmers know very much about compilers’ options as they usually compile programs with inherited procedures. For instance, the very well known GCC compiler has a stack protection with the fstack-protector option [1].
    3. In the middle of the past decade, manufacturers introduced the No-eXecute  (NX) bit which prevents the execution of code beyond the text area of a program. When this bit is ON, the processor sends a signal to the Operating System (OS). In addition, it is also necessary for the Operating System to be instructed to stop the code execution. In Windows, this is achieved by activating the Data Execution Prevention.
    4. Readers must be aware that the NX bit does not prevent stack overflow and only prevents the execution of injected code. So, if you are able to exploit such a vulnerability, you are completely free to write  anything you like in the stack. However, a clever hacker may think…. “Of course, I can’t execute code but I can alter the normal flow of execution, making the program go to another address by means of overwriting the return address located in the stack”.
    5. As a concept of proof,  we will work with this simple program:

 

  • #include <ctype.h>
  • #include <stdio.h>
  • #include <string.h>
  • int tabla[5] = {91, 92, 93, 94, 95};
  • {
  • FILE *fd;
  • int in1,in2;
  • int arr[20];
  • char var[20];
  • if (argc !=2){
  •  printf(mensaje0,*argv);
  •  return -1;
  • }
  • fd = fopen(argv[1],”r”);
  • if(fd == NULL)
  • {
  •  fprintf(stderr,mensaje1);
  •  return -2;
  • }
  • memset(var,7,sizeof(var));
  • memset(arr,6,20*sizeof(int));
  • while(fgets(var,20,fd))
  • {
  •  in1 = atoll(var);
  •  fgets(var,20,fd);
  •  in2 = atoll(var);
  •  /* fill array */
  •  arr[in1]=in2;
  •  //printf(“%d – %d\n”, arr[in1], tabla[in1]);
  •  if (arr[in1] != tabla[in1])
  •  {
  •  printf(“Sorry values are no correct!\n”);
  •  return ;
  •  }
  •  printf(“Correct”. The process follows\n”);
  •  printf(“Your are in the core of the program\n”);
  •  return;
  • }

 

  1. }
    1. Code Logic
    2. The program reads a file with 2 lines; each line contains a number (in1 & in2), in1 is used as the index. If the value contained in the cell table[in1]  is equal to in2, then the process is OK and will continue; otherwise, the process terminates.
    3. In a real environment, the table will be out of the program, even encrypted or secured with another security measure; but for us, this is not relevant because the only matter we must deal with is the return address.
    4. Readers may wonder at these odd initializations:

 

  • memset(var,7,sizeof(var));

 

  1. memset(arr,6,20*sizeof(int));
  2. They are only just a trick to make these values more visible in the stack area. And this is what happens when parameter values contained in the file are: 2 (in the first line) and 93 (in the second line):

1

  1. And as shown in the next figure, this is what will happen when the parameter file contains incorrect values: 2, 95 :

1

Now, we start the program under Ollydbg [2] and we should make a breakpoint when jumping depending on the values in the parameter file. When parameters are set correctly, the following snapshot should appear. Take a look:

1

As shown, the program jumps to 0x4017F2 and follows the normal execution (in this example, the normal execution is only a message). If the data is not correct, a  “Data are not correct….” message appears. Afterwards, control is transferred to address 0x40180C. Now, let’s take a look at the stack:

Untitled

Due to special initializations, it’s easy to locate the variable areas. We focus on address 0x22FF2C; this is a return address and we can be 90% sure this return address would be used for RET instruction at address 0x40180C. We put another breakpoint in this address for it to continue execution until this point as shown:

1

ESP points to address 0x22FF2C. This is our target!!!.

What to do next? We must overwrite this address with value 0x4017F2, directly addressing the normal part of the program. This entry in the stack is in an offset of 6 above our work areas. The program does not check values in parameter so if we changed the first parameter to a value of 26, we can overwrite this entry. The second value must be  0x4017F2 in decimal: 4200434

This image clarifies the settings:

1

we must see the message first, which is telling us that the input is not correct.  Afterwards, because we have changed the value of the return address, messages will tell us your data are correct as follows:

1

can take advantage of a vulnerability without injecting code and the exploit works even while the program is running in a system with Data Execution Prevention.

One Step More…

 

The explained technique above is only one way for exploiting a buffer overflow but there are more ways.

Another way is called return-to-libc. With ret2libc, we change the return address with the address of a system function and its parameters. Usually a calling to system() function. The latter technique I had explained is called return chaining. We see with an example. Look at the following figure:

Untitled

We have identified the following instructions, each one is followed by RET instruction:

  • pop a
  • pop c
  • mov [ecx], eax.

Also, there is a RET leading program at the address: 0x684a0f4e.

1

These instructions extract value on the top of the stack. And the following RET extracts value which transfers control to:

Code at this address is:

Untitled

As anterior set of instructions, after extracting value from the stack and loading in the ECX

1

register transfers control to this code:

The final result will be as in the following figure:

1

This set of values is called “gadget”; a patient hacker can recollect a large set of instructions’ addresses followed by a ret and make a catalogue. Then by combining the needed values, he can execute instructions as if the code is being injected.

We can see gadgets like notes written by criminals in old movies:

1

 

Conclusion

 

    1. In this article, I introduced how easy a hacker can exploit a stack overflow in an NX bit protected system and the other protections that we must not neglect as well such as compiler options and Address Space Layer Randomization (ASLR). Only when these protections are working together,  we must think about a hardened programming environment.

References

 

[2] “Ollydbg is a powerfull debugger” , < http://www.ollydbg.de >

 

About The Author

 

Juanma Menéndez is a system engineer with a strong experience in programming with a wide range of languages and  operating systems. He is currently working as a Senior Consultant at Atos Spain.

Juanma is also the author of z3r0 r0ws (http://zerorows.blogspot.com.es), a blog specialized in security and system programming.

He can be reached via LinkedIn: http://es.linkedin.com/in/juanmamenendez/

 

 

 

December 4, 2014

0 responses on "Return Oriented Programming"

Leave a Message

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

© HAKIN9 MEDIA SP. Z O.O. SP. K. 2013