An authenticated SQL injection in a free PHP HRMS sample app that nobody runs in production
CVE-2026-13525 is a classic SQL injection in CodeAstro Human Resource Management System 1.0, specifically in the emselectByCode function inside application/models/Employee_model.php. The emid parameter on the *Update_Earn_Leave* endpoint is concatenated unsanitized into the underlying query, allowing time-based blind SQLi by any user who can authenticate to the application. Only version 1.0 is named affected, and CodeAstro has not published a patched build — this is a shipped-as-source CodeIgniter project sold as a learning template on codeastro.com.
Vendor severity is MEDIUM (CVSSv3.1 6.3), and that overstates reality. CodeAstro's HRMS is a tutorial-grade application — it shows up in student portfolios and on freelancer demo sites, not behind enterprise load balancers. The bug is real and the proof-of-concept by *ashikmd0507* is public, but the installed base that an enterprise defender would actually be responsible for is effectively zero. We grade this LOW — patch-management noise, not a fire.
4 steps from start to impact.
Identify a CodeAstro HRMS 1.0 instance
/assets/dist/img/codeastro.png), or login page branding. The footprint is tiny: this is not a product an enterprise procures, it is source-code dropped onto cheap shared hosting. Mass-scanning tools like httpx plus a CodeAstro-favicon hash will surface what little exists.- Internet-reachable instance of CodeAstro HRMS 1.0
- Default templates left in place
- Negligible installed base — fewer than a few hundred instances visible in
Shodan/FOFAcombined - Most copies run on dev laptops or paused student projects, not production hosts
Obtain valid credentials (PR:L required)
PR:L — the attacker must be authenticated. That means either a registered HR-role user, default admin credentials (admin/admin is documented in the CodeAstro tutorial), or a prior credential-stuffing win. Without an account, the *Update_Earn_Leave* endpoint is gated by the CodeIgniter session middleware.- Working session cookie
- Account with access to leave-management module
- Authentication wall stops opportunistic drive-by scanners
- Account creation is not self-service in stock builds
user_id of the requester — post-incident attribution is trivial if logs surviveInject through the emid parameter on Update_Earn_Leave
sqlmap-style time-based blind payload in emid (e.g. 1 AND SLEEP(5)-- -). Because emselectByCode() concatenates the value into a SELECT ... WHERE emid = $emid query with no parameterization, MySQL evaluates the boolean and sleeps. sqlmap -u ... -p emid --technique=BT --dbms=mysql automates the full extraction.- MySQL backend allows
SLEEP()(default) - Request bypasses any front-end WAF
- No WAF tuning is standard for this product — shared hosting providers like cPanel-based hosts may have generic ModSecurity rules that catch obvious unions
sqlmap user-agent and time-based timing patterns are caught by mod_security CRS and any commercial WAF in default configExfiltrate employee PII / pivot to credential reuse
users table (bcrypt or, in many CodeAstro tutorials, plaintext / MD5 passwords), employee SSNs, salary records, and session tokens. Blast radius is the single tenant database. There is no documented chain from SQLi here to OS RCE — no INTO OUTFILE, no stacked queries via PDO emulation by default.- DB user has SELECT on sensitive tables (typical — app runs as a single MySQL user)
- No path to host compromise without a separate file-write primitive
- Cannot pivot to other applications on the same host without further bugs
SLEEP() patternsThe supporting signals.
| In-the-wild exploitation | None observed. No GreyNoise tag, no campaign telemetry, no incident reports. |
|---|---|
| Proof of concept | Public PoC by researcher ashikmd0507 on GitHub demonstrating time-based blind SQLi via emid. |
| EPSS | Not yet scored by FIRST as of disclosure date (2026-06-29). Expect <1st percentile based on product class. |
| KEV status | Not listed. CISA KEV does not track tutorial-grade PHP projects. |
| CVSS vector | CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:L — network-reachable, low complexity, but requires authentication, scope unchanged, partial CIA impact only. |
| Affected versions | CodeAstro Human Resource Management System 1.0 — the only released version. |
| Fixed version | None published. CodeAstro has not issued a patched build; patch status unconfirmed. |
| Exposure data | Fewer than ~200 instances visible across Shodan+FOFA favicon hashes for the CodeAstro template. Effectively no enterprise footprint. |
| Disclosure date | 2026-06-29 (VulDB / CVE assignment, public PoC same day). |
| Vulnerable component | application/models/Employee_model.php → emselectByCode() → emid parameter on the *Update_Earn_Leave* endpoint. |
noisgate verdict.
Downgraded from MEDIUM to LOW because the single most decisive factor is installed-base share — CodeAstro HRMS is a tutorial template, not enterprise software, and exposure across Shodan/FOFA is in the low hundreds globally. Authentication is also required (PR:L), which compounds the narrowing.
Why this verdict
- Authentication gate (
PR:L): the *Update_Earn_Leave* endpoint sits behind CodeIgniter session auth; opportunistic scanners cannot reach it without valid creds or a default-admin holdover. - Installed-base share: CodeAstro HRMS is sold as source-code learning material on codeastro.com — Shodan/FOFA show fewer than ~200 live instances combined, none in Fortune-2000 estate scans we've seen.
- Role multiplier — low-value role only: This component is never deployed as a domain controller, IdP, hypervisor, CA, backup server, or any other high-value role in the noisgate role catalog. The worst plausible outcome is single-tenant HR-PII disclosure on a hobby/freelance install. No floor uplift applies.
- Blast radius capped at one DB: SQLi here does not chain to OS RCE without a separate file-write primitive;
S:Uin the vector is accurate. - No KEV, no campaigns, no EPSS uplift: zero evidence of in-the-wild interest in this product family.
Why not higher?
MEDIUM would imply enough real-world deployments to make this a measurable patching priority. The installed base does not support that. There is also no path to RCE, no privilege escalation, and the PR:L requirement filters out the bulk of internet-borne SQLi traffic.
Why not lower?
IGNORE would be wrong because the bug is real, the PoC is public, and an enterprise that *does* happen to run CodeAstro HRMS (some SMBs, dev environments, training labs) will leak HR PII if compromised. LOW correctly captures 'fix it on the backlog, don't lose sleep over it'.
What to do — in priority order.
- Inventory whether CodeAstro HRMS exists anywhere in your estate — Run a directory-scan signature for
/application/models/Employee_model.phpand the CodeAstro favicon hash against your asset DB. Most enterprises will get zero hits and can close the ticket immediately. No mitigation SLA applies at LOW — finish inventory within the 365-day remediation window. - If found, place behind a WAF with SQLi signatures enabled — Generic ModSecurity OWASP CRS rules
942100-942510catch the time-based payloads (SLEEP,BENCHMARK, boolean tautologies). Tune for the application's expectedemidinteger pattern — reject anything non-numeric. - Force a credential reset and disable default admin accounts — CodeAstro tutorial walkthroughs leave
admin/adminconfigured. Rotate, enforce length ≥12, and ideally put the whole app behind SSO or a VPN — thePR:Lrequirement becomes a real wall only if creds are not guessable. - Apply a manual code fix using prepared statements — Edit
Employee_model.php::emselectByCode()to use CodeIgniter's query bindings:$this->db->where('emid', $emid); $this->db->get('table')instead of string concatenation. Since the vendor has not shipped a patch, you own the fork. - Restrict the DB user privileges — The MySQL user the app connects with should have only
SELECT, INSERT, UPDATE, DELETEon the HRMS schema — noFILE, no cross-schema access. Limits exfiltration scope if the SQLi is still triggered.
- Input length limits alone — time-based blind payloads fit in <40 characters
- Network IDS signatures looking for
UNION SELECT— the public PoC uses boolean/time-based techniques that never include UNION - Patching the underlying CodeIgniter framework — the bug is in CodeAstro's application code, not the framework
- Disabling MySQL
SLEEP()— attacker will fall back to boolean-based blind with the same outcome, just slower
Crowdsourced verification payload.
Run this on any Linux/macOS host where the CodeAstro HRMS source tree might be deployed. Pass the webroot as $1 (example: ./check.sh /var/www/html/hrms). No special privileges required beyond read access to the source directory.
#!/usr/bin/env bash
# noisgate verifier for CVE-2026-13525 (CodeAstro HRMS 1.0 SQLi in emselectByCode)
# Usage: ./check.sh /path/to/hrms/webroot
# Exit codes: 0 PATCHED, 1 VULNERABLE, 2 UNKNOWN
set -u
ROOT="${1:-}"
if [[ -z "$ROOT" || ! -d "$ROOT" ]]; then
echo "UNKNOWN: provide webroot path as argument"
exit 2
fi
TARGET="$ROOT/application/models/Employee_model.php"
if [[ ! -f "$TARGET" ]]; then
echo "UNKNOWN: Employee_model.php not found under $ROOT — CodeAstro HRMS likely not installed here"
exit 2
fi
# Find the emselectByCode function body and look for unsafe concatenation of $emid
BODY=$(awk '/function[[:space:]]+emselectByCode/,/^[[:space:]]*}[[:space:]]*$/' "$TARGET")
if [[ -z "$BODY" ]]; then
echo "UNKNOWN: emselectByCode() not present — possibly a forked or refactored build"
exit 2
fi
# Unsafe pattern: any direct concatenation of $emid into a query string
if echo "$BODY" | grep -Eq "(query|where)\s*\(.*\".*\\\$emid.*\"" ; then
echo "VULNERABLE: $TARGET concatenates \$emid directly into the SQL query"
exit 1
fi
# Safe pattern: parameter bindings or where() with array
if echo "$BODY" | grep -Eq "where\s*\(\s*['\"]emid['\"]\s*,\s*\\\$emid\s*\)|query\s*\(.*,\s*array\s*\(" ; then
echo "PATCHED: emselectByCode() uses parameterized queries"
exit 0
fi
echo "UNKNOWN: emselectByCode() found but pattern is ambiguous — manual review required at $TARGET"
exit 2
If you remember one thing.
emselectByCode() with CodeIgniter prepared statements and stand up basic WAF coverage. Do not let this displace work on KEV-listed or HIGH-severity issues — it is backlog hygiene, not an incident.Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.