We Digress

 

But do not feel overwhelmed by the length of this journey. All you ever need do is focus on one thing, what you are doing. Stay on the path and put one foot in front of the other - that is all. There is joy in the struggle.

 P. T. Sudo

GroundWork

If only we had headed those words. At this time an article was published on the web that detailed a certain tool to use in reverse engineering wtfu4. We should have heeded the advice quoted above from P. T. Sudo - we didn't. We took a whole load more notice of the article than perhaps we should have done with hindsight, but we were learning. We were having problems at this stage trying to debug wtfu4 using just gdb alone. We had only ever debugged programs we had source code for before this challenge, so we were used to having more information to start off with, so we decided to try fenris and follow the instructions in the article. Sorry, Mr. fenris writer, but we couldn't get the damn thing to work, and we didn't have time to work out why. Some missing binaries, downloaded them, couldn't get them to install. You know the story, we've all been there - most times we have the time and patience to resolve the issue and move on. Our patience was very thin, and time was pressing so we tried to find some other tools. It was here where we found biew a very useful binary editor and viewer for ELF binaries. But wait, that doesn't build on our test system either. *sigh*. A quick look through the makefiles and we have our solution, we just need to hardcode some variables and we will be done. For some reason the Linux system we were using didn't correctly substitute some config variables, so for the purpose of getting it going we hardcoded them. It was still quicker than trying to get fenris working which was more complicated to resolve.

Having read the document indicating how fenris could help us we decided that stopping the program forking would probably cure some of the problems we were having in gdb.

How the fork do We stop that?

The fork() system call is visible within the disassembled code as all system calls are - int 0x80 - and can be identified using the include file <asm/unistd.h>. From that file we can see that the fork() call is actually system call 0x2. This was located and could be stopped from happening in exactly the way the fenris article states, we identified the following code section in the disassembly and the used biew to modify the code to simply return 0.

[root@WhiteHat root]# diff original.asm nofork.asm
2c2
< original:     file format elf32-i386
---
> ./the-binary.nofork:     file format elf32-i386
19102,19103c19102,19104
<  80571eb:     b8 02 00 00 00          mov    $0x2,%eax
<  80571f0:     cd 80                   int    $0x80
---
>  80571eb:     b8 00 00 00 00          mov    $0x0,%eax
>  80571f0:     90                      nop
>  80571f1:     90                      nop

This worked and we now had a binary that didn't fork. We loaded it into gdb and tried to step through it again. We wanted to try and set a breakpoint we knew would be hit to start with, so we tried breaking on the line in the fork above.

Copyright 2001 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux"...(no debugging symbols found)...
(gdb) break 80571eb
No symbol table is loaded.  Use the "file" command.
(gdb)

Ok, so we tell it the binary (we thought we had already done that, but anyway)

(gdb) file ./the-binary.nofork
Reading symbols from ./the-binary.nofork...(no debugging symbols found)...done.
(gdb)

No debugging symbols... what a laugh. So now it knows what file it is, we should be able to break - right?

(gdb) break 80571eb
No symbol table is loaded.  Use the "file" command.
(gdb)

Uhhmmm, didn't we just tell you that. We don't know how to use gdb, help... please ... somebody! Now, we can't remember where the next gem came from, but it proved to be the turning point. We were entering the address wrong, it needed to be a de-referenced address pointing to that memory location, after all, a program is simply executing instructions held in a certain memory block. We were off. No stopping us now, except we don't understand assembler still. We've made some initial guesses, but we are still stuck on that minor detail. If only we could disassemble to C-like code. You must be able to do that somehow. Anyway, back to the task at hand. We set the breakpoint at the fork code and we had it... it stopped. Now, just examine the memory and we will know exactly what this binary is doing. OK. How do we do that?

No symbol table is loaded.  Use the "file" command.
(gdb) break *0x080571eb
Breakpoint 1 at 0x80571eb
(gdb)

And ...

(gdb) run
Starting program: /opt/HoneyNet/TheHoneyNet/reverse/./the-binary.nofork
warning: shared library handler failed to enable breakpoint

Program exited with code 0377.
(gdb) quit

Damn, forgot to run it as root. No matter, we had a means to debug the binary a little now. In fact, helping prevent the fork hadn't helped us in reality, we were still unable to read or debug assembler.

Stumped!

