Pwn-RET Address Overwrite

Last week, I saw a challenge of pwn. The source code has been provided, and what you are asked to do is to pwn the vulnerable program. 

Here is the source code in C language. Your goal is to exploit the vulnerability and let the program print as many coupons as it can. 

#include <stdio.h>
#include <string.h>
#include <stdlib.h> 
#include <unistd.h>
#include <time.h>
 
void coupon(char * arg)
{
    char name[16];
    char * stuff[4] = {"iPad 2", "HTC Thuderbolt", "iPhone 4", "Motorola Xoom"};
 
    strcpy(name, arg);
    printf("Our loyal customer %s:\n", name);
    printf("Use this coupon to redeem your free %s!\n", stuff[rand()%4]);
    printf("Coupon number %d\n\n", rand());
}
 
int main(int argc, char ** argv)
{
    if (argc < 2) {
        printf("usage: %s your_name\n", argv[0]);
        return 0;
    }
 
    srand(time(0));
    coupon(argv[1]);
 
    return 0;
}

Goal: to print a lot of coupons!
• you can only launch the target once, of course
• Think about how to manipulate address to cause this to go into a loop, without adding a loop.

Try

When I was glancing the challenge at the first beginning, I thought it could not be difficult. After all, I major in Information Security and have some basic knowledge about application security, though my focus is on web security. My init idea is like this.

  1. when the function main() calls the function coupon(), the arguments for  coupon() will be pushed into the stack in the order from right to left. Here, argv[1] is the only argument whose address will be pushed.
  2. program will push the return address, and then call the function coupon() and jump into this function. When the instructions are executed, the final instruction ret will lead the program to jumping to the address in the stack top. This address is the return address pushed in the step 1.
  3. with the existence of the function strcpy() inside the coupon(), we can input a long enough string as the argv[1] to overwrite the return address of the function coupon()
  4. to loop the printing of coupons, we can overwrite the return address as the return address of the function coupon() or main().

Problem

I thought my thought was absolutely correct. However, with this method, the program was only able to print two coupons and exit with an exception.

By debugging step by step with gdb, I located the problem. Actually, overwrite the return address is not enough. Another thing we need to do is to recover the content in the registers and stack to avoid exceptions of reading invalid data. 

To be specific, here are two screenshots taken at the same breakpoint at different time. The first one was taken at the first time the program met the breakpoint,  while the second one was taken after the program was redirected by the overwritten return address. 

1. First time reach breakpoint

Obviously, different register value and stack value will lead to a crash. 

Solution

Then I tried to solve this problem. 

After comparing data in the stack and register in two stages, I came up with my solution — use ROP to pop useless data and recover the stack shown as screenshot 2 to screenshot 1. Here I used ropgadget in gdb-peda.

gdb-peda$ ropgadget
ret = 0x804832e
popret = 0x8048345
pop3ret = 0x8048629
pop2ret = 0x804862a
pop4ret = 0x8048628
addesp_12 = 0x8048342
addesp_16 = 0x8048435

Finally, I got the payload.

r $'AAAAAAAAAAAAAAAAAAAAAAAAAAAA\x28\x86\x04\x08AAAABBBBAAAAAAAA\x28\x86\x04\x08AAAAAAAAAAAAAAAA\x2e\x83\x04\x08\x59\x85\x04\x08'

With pop4ret twice and popret one time, the stack was recovered. Then overwrite the return address as the beginning address of the function main() and we can run the program in an infinite loop.

PS: the reason why so many coupons are the same is that the seed of the random function is time. You will get the same coupons in the same second.. 

Leave a Reply

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