Headline
CVE-2021-44427: Improper Access Control leading to Unauthenticated SQL Injection (#328) · Issues · François Jacquet / rosariosis
An unauthenticated SQL Injection vulnerability in Rosario Student Information System (aka rosariosis) before 8.1.1 allows remote attackers to execute PostgreSQL statements (e.g., SELECT, INSERT, UPDATE, and DELETE) through /Side.php via the syear parameter.
Description
Unauthenticated SQL Injection vulnerability in Rosario Student Information System version 8.1 allows remote attackers to execute PostgreSQL statements (e.g.: SELECT, INSERT, UPDATE, DELETE) through /Side.php via syear parameter.
This vulnerability is a result of two issues:
- OWASP Top 10:2021 - A01:2021 – Broken Access Control
- OWASP Top 10:2021 - A03:2021 – Injection
Plugin
README
Vulnerability Type:
SQL Injection (SQLi)
Vendor of the product(s):
RosarioSIS
Affected product(s)/code base:
8.1
Has vendor confirmed or acknowledged the vulnerability:
Yes
Attack type:
Unauthenticated
Impact & Description:
C:H/I:H/A:H
Affected Component:
/Side.php, /Warehouse.php, /functions/Current.php, /functions/GetMP.php
Attack vector(s):
Remote
Proof of concept (PoC):
Details below
References:
Issue #328 (closed)
Severity:
Critical (9.8)
CVSS v3.1 Vector String:
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Initial Communication
Hi @francoisjacquet,
Found two issues in the latest version of RosarioSIS (version 8.1).
Many thanks & hope this helps!
– IJ
Environment Details
- RosarioSIS 8.1
- PHP 7.4.21
- PostgreSQL 13.3
Proof-of-Concept
The following command can be used to replicate this finding
sqlmap -u "http://hostname/path/Side.php" --data="sidefunc=update&syear=111" --user-agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36" --delay=0 --timeout=30 --retries=0 -p "syear" --dbms="PostgreSQL" --technique BET --level=5 --risk=3 --threads=1 --time-sec=5 -b --current-user --current-db --dbs --answers="crack=N,dict=N,continue=Y,quit=N" -v 0 --drop-set-cookie --flush-session
**Recommendations******OWASP Top 10:2021 - A01:2021 – Broken Access Control****
- Create a session handler which will be imported on all PHP files of RosarioSIS
- Always check if the current session is authenticated or not & handle execution flow accordingly
- Avoid exposing conditional statements which directly accept user-supplied input
- Wrap code with intended functionality within declared functions
- Prevent unauthenticated users from directly supplying input & controlling execution flow
****OWASP Top 10:2021 - A03:2021 – Injection****
- Do not trust user-supplied input, always perform validation at the back-end / server-side
- Create a whitelist at the back-end to filter out malicious characters from user-supplied input
- Example: When expecting string input (e.g.: First Name, Last Name) from the user
- Filter against [a-zA-Z0-9]* (and some symbols if necessary - @, ., etc)
- Drop or ignore all else - dangerous characters from user-supplied input (e.g.: <, >, , ', ", `, $, %, !, ?, -, +, _, #, [, ], (, ), etc)
- Example: When expecting string input (e.g.: First Name, Last Name) from the user
- Ensure proper data type handling is observed
- Example: When expecting an integer (e.g.: sYear) from the user
- Accept string input -> Sanitize input -> Check if input is valid integer -> SQL query
- Example: When expecting an integer (e.g.: sYear) from the user
- For SQL, make use of parameterized queries, prepared statements & stored procedures after sanitizing user-supplied input
References
- https://owasp.org/Top10/A01_2021-Broken_Access_Control/
- https://owasp.org/Top10/A07_2021-Identification_and_Authentication_Failures/
- https://owasp.org/Top10/A03_2021-Injection/
- https://stackoverflow.com/questions/60174/how-can-i-prevent-sql-injection-in-php
- https://stackoverflow.com/questions/4712037/what-is-parameterized-query
- https://www.php.net/manual/en/pdo.prepared-statements.php
- https://security.stackexchange.com/questions/32334/what-dangerous-characters-need-to-be-filtered-from-user-input-prior-to-use-in-a
Analysis
During the analysis, breakpoints were set in Visual Studio Code for the following files
- At /Side.php on the following lines:
- 10, 25, 56, 59, 452
- At functions/GetMP.php on the following line:
- 406
A01:2021 – Broken Access Control
The file /Side.php (and other php files in RosarioSIS) does not implement access controls or session management checks to prevent any unauthenticated user from executing its code by directly accessing it through a browser.
Since no session management checks are implemented, visiting /Side.php through a browser causes lines 85 - 90 of /Warehouse.php to be executed from line 10 of /Side.php - which is a code snippet that loads php files within the functions/ directory of the web server:
Lines 1 - 19 of /Side.php (emphasis on line 10)
1 <?php
2 /**
3 * Side
4 *
5 * Side menu
6 *
7 * @package RosarioSIS
8 */
9
10 require_once 'Warehouse.php';
11
12 $old_school = UserSchool();
13 $old_syear = UserSyear();
14 $old_period = UserCoursePeriod();
15
16 $unset_student = $unset_staff = $update_body = false;
17
18 $addJavascripts = '';
19
Lines 82 - 90 of /Warehouse.php
82 /**
83 * Load functions
84 */
85 $functions = glob( 'functions/*.php' );
86
87 foreach ( $functions as $function )
88 {
89 require_once $function;
90 }
Import of functions/GetMP.php from /Side.php
Since the conditional if statement on lines 25 - 30 (boxed in red below) checks for user-controlled parameters ($_REQUEST, $_POST), an unauthenticated user could craft & supply a request which will pass the conditional check and ultimately control the application’s execution flow.
This can be done by sending a request that:
Contains a parameter “sidefunc=update”
Is an HTTP POST request
POST /rosariosis-v8.1/Side.php HTTP/1.1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36 Host: 192.168.254.140 Connection: close Content-Type: application/x-www-form-urlencoded Content-Length: 117
sidefunc=update&syear=111'+OR+(SELECT(SELECT+CASE+WHEN+(SUBSTRING('poc',2,1)=CHR(ASCII('o')))+THEN+1+ELSE+0+END)=1)--
After passing the first conditional check, another series of conditional if statements beginning at line 35 (pointed with a red arrow in the image above) will be executed.
However, since the condition in line 35 is not met, execution will continue to the following else if statement at line 53 which leads to the SQL Injection vulnerability.
A03:2021 – Injection
The value of HTTP POST parameter syear is assigned to the HTTP SESSION parameter UserSyear at line 56 which is then passed on to line 59 where the GetCurrentMP() function is called with the DBDate() argument.
Stepping into from line 59 leads to the function definition of DBDate() at line 32 of functions/Date.php which returns the current date in ISO format (e.g. 2021-09-25). This value is passed on to GetCurrentMP() as its 2nd argument.
Stepping into from line 32 of functions/Date.php leads to the function definition of GetCurrentMP() which starts at line 404 of functions/GetMP.php
A SQL query is defined in lines 411 - 416 where it is assigning to the SQL query parameter SYEAR the returned value of the function call to UserSyear()
Stepping into from line 411 of functions/GetMP.php leads to the function definition of UserSyear() located in line 27 of functions/Current.php which shows that its returning the value initially assigned to the HTTP POST request parameter syear which was assigned earlier.
From this point, the SQL query is executed through the DBQuery() function which is defined in database.inc.php. The output can be observed at the postgresql log file (If logging is enabled). This can lead to a Time-based blind SQL Injection attack.
It was also observed that the injected SQL query is being executed again on line 452 of Side.php - The error message & injected SQL query is actually displayed from Side.php which can also lead to Error-based & Boolean-based blind SQL Injection attacks.