On February 27, 2024, Progress released a security advisory for OpenEdge, their application development and deployment platform suite. The advisory details that there exists an authentication bypass vulnerability which effects certain components of the OpenEdge platform. Our proof of concept can be found here.
When the OpenEdge Authentication Gateway (OEAG) is configured with an OpenEdge Domain that uses the OS local authentication provider to grant user-id and password logins on operating platforms supported by active releases of OpenEdge, a vulnerability in the authentication routines may lead to unauthorized access on attempted logins.
Similarly, when an AdminServer connection is made by OpenEdge Explorer (OEE) and OpenEdge Management (OEM), it also utilizes the OS local authentication provider on supported platforms to grant user-id and password logins that may also lead to unauthorized login access.
OpenEdge Architecture
The OpenEdge platform has several different components that can be deployed across an environment:
- OpenEdge Management (an enterprise version of OpenEdge Explorer)
- OpenEdge Enterprise RDBMS
- OpenEdge Replication
- OpenEdge Authentication Gateway (OEAG)
- Progress Application Server (PAS) for OpenEdge
- Progress Develop Studio (PDS) for OpenEdge
Typically the OpenEdge Management, OpenEdge Enterprise RDBMS, and PAS roles are deployed on a system and act as the backend, central source of information for developers using PDS as clients to develop applications. If an the Authentication Gateway is in use, it centrally manages authentication across the OpenEdge ecosystem.
Figure 1. Example OpenEdge Deployment
Finding The Vulnerable Component
In this case, we were unable to obtain a patched system to perform patch diffing, but there are quite a few interesting details that can be picked from the advisory. The advisory states: “The AdminServer logins are always potentially vulnerable because they only support OS local logins”. Additionally the temporary mitigations specify:
The following mitigation options are intended for short-term use until you can apply the provided OpenEdge Update to your deployments. The revised “auth.dll” library associated with the OS you’re using should be copied into $DLC/bin to replace the vulnerable version of the “auth.dll” library that existed in LTS Updates 11.7.18, 12.2.13 or 12.8.0.
Given this information, we install OpenEdge Manager, RDBMS, and PAS on a single Windows server and inspect the installed services with TaskManager and find that these roles will start the vulnerable “AdminServer” service referenced in the advisory.
Figure 2. AdminServer service running
Now that we have the vulnerable component running – an often overlooked part of reversing is spending hours reading documentation, we find documentation on the AdminServer service and what its used for. The documentation states that its a Java RMI service listening by default on tcp/20931 and references several command line utilities to communicate with the service:
- This is the RMI port used by command line utilities: proadsv, asbman, wtbman, nsman, restman, and pre OpenEdge 12.0 fathom cmd line tooling
- The default listening port for the AdminServer (-port) remains 20931 for all versions.
Inspecting our listening connections on the Windows server, we find that the AdminServer is indeed listening on tcp/20931.
Figure 3. AdminServer service listening
Inspecting the command use to kick off the Java process we find it’s loading several Progress JARs and calling com.progress.chimera.adminserver.AdminServerStarter
.
Figure 4. AdminServer command line
Reversing the AdminServer Service
We find that the com.progress.chimera.adminserver.AdminServerStarter
class is defined in C:\Progress\OpenEdge\java\progress.jar
. Inspecting the class, we find that when a remote connection is made the connect()
method is called and expects a user supplied username and password.
Figure 5. AdminServer connect()
The connect()
method interestingly loads a native system library, auth.dll
, and eventually calls the authorizeUser()
method defined in it. Replacing auth.dll
was mentioned in the temporary mitigations so we’re likely on the right track.
Opening up auth.dll
in Ghidra, we find that it exports several functions to be available as Java interfaces, one of which is our authorizeUser()
function.
Figure 7. Java Interfaces from auth.dll
The authorizeUser()
function performs some basic input validation, ensures the supplied credentials meet certain criteria, and passes control to a function we named vulnerable_checks()
(defined at 0x1800051a0). This function does further validation, but getting right into the meat of the vulnerability we see that on line 262 the user supplied AccountName (username) is compared the NT AUTHORITY/SYSTEM
. If it matches, you are authenticated.
Figure 8. If username == “NT AUTHORITY/SYSTEM”: you may pass
Thats the vulnerability.
Impact
While we’ve bypassed authentication, finding attack surface to abuse to drive some impact like remote code execution was the next goal.
Deserialization
Java Remote Method Invocation (RMI) interfaces typically suffer from deserialization vulnerabilities, but in this case there were no classic libraries in the class path of the service to easily abuse with a ysoserial gadget. We did confirm that deserialization is possible with a simple out-of-band DNS request payload, but did not spend the time to develop a custom gadget with the in scope libraries. Remote code execution is likely possible with this avenue.
Abuse of Built In Functionality
We spent the better part of a day looking for easily abusable functionality within the available RMI interfaces. Easily reachable functionality allows a user to start, stop, and list performance metrics of applications. Deeper attacker surface looks like it may allow a user to deploy new applications via remote WAR file references, but the complexity increased dramatically in order to reach this attack surface because of the use of internal service message brokers and custom messages. We believe there is again likely an avenue to remote code execution via built in functionality given enough research effort.
Creating a Proof of Concept
We continue our investigation by re-examining the AdminServer
class and dbman.bat
which makes use of the AdminServer
. We find that we can connect to the AdminServer
over RMI at rmi://<target_ip>:29031/Chimera
. We get back an IAdminServerConnection
which exposes two connect
methods that require a username and password. With our knowledge from dbman.bat
we know we need to encode the username and password using an Encoder
from oeauth-12.8.0.jar
.
Now that we are connected to the AdminServer
, what can we do? We can use the following code to display all interfaces available to us from the AdminServer
‘s getPlugins
method:
We get the following output:
com.progress.system.SystemPlugIn
com.progress.chimera.common.IChimeraRemoteObject
com.progress.system.ISystemPlugIncom.progress.agent.database.AgentPlugIn
com.progress.chimera.common.IChimeraRemoteObject
com.progress.agent.database.IAgentPlugIn
com.progress.ubroker.tools.NSRemoteObject
com.progress.chimera.common.IChimeraHierarchy
com.progress.ubroker.tools.IYodaRMI
com.progress.ubroker.tools.IYodaSharedResources
com.progress.ubroker.tools.UBRemoteCommand
com.progress.chimera.common.IChimeraRemoteCommand
com.progress.juniper.admin.JAPlugIn
com.progress.chimera.common.IChimeraRemoteObject
com.progress.juniper.admin.IJAPlugIn
com.progress.agent.smdatabase.SMPlugIn
com.progress.chimera.common.IChimeraRemoteObject
From here, we leave it as an exercise to the reader to figure out what you can do with the above interfaces. Our proof of concept can be found here.
NOTE: We will not be distributing the Progress JARs given we do not own that code. These JARs can be obtained from an OpenEdge installation and are required to run the proof of concept.
Indicators of Compromise
When a connection is made to the AdminServer service, logs are generated at C:\OpenEdge\WRK\admserv
. An example log entry can be seen below where it records the user authenticating as well as the Java interfaces that user is accessing, the UBRemoteCommand class in our case. While it seems that accessing this service via the NT AUTHORITY/SERVICE account was intended, we did not observe log entries associated to this account outside of service startup. We also were not running a production server where more service traffic may be generated and observed.