FTP


Nmap discovered a FTP server running on the port 2121 of the 192.168.210.214 host. The running service is vsftpd 3.0.5.

Null Session


┌──(kali㉿kali)-[~/PEN-200/PG_PRACTICE/spidersociety]
└─$ ftp $IP -P 2121
Connected to 192.168.210.214.
220 (vsFTPd 3.0.5)
Name (192.168.210.214:kali): 
331 Please specify the password.
Password: 
530 Login incorrect.
ftp: Login failed
ftp> ^D
221 Goodbye.
 
┌──(kali㉿kali)-[~/PEN-200/PG_PRACTICE/spidersociety]
└─$ ftp ftp@$IP -P 2121 
Connected to 192.168.210.214.
220 (vsFTPd 3.0.5)
331 Please specify the password.
Password: 
530 Login incorrect.
ftp: Login failed
ftp> ^D
221 Goodbye.
 
┌──(kali㉿kali)-[~/PEN-200/PG_PRACTICE/spidersociety]
└─$ ftp anonymous@$IP -P 2121 
Connected to 192.168.210.214.
220 (vsFTPd 3.0.5)
331 Please specify the password.
Password: 
530 Login incorrect.
ftp: Login failed
ftp> ^D
221 Goodbye.

The target FTP server does not allow null session. A valid credential is required.

ss_ftpbckuser Session


An FTP credential was retrieved from the target Web application; ss_ftpbckuser:ss_WeLoveSpiderSociety_From_Tech_Dept5937!

┌──(kali㉿kali)-[~/PEN-200/PG_PRACTICE/spidersociety]
└─$ ftp ss_ftpbckuser@$IP -P 2121
Connected to 192.168.210.214.
220 (vsFTPd 3.0.5)
331 Please specify the password.
Password: ss_WeLoveSpiderSociety_From_Tech_Dept5937!
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> rstatus
211-FTP server status:
     Connected to 192.168.45.182
     Logged in as ss_ftpbckuser
     TYPE: ASCII
     No session bandwidth limit
     Session timeout in seconds is 300
     Control connection is plain text
     Data connections will be plain text
     At session startup, client count was 1
     vsFTPd 3.0.5 - secure, fast, stable
211 End of status
 
ftp> put test
local: test remote: test
229 Entering Extended Passive Mode (|||40421|)
150 Ok to send data.
     0        0.00 KiB/s 
226 Transfer complete.

Successfully authenticated. Additionally, write access is granted.

ftp> ls
229 Entering Extended Passive Mode (|||44808|)
150 Here comes the directory listing.
-rwxr-xr-x    1 0        0            1391 Apr 14 17:53 404.html
drwxr-xr-x    2 0        0            4096 Apr 14 17:53 images
-rwxr-xr-x    1 0        0            4317 Apr 14 17:53 index.html
drwxr-xr-x    2 0        0            4096 Apr 14 17:53 libspider
-rwxr-xr-x    1 0        0            1345 Apr 14 17:53 simple.py
-rw-------    1 1002     1002            0 Jun 26 15:55 test
226 Directory send OK.

Checking the directory reveals that this appears to be the web root directory for the target Web server.

ftp> cd libspider
250 Directory successfully changed.
ftp> put test 
local: test remote: test
229 Entering Extended Passive Mode (|||41334|)
553 Could not create file.

Unable to write to the /libspider directory where a PHP web application is being hosted.

simple.py


ftp> more simple.py
#!/usr/env python3
import http.server
import os
import logging
 
try:
    import http.server as server
except ImportError:
    # Handle Python 2.x
    import SimpleHTTPServer as server
 