OK, so now we had wasted a good few hours - yet again - and we appeared to have got to the same point we had been at before the publication of the fenris paper. Perhaps this had been misinformation to throw competitors off the track, who knows? Anyway, we were determined to find at least some answers in there so we went back to our friend Google and this time we were going to search for tools that would help us determine the structure of the program so we didn't have to stare at that damn assembler for too long, it was beginning to make our eyes (and brains) hurt. so we searched on terms like "+'reverse engineering' +'linux' +'ELF binaries' +'C code'. Jackpot!, there was a program called REC we'd seen this page in a previous search but thought nothing of it, we weren't looking for that at the time. OK, it's a Windows(tm) executable but it looks like it will help. We were here, we had arrived, this was the key to unlocking wtfu4 - excellent. Now we could make haste, but wait... what are all those weird symbols " *(ebp - 2048)"? Good job we have experience with C otherwise we would be lost now, this is as close as it gets, but it still looked weird. We just used the defaults and produced the file wtfu4_dumps/binary.rec, but the interactive mode was good too. We quickly identified the fork point and the position of the process renaming

    edx = *ebx;
    *edx = *"[mingetty]";	// new process name
    *(edx + 4) = *"getty]";
    *(edx + 8) = *"y]";
    *(edx + 10) = *"";
    (save)1;
    (save)17;	
    L080569BC();		// sighandler
    esp = esp + 20;
    if(L080571E8() != 0) {	// fork()
        (save)0;
        L08055FBC();
    }
    L0805733C();
    (save)1;
    (save)17;
    L080569BC();
    esp = esp + 8;
    if(L080571E8() != 0) {	// fork()
        (save)0;
        L08055FBC();
    }

Now, let's locate that pesky recv call. Uhhmm, harder than it looked we will need to know what these strange symbols mean. We have provided a summary of what we believe are the important memory locations in the recv area and what they are probably pointing to. The section of the wtfu4_dumps/binary.rec we were interested in was the function at "L08048134()" - this was called from within the "__entry_point__()" function. The file wtfu4_dumps/main.rec contains the main loop annotated - including the important memory sections (pointers). From that we concluded that the following lines were the recv call and the subsequent decisioning code.

    for(*(ebp + -17636) = ebp + -4536; 1; L080555B0(10000)) {
        esi = L08056B44( *(ebp + -17608), ebp + -2048, 2048, 0);
        if(*( *(ebp + -17616) + 9) == 11 && *( *(ebp + -17620)) == 2 && esi > 200) {

Now we were rocking, in little under an hour from obtaining this tool we had gained more than we had in 20+ hours analysing it using other tools. We could now marry this up to the assembly code we had been banging our heads against for the past few days. The important sections of this have been annotated in the file wtfu4_dumps/binary.asm starting at offset 0x0804820c. The important function we wanted to study was the call immediately following the recv call. Hold on a minute there's something else here. We looked closely at the actions to be taken immediately following the recv call and found 3 conditions that had to be met by the incoming buffer before we would get any type of response and be able to trace into the decoder. These were:

  1. The recieving buffer had to be > 200 bytes.

  2. The character pointed to by *(ebp + -17616) + 9 must be 11 (0xb).

  3. The character pointed to by *( *(ebp + -17620)) must be 2.

Now we had a problem, so we put together a primitive client that talked raw tcpip to port 11. We made it send 200+ bytes of garbage and we made sure character uhhmm, 1 was a 2 and character 9 was 11. We were just guessing to try and get a response. We went back to our friend gdb and started debugging the binary, issuing a breakpoint at *0x080482e5 and stepping through the instructions. We won't bore you with all the detail, but we did take a very long time to work out how to do that. Being used to debugging C-code with debug symbols we were trying to issue commands relevant to that context, step, next, etc. However, there were assembler equivalents that stepped through instructions at a time. stepi was the most important of these instructions. It allowed us to step through the program and note the effect on the %eip register, this we already knew from our understanding of buffer overflows was the instruction pointer to the next executable instruction. We had many problems trying to work this section out, we were using ethereal to determine which byte was being compared as we stepped through and examined the registers, but didn't appreciate the simplicity until later. We then modified the wtfu4 binary to compare to this value instead of the 0x2 value of the original. Using this technique we managed to get wtfu4 to go past the decision point and step into the decoder. Phew, we were getting somewhere at last. The listing in src/early_client.c demonstrates one of the early clients we used to try and debug wtfu4. We hadn't yet worked out the significance of the 0x2, only that by editing wtfu4 we could bypass this check, and by getting gdb to display the contents of the memory at *( *(ebp + -17620)) using x/c $ebp - 17620 which formats the memory address pointed to as a character we knew how to modify the code to check for this character.

Using gdb in this way for the investigation of the decoder was hard work. working at the command line has its advantages in some circumstances, but we needed a GUI for this, there were that many changes internal to the memory space of the running instance of wtfu4 that we couldn't possibly map out what was going on. We were filling reams of paper trying to step through this. We needed a GUI debugger. Enter kdbg - a graphical debugger for the KDE environment. It is a tool in it's infancy, but it did everything we needed to be able to do with wtfu4