Overview
In an effort to safeguard our customers, we perform proactive vulnerability research with the goal of identifying zero-day vulnerabilities that are likely to impact the security of leading organizations. Recently, we decided to take a look at the 3CX Phone Management System with the goal of identifying an unauthenticated remote code execution vulnerability within the web-based management console.
During our analysis we did not identify any unauthenticated remote code execution vulnerabilities. However, we did identify a local privilege escalation vulnerability impacting the Windows version of the application and also identified a post-authentication arbitrary file read vulnerability within the management console. In this case, an attacker with access as an unprivileged local user on the system could exploit this vulnerability to elevate privileges to NT AUTHORITY\SYSTEM. This vulnerability was assigned CVE-2024-25085.
From discussions with 3CX, we learned that this vulnerability only impacts version 18 of the application. 3CX also stated that most of their users are running the Linux version of the application. The Linux version of the application also allowed attackers to execute code within the PostgreSQL process. However, on Linux, the permissions associated with the PostgreSQL service account were more restricted and thus didn’t allow for immediate privilege escalation to root. This vulnerability has been remediated in Version 20 Update 1 and all subsequent versions of the 3CX application.
Why 3CX Phone Management System?
Our team decided to focus on the 3CX Phone Management System as the application was leveraged by a few of our Chariot customers and the application was very widely used by a variety of organizations. We leveraged some very simple searches on Shodan and identified over two-hundred thousand instances of the application with a large presence in the United States and other developed countries (see Figure 1).
Figure 1: On Shodan we observed there were over two-hundred thousand instances of the 3CX Phone System Management Console application deployed on the Internet.
The Vulnerability That Never Was
At this point, we began our initial review of the applications architecture and observed that the 3CX Phone Management System leveraged an Nginx service, which proxied incoming requests to various backend services deployed by the application. We thought it might be worthwhile to review the Nginx configuration file used by the service for various misconfiguration issues. When examining the Nginx configuration file, we identified what appeared, at first, to be an off-by-slash misconfiguration vulnerability (see Figure 2).
Figure 2: Praetorian reviewed the Nginx configuration file for potential misconfigurations and observed what appeared could be a potential off-by-slash vulnerability.
We identified a directory adjacent to the “Reports” directory called webroot with a file named config.json. This config.json file contained privileged database credentials to the PostgreSQL instance installed by the 3CX Phone Management systems (see Figure 3 and Figure 4).
Figure 3: We identified a directory adjacent to Reports, the web root directory containing a file named config.json. This file appeared to contain privileged PostgreSQL database credentials.
Figure 4: We observed that the config.json file in the adjacent webroot directory contained PostgreSQL database credentials.
At this point, we excitedly attempted to download config.json using the potential off-by-slash vulnerability and were disappointed when we received a 404 in response to our exploitation attempt (see Figure 5).
Figure 5: An attempt to exploit a potential off-by-slash vulnerability in the application failed as the system file path was not prepended with a slash.
Even though the system wasn’t vulnerable to an Nginx off-by-slash vulnerability we thought it was pretty interesting that the only thing standing between us and a potential arbitrary file read of a highly sensitive configuration file was a single slash in an Nginx configuration file. We added a prepended slash to the alias directly to confirm our hypothesis (see Figure 6).
Figure 6: We modified the alias directive to include a slash prepended to the file path directive.
After making this modification and rebooting the system we were able to send the same request as we did in Figure 7 to the system and successfully obtained the core application configuration file. This configuration file included database credentials for the PostgreSQL service running on the system (see Figure 7).
Figure 7: An example where we would have been able to leak a very sensitive configuration file had the file path specified in the configuration file been prepended with a slash.
If this vulnerability had existed an attacker could have leveraged this vulnerability to download the PostgreSQL administrative credentials for the application. Then, if the PostgreSQL service was externally accessible an attacker would have been able to completely compromise the application and likely would have been able to execute code as NT AUTHORITY\SYSTEM through the PostgreSQL service.
We thought this was quite interesting as effectively the only thing standing between us and full remote code execution on an appliance with over two-hundred thousand internet-facing installations was a single slash in an Nginx configuration file. The risk of this misconfiguration is compounded by the fact that the PostgreSQL service deployed by the 3CX application also binds and listens on all interfaces. If the application firewall doesn’t restrict access to the service on port 5485 then the PostgreSQL service will be accessible without authentication by default (see Figure 8).
Figure 8: We observed that the PostgreSQL service deployed by the 3CX application runs as the NT AUTHORITY\SYSTEM user and listens on all available interfaces.
For those interested in learning more about these types of Nginx configuration issues Frans Rosen has several articles he has published that detail some of these common Nginx configuration issues in more depth. The first article is titled Common Nginx misconfigurations that leave your web server open to attack and the second article is titled Middleware, middleware everywhere – and lots of misconfigurations to fix.
Digging Into the Config.json File
At this point, we were feeling quite disappointed, that the potential off-by-slash vulnerability turned out to be a false positive due to a single missing slash. However, this did prompt us to dig into the config.json file and the permissions associated with it (see Figure 9). We observed that any local system user could read this configuration file and execute the theoretical attack chain we mentioned previously. This wouldn’t lead to remote code execution, but would likely lead to local privilege escalation on the system as the PostgreSQL service ran as the NT AUTHORITY\SYSTEM user account.
Figure 9: We observed that any unprivileged operating system user on the system could read the contents of the config.json file within the ProgramData directory.
Examining the PostgreSQL Service (Windows)
We then began digging into the privileges associated with the PostgreSQL service account and observed that the service ran as the NT AUTHORITY\SYSTEM user account on the system (see Figure 10). We then queried the privileges associated with the user account within the configuration file and observed that the user account has full superuser/administrative privileges within PostgreSQL (see Figure 11).
Figure 10: We observed that the PostgreSQL service installed by the 3CX Phone Management System was running as the NT AUTHORITY\SYSTEM user account.
Figure 11: We connected to the PostgreSQL service leveraging the credentials recovered from the config.json file and observed that our phonesystem user account had administrative privileges within PostgreSQL.
Escalating Privileges Leveraging the PhoneSystem User
At this point we knew that the phonesystem user account had superuser permissions on the system. Next, we needed to prove that we could leverage the privileges associated with this user account to run arbitrary code within the context of the PostgreSQL service on the system. We determined that PostgreSQL had functionality to load an arbitrary dynamic linked-library (DLL) file from disk (see Figure 12).
Figure 12: We leveraged administrative access to PostgreSQL to attempt to load a malicious DLL named pgsql.dll into the PostgreSQL service.
After executing the query we observed that the PostgreSQL service process attempted to load the pgsql.dll file specified in the previous query (see Figure 13).
Figure 13: We then observed in ProcMon that the postgres.exe service, which ran as NT AUTHORITY\SYSTEM, attempted to load our malicious DLL file.
Next, we compiled a custom PostgreSQL extension which spawned a reverse shell to execute code within the context of the PostgreSQL service. The article RCE with PostgreSQL Extensions from HackTricks provides source code for an example PostgreSQL extension which spawns a reverse shell. Unfortunately, it was a little tricky to compile an extension that would be loaded by the PostgreSQL service. To accomplish this we had to:
- Ensure that the extension is compiled against the matching major version associated with the PostgreSQL version being targeted for testing.
- Ensure that the extension code isn’t being compiled as C++ code as this would mangle the symbol names and result in an error message indicating that PG_MODULE_MAGIC is not found.
We received a callback with the ability to run commands as the NT AUTHORITY\SYSTEM user account. However, we observed that the PostgreSQL service dropped common privileges associated with the NT AUTHORITY\SYSTEM user account (see Figure 14).
Figure 14: We observed that while the PostgreSQL service ran as the NT AUTHORITY\SYSTEM user account the service was missing some of the permissions we would typically expect when running the whoami /priv command.
This was in direct contrast to the expected output of the whoami /priv command when run as the NT AUTHORITY\SYSTEM user account (see Figure 15).
Figure 15: The expected output of the whoami /priv command when running as NT AUTHORITY\SYSTEM.
We attempted to add a new administrative user leveraging the reverse shell and received an access denied error message. Despite running as the NT AUTHORITY\SYSTEM user account, our privileges were somewhat limited (see Figure 16).
Figure 16: We attempted to elevate privileges using our reverse shell using different methods such as adding a new administrative account on the system.
However, we were able to achieve privilege escalation and add a new administrator account by overwriting a service account binary which ran with full NT AUTHORITY\SYSTEM privileges and then restarting the service so our malicious code would run with full privileges (see Figure 17 and Figure 18).
Figure 17: We overwrote the binary used by the 3CXEventNotificationManager service and then restarted the service to obtain full administrator privileges using the reverse shell under the PostgreSQL service discussed previously.
Figure 18: The payload we leveraged to add the new administrative user account named attacker.
We confirmed the newly created administrator account had full administrative privileges (see Figure 19). Of course, from an operational perspective, there are much more subtle ways to escalate privileges than simply adding a new administrative user account. However, we thought this was sufficient for an initial proof-of-concept exploit to prove out the existence of the vulnerability.
Figure 19: We confirmed that our newly created attacker administrator account had full administrative privileges on the system.
We reported this local privilege escalation issue to 3CX and the vulnerability was assigned CVE-2024-25085.
What are the likely exploitation scenarios in this case?
There are two primary exploitation scenarios where we believe this vulnerability would be considered relevant in the context of an enterprise network environment:
- Overly Broad Remote Desktop Protocol Access Permissions: In a rather significant number of environments, we have observed overly broad remote desktop permissions where certain groups have unprivileged remote desktop access to a large number of servers within an environment. An attacker with these types of remote desktop permissions could leverage RDP access to gain code execution within the context of an unprivileged user account and then escalate privileges to NT AUTHORITY\SYSTEM using this vulnerability. Next, the attacker could leverage this system as a jumping off point for further attacks such as performing local LLMNR/NBNS poisoning with the elevated privileges or by installing a malicious security support provider on the system so that they harvest credentials of other users logging into the system.
- Exploitation using Access to the vSphere Web Console: An attacker with unprivileged access to the vSphere web console and unprivileged domain user credentials could potentially expand their access by logging into the server running the 3CX management console as an unprivileged domain user then escalating privileges on the system using the vulnerability outlined previously. The ability to perform this action is governed by the allow log on locally setting in Windows. In most configurations, this setting is not modified and unprivileged domain users with physical control access can login to servers as unprivileged user accounts. A similar exploitation scenario would be possible outside of vSphere in scenarios where exposed keyboard virtual mouse (KVM) interfaces are identified.
A Linux Post Authentication Arbitrary File Read
We found another interesting feature during our application review. We installed the 3CX system on both Windows and Linux and we noticed that the Linux installation management console included a “Terminal” feature. Due to its name and probable functionality we decided to investigate further to ascertain its implementation security. The feature, available only on Linux servers hosting the software (it was not present on our Windows install), provides a small group of commands useful for server diagnostics and debugging. Arbitrary command execution is not permitted by default through this feature as the set of available commands is restricted.
Figure 20: The Terminal feature in the Management Interface
Figure 21: The list of supported commands in the terminal interface.
The backend implementation of the functionality (contained in “ConsoleCommandHandler.cs”) filters the command input. A regex filter strips out most bash special characters and another check is performed to validate that the provided command matches one contained in the permitted list.
Figure 22: The Regex that filters out special shell characters.
Figure 23: The list of supported commands.
We attempted to bypass the “IsCommandValid” filter but were unable to run any command that was not already included in the allowed list. We also were not able to sneak any special bash characters by the regex filter that would allow us to execute a command.
After our command injection attempts were unsuccessful, we searched the GTFOBins project for unintended functionality of the allowed commands and found that both “ip” and “date” would permit arbitrary file read. An attacker can run the “ip” command with “ip -force -batch /path/to/file/to/read” to retrieve file contents (which is slightly corrupted due to the ip error output). For the “date” command the attacker specifies the file to read, “date -f /path/to/file/to/read”. The “date” output is not truncated and returns the entire file.
Figure 24: Using the IP command to read a file.
Figure 25: Using the date command to read a file.
We also found, from GTFOBins, that the “ip” command allows for arbitrary command execution. However, our 3CX deployment (the .iso download from the 3CX website) did not run the 3CX management console as root. If the 3CX management console is running as root, an attacker can execute arbitrary commands with “ip netns add test” and “ip netns exec test CMD”.
Figure 26: Attempting to execute commands via “ip” fails when adding an ip namespace fails due to lack of permissions.
Executing the same command on the 3CX server as root returns successfully and results in command execution. If the server is configured to run the 3CXManagementConsole as root or another privileged process, the command execution will work through the terminal interface.
Figure 27: The command execution worked on the server as root.
Figure 28: The ManagementConsole was running as a non root user.
We reported the finding to 3CX and they informed us that the Terminal feature is being removed from new versions of the 3CX software, the Terminal feature removal will fix the file read vulnerability we identified. Due to the relatively low amount of risk associated with this issue we decided not to apply for a CVE for this issue.
Conclusion
In this article we discussed the results of our research into the 3CX Phone System Management Console. We discussed a local privilege escalation and authenticated arbitrary file read vulnerability we identified within the management console.
It’s quite interesting how in vulnerability research the difference between a serious security issue and an unexploitable condition could come down to something as simple as a single slash within an application configuration file. While we didn’t succeed in our goal of identifying a serious unauthenticated remote code execution vulnerability, we did identify a couple of lower-risk issues we thought were quite interesting and really enjoyed taking a look at the application.
Proactive vulnerability research allows us to identify critical security vulnerabilities in applications within a client’s attack surface before an attacker has a chance to exploit them. Chariot, by monitoring and categorizing external assets, helps us to identify applications that warrant further review by our team.