class HTTPRequestHandler(server.SimpleHTTPRequestHandler):
    """
    SimpleHTTPServer with added bonus of:
 
    - handle PUT requests
    - log headers in GET request
    """
 
    def do_GET(self):
        server.SimpleHTTPRequestHandler.do_GET(self)
        logging.warning(self.headers)
 
    def do_PUT(self):
        """Save a file following a HTTP PUT request"""
        filename = os.path.basename(self.path)
 
        # Don't overwrite files
        if os.path.exists(filename):
            self.send_response(409, 'Conflict')
            self.end_headers()
            reply_body = '"%s" already exists\n' % filename
            self.wfile.write(reply_body.encode('utf-8'))
            return
 
        file_length = int(self.headers['Content-Length'])
        with open(filename, 'wb') as output_file:
            output_file.write(self.rfile.read(file_length))
        self.send_response(201, 'Created')
        self.end_headers()
        reply_body = 'Saved "%s"\n' % filename
        self.wfile.write(reply_body.encode('utf-8'))
 
if __name__ == '__main__':
    server.test(HandlerClass=HTTPRequestHandler)

This appears to be a simple Python web server that supports file upload via PUT requests. It checks if the file already exists, then it saves it if it doesn’t exist. However, this does not seem to apply to the target web server as it is running Apache.

/libspider/users.php


ftp> more users.php
<?php
$users = [
    "admin" => "admin"
];
?>

admin:admin

/libspider/login.php


ftp> more login.php
<?php
session_start();
include 'users.php'; // Load user credentials
 
function login($username, $password) {
    global $users;
 
    // Validate user credentials
    if (isset($users[$username]) && hash_equals($users[$username], $password)) {
        $_SESSION["user"] = $username;
        return true;
    }
    return false;
}
 
if ($_SERVER["REQUEST_METHOD"] === "POST") {
    $username = trim($_POST["username"] ?? '');
    $password = trim($_POST["password"] ?? '');
 
    if (login($username, $password)) {
        echo json_encode(["success" => true]);
    } else {
        echo json_encode(["success" => false, "message" => "Invalid username or password."]);
    }
    exit();
}
?>

N/A

/libspider/fetch-credentials.php


ftp> more fetch-credentials.php
 
 
<?php
// fetch-credentials.php
 
// Path to the credentials file
$credentialsFile = __DIR__ . '/.fuhfjkzbdsfuybefzmdbbzdcbhjzdbcukbdvbsdvuibdvnbdvenv';
 
// Function to parse the credentials file
function getCredentials($filePath) {
    if (!file_exists($filePath)) {
        die('Credentials file not found.');
    }
 
    $lines = file($filePath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
    $credentials = [];
 
    foreach ($lines as $line) {
        if (strpos($line, '=') !== false) {
            list($key, $value) = explode('=', $line, 2);
            $credentials[trim($key)] = trim($value);
        }
    }
 
    return $credentials;
}
 
// Fetch credentials
$credentials = getCredentials($credentialsFile);
$user = $credentials['FTP_BACKUP_USER'] ?? null;
$pass = $credentials['FTP_BACKUP_PASS'] ?? null;
 
// Check if the request is coming from contro-panel.php
/* if ($_SERVER['HTTP_REFERER'] !== 'http://yourdomain.com/contro-panel.php') {
    http_response_code(403);
    die('Unauthorized access.');
} */
 
// Check if the user is authenticated
session_start();
if (!isset($_SESSION['user'])) {
    http_response_code(401);
    die('User not authenticated.');
}
 
// Respond with the credentials
header('Content-Type: application/json');
echo json_encode([
    'FTP_BACKUP_USER' => $user,
    'FTP_BACKUP_PASS' => $pass
]);

It would appear that the /libspider/fetch-credentials.php file parses the FTP credential from the .fuhfjkzbdsfuybefzmdbbzdcbhjzdbcukbdvbsdvuibdvnbdvenv file from the __DIR__ directory, which is a PHP magic constant to denote the current directory.

.fuhfjkzbdsfuybefzmdbbzdcbhjzdbcukbdvbsdvuibdvnbdvenv


Hidden files from within a FTP session can be listed using the -la flag.

ftp> more .fuhfjkzbdsfuybefzmdbbzdcbhjzdbcukbdvbsdvuibdvnbdvenv
Failed to open file.

Reading the file is impossible as it is owned by 33(www-data) and has its permission bits set to 0400. This would mean it can only be accessible from the web server.

The file also contains a DB credential; spidey:WithGreatPowerComesGreatSecurity99! Testing it against the target SSH server for credential reuse.