Blog post

Backend SQL Injection in BigTree CMS 4.4.6

Robin Peraglie photo

Robin Peraglie

Vulnerability Researcher

Date

  • Security
BigTree is a small content management system which does not depend on many frameworks and advertises itself as user friendly and developer ready. In this blog post, we will take a look at...

We have analyzed one of the latest versions of BigTree CMS 4.4.6 and detected multiple vulnerabilities. Among them is a SQL Injection vulnerability and a Phar Deserialization vulnerability leading to a Remote Code Execution in the small web application.

Chaining SQL Injection and XSS

BigTree CMS suffers from a plain SQL Injection which can be exploited in the dashboard. An unsanitized parameter allows overriding the Table property, enabling the manipulation of the underlying SQL syntax to extract arbitrary sensitive information from the database. The web application then continues to print all the data retrieved through the SQL query and returns it to the authenticated administrator.


Since BigTree does not make use of any CSRF tokens here, the vulnerability can be exploited through CSRF. A Second Order Cross-Site Scripting vulnerability can then be used to smuggle the data out to an external Server. In the following we will see the entry point to the vulnerability:


core/admin/ajax/dashboard/check-module-integrity.php

 6    $form = BigTreeAutoModule::getForm($_GET["form"]);	
 7    // Create a generic module class to get the decoded item data
 8    $m = new BigTreeModule;
 9    $m->Table = $form["table"];
10    $item = $m->get($_GET["id"]);

On line 6 the user input is received through the form parameter and stored in the $formvariable. Its value is then assigned to the $m->Table property of the BigTreeModule instance. Finally, the get() method is invoked on the object which launches a SQL query on line 10. This method is embedding the user input of the $item variable safely and embedding the tainted Table property unsafely.


core/inc/bigtree/modules.php

 7    class BigTreeModule {
 8    ⋮
 9        public function get($item) {
10        ⋮
11            $item = sqlfetch(sqlquery("SELECT * FROM `".$this->Table."` WHERE id = '".
12                                        sqlescape($item)."'"));

The data can then be smuggled out by exploiting a Cross-Site Scripting vulnerability. To achieve this, the attacker must control the values of the $item array returned by the SQL query to start with the string “http”. This causes the control flow to branch into the program block which processes links automatically, starting on line 19 of the following source code.


core/admin/ajax/dashboard/check-module-integrity.php

13    <?php
14    foreach ($form["fields"] as $field => $resource) {
15        ⋮
16        if ($resource["type"] == "text" && is_string($item[$field])) {
17            $href = $item[$field];
18            ⋮
19            if (substr($href,0,4) == "http" && strpos($href,WWW_ROOT) === false) {
20                ⋮
21                if (!$admin->urlExists($href))
22                    $integrity_errors[$field] = array("a" => array($href));
23            }
24        }
25    }
26    ⋮
27    foreach ($integrity_errors as $field => $error_types) {
28        foreach ($error_types as $type => $errors) {
29            foreach ($errors as $error) { ?>
30                <p>Broken <?=(($type == "img") ? "Image" : "Link")?>: <?=$error?>
31                in field &ldquo;<?=$form["fields"][$field]["title"]?>&rdquo;</p>

For each potential link, the program will send a web request via the urlExists() method. If the request fails, the data is added to the error array $integrity_errors on line 22. The values of this array are printed unsanitized in a foreach loop on line 30 leading to the output of our SQL Injection directly next to our Cross-Site Scripting payload extracting the data to an external server. Although it's usually tricky to exploit a SQL Injection via CSRF, we can in this case make use of the Cross-Site Scripting vulnerability to smuggle out the results easily with an AJAX request.

Phar Deserialization via CURL wrapper

Curls CLI file feature allows uploading files from the file system by prepending the filename with an @ symbol. Adding the curl option -d param=@/path/to/filename in the curl CLI would comfortably upload the contents of the specified filename to the target server. BigTree developed its own curl wrapper function BigTree::cURL() to implement this feature.


core/inc/bigtree/utils.php

263    public static function cURL($url, $post = false, $options = [], 
264        $strict_security = true, $output_file = false, $updating_bundle = false) {
265        ⋮
266        if ($post !== false) {
267            if (function_exists("curl_file_create") && is_array($post)) {
268                foreach ($post as &$post_field) {
269                    if (substr($post_field, 0, 1) == "@" 
270                    && file_exists(substr($post_field, 1))) {
271                        $post_field = curl_file_create(substr($post_field, 1));

The method receives data to be send in the HTTP body as the second argument $post of the static method. On line 268 it iterates over the array and checks the values for an @ character which is potentially suffixed with a filename. This filename is used as an argument to file_exists() on line 270 before adding the contents of the file to the curl request, leading to a Phar Deserialization vulnerability if we have control over a value of the $post array. This assumption is true for the URL http://<host>/bigtree446/site/index.php/admin/developer/services/instagram/return/?code=@phar://myphar.phar which routes an authenticated backend user directly to the following entry point:


core/admin/modules/developer/services/common/return.php

$token = $api->oAuthSetToken($_GET["code"]);

The user input stored in the code parameter is passed as the $code argument to the oAuthSetToken() method. This forwards the value directly into the BigTree::cURL() wrapper from above, leading to the Phar Deserialization vulnerability.


core/inc/bigtree/apis/_oauth.base.php

337    public function oAuthSetToken($code) { 
338        $response = json_decode(BigTree::cURL($this->TokenURL,array( 
339            "code" => $code, 
340            ⋮ 
341        ))); 

To exploit this vulnerability, a file must be uploaded. This can only be achieved by correctly posting a CSRF token. However, this CSRF token can be stolen by exploiting the Cross-Site Scripting vulnerability from above and stealing the token. This will enable a file upload that can be used in the Phar Deserialization process.

Timeline

DateEvent
10/15/19Vulnerability details send for BigTree 4.4.6
10/15/19Vendor acknowledged and confirmed issue
10/15/19Vendor released BigTree 4.4.7 patching the issue

Summary

In this blog post, we have seen that sanitizing every parameter by hand is a tedious and dangerous method to secure your application. Sophisticated attackers can chain multiple exploits to launch a successful targeted attack with high impact. It is recommended to invest time to develop and make use of centralized security module which will bundle sanitization and database preparation. Automated security testing can assist in the process of detecting vulnerable leftovers of legacy code when retroactively implementing security features. The RIPS scanner detected the issues within 7 minutes of scan time. Especially the beginning of a file path should not be in control of an attacker to deny exploitation via arbitrary PHP filters like the phar:// wrapper.

Related Posts