Summary: This content discusses the features and deployment options of FortiSIEM, a SIEM solution that allows for log collection, correlation, automated response, and remediation.
Threat Actor: N/A
Victim: N/A
Key Point :
- The FortiSIEM solution offers various deployment options, ranging from standalone appliances to scaled-out solutions for enterprises and managed service providers.
- There are four types of roles in a FortiSIEM deployment: Supervisor, Worker, Collector, and Manager, each serving different functions in handling data collection and management.
- This research focuses on an all-in-one architecture where the appliance contains all the functionality within the Supervisor role.
The FortiSIEM allows customers to do many of the expected functions of a typical SIEM solution such as log collection, correlation, automated response, and remediation. It also allows for simple and complex deployments ranging from a standalone appliance to scaled out solutions for enterprises and MSPs.
Figure 1. Example Deployment
In a FortiSIEM deployment, there are four types of roles that a system can have:
● Supervisor – for smaller deployments this is all that’s needed, and supervises other roles
● Worker – handles all the data coming from Collectors in larger environments
● Collector – used to scale data collection from various geographically separated network
environments, potentially behind firewalls
● Manager – can be used to monitor and manage multiple FortiSIEM instances
For the purposes of this research, I deployed an all-in-one architecture where the appliance contains all of the functionality within the Supervisor role. For more information about FortiSIEM key concepts refer to the documentation.
Exploring the System
One of the first things we do when auditing an appliance is to inspect the listening services given you have some time of shell access. Starting with the most obvious service, the web service, we see that it listens of tcp/443 and the proxy configuration routes traffic to an internal service listening on tcp/8080.
Figure 2. httpd.conf proxying traffic
Figure 3. Backend webserver
We find that the backend web service is deployed via Glassfish, a Java framework similar to Tomcat in that it provides a simple way to deploy Java applications as WAR files. We find the WAR file that backs the service, unpack it, and decompile it. Inspecting some of the unauthenticated attack surface, we happen upon the LicenseUploadServlet.class
.
Figure 4. LicenseUploadServlet doPost method
We follow the code into this.notify()
, where we eventually observe it calling sendCommand()
, which interestingly sends a custom binary message with our input to the port tcp/7900.
Figure 5. sendCommand()
We find that tcp/7900 hosts the phMonitor service, which listens on all interfaces, not just localhost.
Figure 6. phMonitor on tcp/7900
And it is also a compiled C++ binary.
Building a Client
Now that we’ve identified a pretty interesting attack surface, let’s build a client to interact with it in the same way the web service does. The message format is a pretty simple combination of:
- Command Type – The integer enum mapped to specific function handlers inside the phMonitor service
- Payload Length – The length of the payload in the message
- Send ID – An arbitrary integer value passed in the message
- Sequence ID – The sequence number of this message
- Payload – The specific data the function handler within phMonitor will operate on
Constructing the LicenseUpload message in little-endian format and sending it over an SSL wrapped socket will succeed in communicating with the service. Re-implementing the client messaging protocol in Python looks like the following:
Figure 7. phMonitor Python client
As a test that the client works, we send a command type of 29, mapped to handleProvisionServer
, and can observe in the logs located at /opt/phoenix/log/phoenix.log
that the message was delivered.
Figure 8. phMonitor client successful message sent
phMonitor Internals
The phMonitor service marshals incoming requests to their appropriate function handlers based on the type of command sent in the API request. Each handler processes the sent payload data in their own ways, some expecting formatted strings, some expecting XML.
Inside phMonitor, at the function phMonitorProcess::initEventHandler()
, every command handler is mapped to an integer, which is passed in the command message. Security Issue #1 is that all of these handlers are exposed and available for any remote client to invoke without any authentication. There are several dozen handlers exposed in initEventHandler()
, exposing much of the administrative functionality of the appliance ranging from getting and setting Collector passwords, getting and setting service passwords, initiating reverse SSH tunnels with remote collectors, and much more.
Figure 9. Sampling of handlers exposed
Finding a Bug
Given the vast amount of attack surface available unauthenticated within the phMonitor service, we begin with the easiest vulnerability classes. Tracing the calls between these handlers and calls to system()
we land of the handler handleStorageRequest()
, mapped to command type 81. On line 201, the handler expects the payload to be XML data and parses it.
Figure 10. handleStorageRequest() expecting XML payload
Later, we see that it attempts to extract the server_ip
and mount_point
values from the XML payload.
Figure 11. XML payload format
“An interesting youtube video that may be related to the article above”