Thursday, August 2, 2018

Walkthrough: Bulldog: 2 Vulnerable VM

First post in four years.  I do different stuff now.

This is a walkthrough of the Vulnhub VM Bulldog: 2 vulnerable virtual machine by Nick Frichette.  Full disclosure: Nick and I used to work together briefly, and I shamelessly asked him for hints, which he generously gave.

Spoilers ahoy.


I'm going to do a very honest walkthrough here, so I'll point out where I got stuck, where I cheated or begged Nick for help, and where I screwed up in predictable ways.  Enjoy my suffering.


If you aren't familiar with Vulnhub it's easy to get started.  Just install Virtualbox, download the VM from the link above and double click the OVA, and it should install straight to Virtualbox.  When it finishes booting Nick has thoughtfully included a script that displays the IP it picks up from DHCP right there on the boot screen.  This saves scanning for it.


If you do scan with nmap, which is usually the first thing to do to a vulnerable VM, you will discover that port 80 is the only thing open.  This is evidently a webapp challenge, my bete noir.  Forewarned is forearmed.


Bulldog 2's web page suggests that it's a social media site run by a social media company that's been hacked before (see Bulldog 1) and as a result has shut down registration for new users.  There are a few things you can poke around on, including a login page, but no obvious way to login.  It's at this point where on a webapp I typically view the source in Chrome Developer Tools, deobfuscate any javascript (the little curly bracket button beneath the script source) and save it off for analysis.


I am not a JavaScript wizard, much less a fancy modern JavaScript framework wizard, but I noticed pretty quickly that the login and registration functions for this app are built in to the client side, and that even though the registration page doesn't work, the endpoint for it is still there and still should take a post request. 



l.prototype.onRegisterSubmit = function() {


                var l = this
                  , n = {
                    name: this.name,
                    email: this.email,
                    username: this.username,
                    password: this.password
                };


}
            return l.prototype.registerUser = function(l) {
                var n = new x.Headers;
                return n.append("Content-Type", "application/json"),
                this.http.post("/users/register", l, {
                    headers: n
                }).map(function(l) {
                    return l.json()
                })
            }


And then I got stuck and asked Nick for help, because trying to send the post request got me a HTTP 502 error.  As it turns out, I was on the right track, it's just that, you know, the post parameters are JSON and JSON requires quotes.  Which I knew.  Duh.  But Nick's hints do make me think there are other ways in I missed as well.  Once you do it correctly, it's pretty easy to register a new user (click to embiggen):





When you log in as your new user, you get a JWT token, and here I cheated for the second and final time: I was pretty sure the JWT token could be manipulated, but not how.  It turns out that this is basically JSON encoded with Base64, so I'm sure it can be manipulated a lot of ways (those wacky web developers) but I chose an online encoder/decoder.  As far as how to manipulate it, I'd already spotted an admin role in the javascript source below, so I just changed my auth level to that 'master_admin_user' and re-encoded:



l.prototype.isAdmin = function() {


                var l = localStorage.getItem("user");
                return null !== l && "master_admin_user" == JSON.parse(l).auth_level
            }









Doing that, and then changing the locally stored value to the re-encoded one with the Chrome Developer Tools, got me a nifty new Admin link on the web page.  Clicking that link gets you a secondary login page:






The line about using a CLI tool to log in seemed to me to be a strong hint about command injection.  Unfortunately, it's a blind command injection.  After much messing with both parameters I was about to give up and try SQL Injection or something when I realized the reason my ping wasn't working was a firewall.  Basic infrastructure troubleshooting FTW.







Since the vulnerable machine had Internet access (on an isolated vlan) I was able to use one of my favorite reverse shell one-liners.





Success!  Bulldog 2, The Reckoning:





For privilege escalation I decided to punish myself a bit since I'd already cheated and rather than uploading one of the Linux privilege escalation checker scripts I went for enumerating the box by hand.  Fortunately about the third thing I tried was a global find for writable files, which turned up a glaring suspect.




Ruh roh.


Here I took another detour.  I immediately went for a classic:



echo "r00t:x:0:0:root:/root:/bin/bash"  >> /etc/passwd


It turns out that this does not work on modern Linuxes.  You will get an authentication error when you try to su as r00t with a blank password.  After a bit of Googling I determined that you have to set a password with the correct encoding.





x




After this is done, you can do the same trick to echo a new root user into the file, this time with a working password, and then su to the user.



Subsequent to this you are expected to do the root dance.  I won't spoil the final message for you in case you want to walk through it yourself.


This was a great VM.  It's unusual for its use of modern Javascript frameworks and I learned a great deal from it that I may use on actual pentests, should anyone be foolish enough to allow me to pentest webapps.  Overall I'd give it a strong thumbs up even if I didn't know Nick, and I look forward to his next one.

1 comment:

  1. Interesting and amazing how your post is! It Is Useful and helpful for me That I like it very much, and I am looking forward to Hearing from your next.. what do dogs dream about when they cry?

    ReplyDelete