IIS FTP Server Anonymous Writeable Reinforcement, WEBDAV Anonymous Writeable Reinforcement(undone)

目录

0. 引言
1. IIS 6.0 FTP匿名登录、匿名可写加固
2. IIS 7.0 FTP匿名登录、匿名可写加固
3. IIS >= 7.5 FTP匿名登录、匿名可写加固
4. IIS 6.0 Anonymous PUT(WEBDAV匿名可写)加固
5. IIS 7.0 Anonymous PUT(WEBDAV匿名可写)加固
6. IIS >= 7.5 Anonymous PUT(WEBDAV匿名可写)加固
7. IIS ISAPI Filter(isapiFilters) 
8. IIS Extension
9. IIS FTP匿名登录的自动化修复
10. IIS WEBDAV匿名访问的自动化修复
11. IIS 恶意Filter/Extension的自动化修复

 

0. 引言

0x0: 本文主要研究的技术点

因为本文篇幅较长,这里对全文的调研技术点做一个梳理总结

1. 基于IIS FTP、IIS WEBDAV的黑客入侵、GETSHELL
2. IIS FTP配置加固的原理
3. IIS的增强扩展(Filter、Extension)的原理及利用方式
4. 如何通过API方式对IIS 6、IIS 7.5进行配置修复,从而达到批量集群自动漏洞修复的目的

0x1: 要通过FTP进行GETSHELL需要满足的条件

基于FTP对目标主机进行GETSHELL本质上是一个写磁盘的动作,要达到这个目的,需要满足几个先决条件

1. 目标服务器开启了FTP匿名访问
2. 目标服务器开启了FTP写权限(IIS FTP软件层的逻辑控制)
3. 目标服务器的登录帐号(以及所在组)对指定的目录有写的权限(ACL)
4. 目标服务器的"FTP根目录"同时也是"WEB服务器的WEB目录"

对于第3点,需要特别注意的是,在默认情况下,磁盘文件的ACL设置中,是没有IUSR_computername的,而在windows中,如果没有明确禁止,则隐含的条件是默认允许,从这点也可以看出,如果我们想要明确的禁止这个FTP目录遭到黑客的写入,应该明确地添加IUSR_computername,并禁止写权限

当这4点同时满足时,黑客就可以通过FTP的漏洞达到直接GETSHELL的目的

Relevant Link:

http://blog.csdn.net/luckyp/article/details/3929895

0x2: 如何进行FTP匿名登录

当目标主机开启了FTP匿名访问之后,在客户端使用

1. 帐号: anonymous、密码: 为空
2. 帐号: ftp、密码: 为空

这两个账户都可以登录

0x3: 匿名FTP的来由

大量的匿名FTP站点对所有用户公开文件访问权,目的是发布它们的软件和信息(就是我们常说的下载文件)。鉴于FTP在Internet中仍占据重要的地位,故IIS集成了FTP服务器。在IIS上架构的FTP站点,用户可以用IUSRR_SERVERNAME账户试图连接该服务器,在命令行下用户名是anonymous,密码可以使用任何内容,在Internet Explorer中不用输入任何信息就能匿名登录FTP站点。

Relevant Link:

http://soft.zdnet.com.cn/software_zone/2007/0807/446657.shtml

 

1. IIS 6.0 FTP匿名登录、匿名可写加固

0x1: IIS 6.0 FTP匿名匿名登录原理

匿名身份验证使用户无需输入用户名或密码便可以访问 Web 或 FTP 站点的公共区域。默认情况下,IUSR_computername 帐户用于允许匿名访问。
在 IIS 6.0 中,匿名身份验证不再需要"允许本地登录"用户权限,因为 NETWORK_CLEARTEXT 目前是匿名和基本身份验证的默认登录类型。
在安装过程中,将 IUSR_computername 帐户添加到运行 IIS 的计算机上的 Guests 组中。默认情况下,Guest 与 User 组的成员具有相同的访问权限,但是 Guest 帐户将受到更多限制。

0x2: IIS 6.0 FTP启用匿名身份验证

在开始进行高权限操作的时候,出于最佳安全实践(Best Security Practice),我么应该遵循以下原则

只有本地计算机上 Administrators 组的成员才能执行以下过程。作为安全性最佳操作,请使用不属于 Administrators 组的帐户登录计算机,然后使用 runas 命令以管理员身份运行 IIS 管理器。在命令提示符下,
键入 runas /user:Administrative_AccountName "mmc %systemroot%\system32\inetsrv\iis.msc"

启用匿名身份验证

1. 在 IIS 管理器中,双击本地计算机,右键单击"网站"文件夹、单个网站文件夹、虚拟目录或文件,然后单击"属性"1) 服务器上的所有网站均将继承在网站级别设定的配置设置。可以通过配置单个站点或站点元素来覆盖继承。
2. 单击"目录安全性""文件安全性"选项卡,然后在"身份验证和访问控制"部分中单击"编辑"3. 选中"启用匿名访问"复选框。
4. 单击"确定"两次。

Relevant Link:

http://msdn.microsoft.com/zh-cn/library/cc780334(v=ws.10).aspx
http://msdn.microsoft.com/zh-cn/library/cc737887(v=ws.10).aspx
http://msdn.microsoft.com/zh-cn/library/cc740131(v=ws.10).aspx
http://msdn.microsoft.com/zh-cn/library/cc784103(v=ws.10).aspx

 

2. IIS 7.0 FTP匿名登录、匿名可写加固

0x1: IIS 7 FTP安装

1. windows 7 安装IIS 7 FTP

1. 打开"控制面板",运行"程序和功能"
2. 点击窗户左侧的"打开或关闭Windows功能"
3. 在打开的"Windows功能对于话框"中,展开"Intemet信息服务",勾选"FIP服务器"和其它我们所需功能后"确定"即可
//等待片刻后完成IIS 7的安装
4. 在打开的"Internet信息服务(IIS)管理器"窗户中,依次展开到"网站",右键点击"网站"选择"添加加FTP站点"
5. 输入FTP站点的名称
6. 指定FTP站点的工作物理路径(站点的根目录)
7. 指定绑定的IP地址与服务端口,通常将"SSL"设置为""。如果有域名的话,还可勾选"启用虚拟主机名"并输入域名 
8. 按照现真实情况况勾选"身份验证(可设置允许匿名访问)"和在"授权"用户,设置用户的操作权限,此处至少应选择一项 
9. 完成设置 

2. windows server 2008 R2 安装IIS 7 FTP

1. 打开"控制面板",运行"程序和功能"
2. 点击窗户左侧的"打开或关闭Windows功能",此时将打开"服务器管理器"窗户
3. 添加服务器角色,点击"角色",再点击右侧的"添加角色",之后在打开的"添加角色向导"对于话框中按照向导勾选"Web服务器(IIS)"角色下的"FTP服务器""IIS 6管理节制台"以及其它所需角色
4. 点击左侧"FTP站点"节点,添加FTP站点

Relevant Link:

http://iis.juj6.com/html/viewnews-2053.html
http://technet.microsoft.com/zh-cn/library/cc770966(v=ws.10).aspx
http://www.iis.net/learn/publish/using-the-ftp-service/configuring-ftp-user-isolation-in-iis-7

 

3. IIS >= 7.5 FTP匿名登录、匿名可写加固

待研究

 

4. IIS 6.0 Anonymous PUT(WEBDAV匿名可写)加固

0x1: WebDAV简介

基于万维网的分布式创作和版本控制(WebDAV)是一组基于超文本传输协议的技术集合,有利于用户间协同编辑和管理存储在万维网服务器文档。WebDAV最重要的特性包括:

1. 锁: 防止覆盖
2. 特性: 
    1) 创建
    2) 移除
    3) 查询
3. 命名空间管理
4. 集合
    1) 创建
    2) 移除
    3) 列举资源

WebDAV是一种基于 HTTP 1.1协议的通信协议.它扩展了HTTP 1.1,在HTTP标准方法以外添加了一些新的方法

1. Options
2. Head
3. Trace
主要由应用程序用来发现和跟踪服务器支持和网络行为。

4. Get
检索文档

5. Put
6. Post
将文档提交到服务器

7. Delete
销毁资源或集合

8. Mkcol
创建集合

9. PropFind
10. PropPatch
针对资源和集合检索和设置属性

11. Copy
12. Move
管理命名空间上下文中的集合和资源

13. Lock
14. Unlock
改写保护

WebDAV 请求的一般结构遵循 HTTP 的格式,并且由以下三个组件构成:

1. 方法
声明由客户端执行的方法(Options/Head..)

2. 标头
描述有关如何完成此任务的指令

4. 主体(可选)
定义用在该指令或其他指令中的数据,用以描述如何完成此方法 
在主体组件中,XML 成为整个 WebDAV 结构中的关键元素

0x2: IIS实现WebDAV原理

IIS实现Webdav是采用的其两种接口

1. CGI(Common Gateway Interface)
CGI是外部应用程序(CGI程序)与Web服务器之间的接口标准,是在CGI程序和Web服务器之间传递信息的规程。CGI规范允许Web服务器执行外部程序,并将它们的输出发送给Web浏览器,CGI将Web的一组简单的静态超媒体文档变成一个完
整的新的交互式媒体
2. ISAPI(ISA)接口(ISAPI Extension) ISAPI 服务器扩展是可以被 HTTP 服务器加载和调用的 DLL。Internet 服务器扩展也称为 Internet 服务器应用程序 (ISA),用于增强符合 Internet 服务器 API (ISAPI) 的服务器的功能。ISA 通过浏览器应用程序调用,并
且将相似的功能提供给通用网关接口 (CGI) 应用程序
注意和"ISAPI Filter"区分

WebDAV的解析没有采用影射的方式,所以IIS的主程序w3svc.dll本身包含了Webdav的信息,也就是说,webdav的流程和普通的http流量是混合在一起的 ,iis识别出是Webdav的请求后就调用Webdav的处理模块httpext.dll(这是一个ISAPI)
WebDAV的识别流程如下

1. 对于常见几种请求方法GET、HEAD、POST等,因为常见一些映射都支持。所以不能以请求方法作为Webdav请求的判断
2. w3svc.dll根据请求头的字段的特征来识别识别 
3. 如果请求头里面包含Translate:、If:、Lock-Token:中的一种,就认为是Webdav的请求 
4. W3svc.dll还内置了几个别的请求方法TRACK、TRACE等
5. TRACK就是用于调试错误的,如果收到这样的请求头,w3svc.dll会原样返回请求数据。相当于我们常见的ping.exe。同时,IIS对TRACK请求没有进行LOG记录,这点我们可以用于来获得banner,而不用担心留下日志记录
6. 那么w3svc.dll就会认为是Webdav的请求,交给httpext.dll处理了 

0x3: IIS开启WebDAV

为了安全上的考虑,IIS默认并不会启动WebDAV的功能,因此必须另外来激活它。
通过启动“IIS管理器”,展开本地计算机,选择"Web服务扩展"选择"允许"的途径来启动WebDAV功能

Relevant Link:

http://drops.wooyun.org/papers/238
http://zh.wikipedia.org/wiki/WebDAV
http://en.wikipedia.org/wiki/WebDAV
http://www.microsoft.com/technet/prodtechnol/WindowsServer2003/Library/IIS/844f5e01-4b9e-4dac-897e-2a0bb33f28af.mspx?mfr=true
http://www.rapid7.com/db/modules/auxiliary/scanner/http/dir_webdav_unicode_bypass

0x4: 黑客如何利用IIS WebDAV进行GETSHELL

在学习各种的攻击技术之前,我们首先要明白这种技术的原理,对于IIS PUT匿名可写也一样,WebDAV本质上就是一种HTTP协议,所以我们进行GETSHELL也就是在构造一定格式的HTTP数据包,将我们的payload发送到服务端的webdav的接口上,如果服务器的配置满足以下条件,则可以完成GETSHELL

1. WEB服务器扩展里设置WebDAV为允许 
2. 网站权限配置里开启了写入权限 
3. WEBDAV所在目录的ACL权限开启了写权限

新建webdav虚拟目录

黑客通过iIS PUT漏洞上传webshell的文件之后,可以利用copy、move命令进行重命名,从而达到getshell的目的

Relevant Link:

http://blog.sina.com.cn/s/blog_6b347b2a0101auat.html

测试iis put可以使用netscan、iis put scanner这些工具来做

0x5: WEBDAV加固

1. 验证客户端
配置WebDAV目录的最佳方法取决于要进行的发布类型。当通过IIS来创建虚拟目录时,匿名和集成Windows身份验证都是打开的。虽然这种默认配置对于将客户端连接到服务器、读取网页中的内容以及运行脚本来说可以工作得很好,但要
将客户端发布到目录以及操作该目录上的文件时就会无法胜任。 IIS提供了以下身份验证方法:
1) Kerberos: 域内主要的安全验证协议。Kerberos是用于WebDAV客户端身份验证和文件安全性的最佳选项 2) 匿名身份验证: 允许任何人访问该目录。必须关闭对WebDAV目录的匿名访问。如果不控制访问的用户,您的目录可能会受到某些未知客户端的攻击。 3) 基本身份验证: 以明文形式通过连接发送密码。可以截取和解读明文密码。只有在通过安全套接字层加密密码时,才能打开基本身份验证。 4) 摘要式身份验证: 将信息发布到通过Internet和防火墙访问的服务器上的极佳选择,因为密码在网络上是以MD5哈希值的形式来发送的。然而,密码以明文形式保存在Active Directory中。 5) 高级摘要式身份验证: 摘要式身份验证的改进形式,因为除了以MD5哈希值形式通过网络发送密码外,密码还以MD5哈希值的形式(而不是明文形式)保存在Active Directory中。这使得高级摘要式身份验证成为将信息发布到通
过Internet和防火墙访问的服务器的最佳选择
6) 集成Windows身份验证: 在Intranet上设置WebDAV目录时,最为有效 7) .NET Passport身份验证: 使用cookies来验证用户凭据 2. 控制访问 2.1 配置Web权限 1) 启用读取、写入和目录浏览:启用这些权限允许客户端查看资源列表并进行修改(除非对这些资源没有写入权限)、发布自己的资源以及处理文件 2) 启用写入;并禁用读取和目录浏览:如果只想让客户端在目录中发布私人信息,而不希望别人查看所发布的内容,可以设置写入权限,但不设置读取和目录浏览权限。该配置在客户端端提交选票或性能检查时非常有用。 3) 启用读取和写入;并禁用目录浏览:如果希望通过隐藏文件名来提高安全性,可设置该配置。然而,请注意,通过隐藏文件名来设置安全性是一种低级的安全防范措施,因为一个故意破坏者可通过试探和错误信息来猜测出文
件名。
4) 启用索引资源:如果打算让客户端搜索目录资源,请确保启用了索引服务。 2.2 使用DACL控制访问 1) WebDAV 利用了平台和Web服务器提供的安全功能,其中包括权限控制和NTFS文件系统中的随机访问控制列表(DACL) 2) 在NTFS文件系统驱动器上设置WebDAV发布目录时,请确保"Everyone"组只有读取权限。然后授予特定的个人或组写入权限。 2.3 保护脚本代码 1) 如果在发布目录中有一些不想让客户端看到的脚本文件,您可以通过不授予"脚本资源访问"权限来拒绝访问。可执行文件将作为静态 HTML文件处理,除非为该目录启用了"脚本和可执行文件"2) 要阻止.exe 文件下载并作为HTML文件来查看,但允许其运行,可在发布目录的"虚拟目录"属性页中,将执行权限更改为"脚本和可执行文件",这一权限级别使所有可执行文件受"脚本资源访问"设置的影响。换句话说,如
果选中了"脚本资源访问",有读取权限的客户端可以看到所有的可执行文件;有写入权限的客户端既可运行它们,也可以编辑它们。

Relevant Link:

http://longsago.blog.163.com/blog/static/168237037201111595351401/
http://documents.software.dell.com/DOC223423
http://msdn.microsoft.com/zh-cn/library/cc779784(v=ws.10).aspx

 

5. IIS 7.0 Anonymous PUT(WEBDAV匿名可写)加固

IIS 7的WEBDAVDE开关和IIS 6上是类似的

 

6. IIS >= 7.5 Anonymous PUT(WEBDAV匿名可写)加固

http://www.iis.net/learn/install/installing-publishing-technologies/installing-and-configuring-webdav-on-iis

待研究

 

7. IIS ISAPI Filter(isapiFilters) 

在IIS下,要想实现后门,或者从本质上讲要实现对HTTP请求的自定义处理,通过自定义的IIS Handler,黑客可以将攻击的payload放在正常的HTTP流量中,可以通过编写IIS Filter、或者IIS Extension来实现

0x1: ISAPI Filter简介

ISAPI filters are DLL files that can be used to modify and enhance the functionality provided by IIS. ISAPI filters always run on an IIS server, filtering every request until they 
find one they need to process. The ability to examine and modify both incoming and outgoing streams of data makes ISAPI filters powerful and flexible.

IIS ISAPI Filter的作用

//ISAPI filters can be registered with IIS to modify the behavior of a server. For example, filters can perform the following tasks:
1. Change request data (URLs or headers) sent by the client
2. Control which physical file gets mapped to the URL(URL Mapping)
3. Control the user name and password used with anonymous or basic authentication
4. Modify or analyze a request after authentication is complete
5. Modify a response going back to the client(Outing Package Filter)
6. Run custom processing on "access denied" responses
7. Run processing when a request is complete
8. Run processing when a connection with the client is closed
9. Perform special logging or traffic analysis.
10. Perform custom authentication.
11. Handle encryption and compression.

0x2: ISAPI Filter Processing Sequence

The following steps outline the interaction between ISAPI filters and IIS:

1. When IIS initially loads an ISAPI filter, it also creates and partially populates an HTTP_FILTER_VERSION structure. It then calls the filter's GetFilterVersion function, passing 
a pointer to the new structure as a parameter.
2. The ISAPI filter populates the HTTP_FILTER_VERSION structure with version information and descriptive information. More importantly, the filter also uses HTTP_FILTER_VERSION to specify which event notifications it should receive(Filter需要告诉IIS自己所关注的事件Event), and to declare the general
priority level for the filter(当多个Filter都注册了同一个事件Event的时候,需要根据Filter的Priority来进行排序). In addition, the filter also indicates whether it is interested in events from secure ports only, unsecure ports only, or both. 3. Each HTTP transaction between IIS and a client browser triggers several distinct events. Every time an event occurs for which an ISAPI filter is registered, IIS calls the
filter's HttpFilterProc entry-point function(当HTTP事件到达时,调用对应的处理函数). If more than one ISAPI filter is registered for a given event(事件驱动模型), IIS notifies the filters that the event occurred. The filters, which are marked as high, medium, or low
priority, are notified according to priority in descending order. If more than one ISAPI filter is declared the same general priority level, IIS uses the order in which the filters
appear in the FilterLoadOrder property to resolve the tie. 4. The ISAPI filter uses the notification type information, passed by IIS as a parameter to HttpFilterProc, to determine which particular data structure is pointed to by the other
HttpFilterProc parameter, pvNotification. The ISAPI filter then uses the data contained in that data structure, as well as in the context structure HTTP_FILTER_CONTEXT, to perform
any custom processing.
5. Once processing is complete, the filter returns one of the SF_STATUS status codes to IIS, and IIS continues processing the HTTP request or response until another event occurs

for which ISAPI filters are registered. 6. When the Web service is stopped or unloaded, IIS calls TerminateFilter in all ISAPI filters as part of its shutdown sequence, for any filters that implemented and exported the
function. TerminateFilter is typically used to perform cleanup and de-allocation of allocated resources.

0x3: ISAPI Filter Event Notifications

In general, the events that occur during the processing of a typical Internet Information Services (IIS) request and response are regular and predictable. The following list outlines the most common ordering of events.

1. SF_NOTIFY_READ_RAW_DATA: HttpFilterProc Function is passed a pointer to HTTP_FILTER_RAW_DATA Structure (IIS))
When a client sends a request, one or more SF_NOTIFY_READ_RAW_DATA notifications occur. Data is read until the client has sent all of the HTTP headers associated with the request.

2. SF_NOTIFY_PREPROC_HEADERS: (HttpFilterProc Function is passed a pointer to HTTP_FILTER_PREPROC_HEADERS Structure (IIS))
A single SF_NOTIFY_PREPROC_HEADERS notification occurs for each request. This notification indicates that the server has completed preprocessing of the headers associated with the 
request, but has not yet begun to process the information in the headers. 3. SF_NOTIFY_URL_MAP: (HttpFilterProc Function is passed a pointer to HTTP_FILTER_URL_MAP Structure (IIS)) An SF_NOTIFY_URL_MAP notification occurs whenever the server is converting a URL to a physical path. This notification occurs at least once after the preprocessed
header's notification for the request, and might occur many additional times during processing of the associated request. 4. SF_NOTIFY_AUTHENTICATION: (HttpFilterProc Function is passed a pointer to HTTP_FILTER_AUTHENT Structure (IIS)) An SF_NOTIFY_AUTHENTICATION notification occurs just before IIS attempts to authenticate the client. This notification occurs for every new connection
(including anonymous requests), and every time the client sends enabled user credentials for the target URL, in the form of an authorization header, to be authorized by the server.
The AuthPersistence property setting in the metabase directly affects this filter. Note that not all requests are guaranteed to trigger an authentication notification.
This notification only fires for anonymous requests and requests with an authorization header that specifies Basic authentication. 5. SF_NOTIFY_AUTH_COMPLETE: (HttpFilterProc Function is passed a pointer to HTTP_FILTER_AUTH_COMPLETE_INFO Structure (IIS)) This notification, new to IIS 5.0, offers functionality similar to that of SF_NOTIFY_PREPROC_HEADERS. Specifically, it allows viewing and modification of the method, URL, version,
or headers sent from the client. The key difference between this notification and preprocessed headers is that this notification occurs after the
client's identity has been negotiated with the client. Because of the notification's timing, the AUTH_USER server variable can be used to reliably obtain the identity of the user.
Also, functionality is provided to retrieve a copy of the token that IIS impersonates when processing the request. 6. SF_NOTIFY_READ_RAW_DATA: (HttpFilterProc Function is passed a pointer to HTTP_FILTER_RAW_DATA Structure (IIS)) As mentioned in step 1, if the client has more data to send, one or more SF_NOTIFY_READ_RAW_DATA notifications occur at this point. Each one indicates that IIS has read another
chunk whose size equals either the value of the UploadReadAheadSize metabase property (usually 48 KB), or the remaining number of bytes available (if the chunk is the last one). Because many factors can force IIS to adopt a different chunking scheme, additional raw read events are not always completely predictable. Therefore, ISAPI filters should not rely
on the exact behavior described above. NoteNote: At
this point, IIS begins processing the substance of the request. This can be done by an ISAPI extension, a CGI application, a script engine such as ASP or PERL,
or by IIS itself for static files. 7. SF_NOTIFY_SEND_RESPONSE: (HttpFilterProc Function is passed a pointer to HTTP_FILTER_SEND_RESPONSE Structure) This event occurs after the request is processed and before headers are sent back to the client. 8. SF_NOTIFY_SEND_RAW_DATA As the request handler returns data to the client, one or more SF_NOTIFY_SEND_RAW_DATA notifications occur. 9. SF_NOTIFY_END_OF_REQUEST At the end of each request, the SF_NOTIFY_END_OF_REQUEST notification occurs. 10. SF_NOTIFY_LOG: (HttpFilterProc Function is passed a pointer to HTTP_FILTER_LOG Structure (IIS)) After the HTTP request is complete and just before IIS writes the request to its log, the SF_NOTIFY_LOG notification occurs. 11. SF_NOTIFY_END_OF_NET_SESSION When the connection between the client and the server is closed, the SF_NOTIFY_END_OF_NET_SESSION notification occurs. If a Keep-Alive connection has been negotiated, it is
possible for many HTTP requests to occur before this notification.

可以看到,整个IIS Event的处理流程就是一个处理状态机的逻辑流程,从一个HTTP数据报达到IIS后开始解析,到最后解析完毕这一整个流程,事件之间是遵循一定的先后顺序的

0x4: ISAPI filter编程

和apache、nginx的模块、插件、Filter编程一样,IIS ISAPI Filter也属于一种插件式编程的架构,也就是说我们在编写DLL程序的时候,需要遵循一定的函数名规范、约定,必须声明实现一些指定的函数,在满足大的框架的前提下,去利用IIS传给Filter的参数去实现自定义的HTTP数据报操作逻辑功能

Every ISAPI filter is contained in a separate DLL that must export some entry-point functions

1. BOOL WINAPI GetFilterVersion( PHTTP_FILTER_VERSION pVer); 
该函数是DLL筛选器第一次被加载到站点处理进程时被调用

2. DWORD WINAPI HttpFilterProc(PHTTP_FILTER_CONTEXT pfc,DWORD notificationType,LPVOID pvNotification);
该函数用于响应注册在GetFilterVersion 的形参PHTTP_FILTER_VERSION 中的dwFlags通知事件。根据所注册的通知事件进行相应的筛选处理

3. BOOL WINAPI TerminateFilter(DWORD dwFlags);
该函数是DLL筛选器被站点处理进程卸载时时所调用的处理

这3个函数是我们编写IIS Filter必须要实现的导出函数

The metabase property, FilterLoadOrder, contains a list of all filters that IIS loads when the Web service is started.

Example(redirects HTTP requests to HTTPS)

IIS Filter、IIS Extension本质上是一个DLL,所以我们是在进行DLL编程

使用VS2010创建一个空的DLL项目

并添加2个文件MyISAPIFilter.cpp、MyISAPIFilter.def

MyISAPIFilter.cpp:DLL源代码文件,实现了IIS Filter必须要求实现的导出函数

#define _WIN32_WINNT 0x0400

#include <windows.h>
#include <httpfilt.h>

#define BUFFER_SIZE 2048

BOOL WINAPI GetFilterVersion(PHTTP_FILTER_VERSION pVer)
{
    //char tmp[SF_MAX_FILTER_DESC_LEN] = "RedirectHttpToHttps";
    // Specify the filter version and description.
    pVer->dwFilterVersion = HTTP_FILTER_REVISION;    
    lstrcpy((LPWSTR)(pVer->lpszFilterDesc), (LPWSTR)"RedirectHttpToHttps");
    // Specify the filter notifications.
    pVer->dwFlags = SF_NOTIFY_ORDER_HIGH | SF_NOTIFY_PREPROC_HEADERS;

    return TRUE;
}

DWORD WINAPI HttpFilterProc(PHTTP_FILTER_CONTEXT pfc, DWORD NotificationType, LPVOID pvNotification )
{
    if (NotificationType == SF_NOTIFY_PREPROC_HEADERS)
    {
        char szServerName[BUFFER_SIZE] = "";
        char szSecure[2] = "";
        char szLocationHeader[BUFFER_SIZE + 32] = "";
        char szRequest[BUFFER_SIZE] = "";
        DWORD dwBuffSize = 0;

        // Determine if request was sent over a secure port.
        dwBuffSize = 2;
        pfc->GetServerVariable(
            pfc, "SERVER_PORT_SECURE",
            szSecure, &dwBuffSize);

        // If the request is on a secure port, do not process further.
        if (szSecure[0] == '1')
            return SF_STATUS_REQ_NEXT_NOTIFICATION;

        // Retrieve the URL for the request.
        dwBuffSize = BUFFER_SIZE;
        pfc->GetServerVariable(
            pfc, "URL",
            szRequest, &dwBuffSize);

        // Retrieve the server name.
        dwBuffSize = BUFFER_SIZE;
        pfc->GetServerVariable(
            pfc, "SERVER_NAME",
            szServerName, &dwBuffSize);
        
        // Specify the redirection header.
        wsprintf(
            (LPWSTR)szLocationHeader, (LPWSTR)"Location: https://%s/%s\r\n\r\n",
            szServerName, &szRequest[1]);
        pfc->AddResponseHeaders(
            pfc, szLocationHeader, 0);
        pfc->ServerSupportFunction(
            pfc, SF_REQ_SEND_RESPONSE_HEADER, "302 Object Moved",
            (DWORD)"Please resubmit the request using a secure port.", 0);

        return SF_STATUS_REQ_FINISHED;
    }

    return SF_STATUS_REQ_NEXT_NOTIFICATION;
}

MyISAPIFilter.def:导出函数被导出的时候不能被VC++编译器导出后函数名发生改变,所以使用定义模块文件对三个文件进行导出

LIBRARY    "MyISAPIFilter"
EXPORTS
    GetFilterVersion       
    HttpFilterProc              

编译之

加载到IIS指定的站点中

Relevant Link:

http://blog.csdn.net/mycoolx/article/details/6913048
http://www.codeproject.com/Articles/2570/Discover-ISAPI-Working-with-GET-POST-data
http://www.codeproject.com/KB/ISAPI/
http://msdn.microsoft.com/en-us/library/ms525035.aspx
http://www.iis.net/configreference/system.webserver/isapifilters
http://blog.526net.com/?p=378
http://msdn.microsoft.com/en-us/library/ms525103(v=vs.90).aspx
http://msdn.microsoft.com/en-us/library/ms524610(v=vs.90).aspx
http://msdn.microsoft.com/en-us/library/ms525612(v=vs.90).aspx
http://msdn.microsoft.com/en-us/library/ms524855(v=vs.90).aspx
http://www.hacker.com.cn/show-16-1250-1.html
http://www.xfocus.net/articles/200508/813.html
http://technet.microsoft.com/zh-cn/library/cc733109(v=ws.10).aspx

 

8. IIS Extension

我们已经学习了IIS ISAPI Filter,在继续学习IIS Extension之前,我们需要梳理一下IIS Filter和IIS Extension的概念

/*
ISAPI分为两种:ISAPI extension(ISAPI扩展)和ISAPI filter(ISAPI筛选器)
*/
1. ISAPI服务器扩展(ISAPI extension)
    1) 可以被 HTTP 服务器加载和调用的DLL
    2) ISAPI扩展(extension)也称为Internet服务器应用程序(ISA),用于增强符合Internet服务器API(ISAPI)的服务器的功能
    3) ISAPI扩展(extensio)通过浏览器应用程序调用,并且将相似的功能提供给通用网关接口(CGI)应用程序

2. ISAPI筛选器(ISAPI Filter)
    1) 在启用ISAPI的HTTP服务器上运行的DLL,用以筛选与服务器之间来回传送的数据
    2) 该筛选器可以完成一些前置、后置处理,通过注册事件的通知的方式,当发生选定事件时,筛选器被调用
        2.1) 登录或URL映射
        2.2) 监视及更改数据(在数据从服务器传输到客户端或相反的过程中)
        2.3) 使用ISAPI筛选器提供增强的HTTP请求记录(例如,跟踪登录到服务器的用户)、自定义加密、自定义压缩或其他身份验证方法

ISAPI扩展(ISAPI extension)同样需要3个导出函数(TerminateExtension是可选的):

1. BOOL WINAPI GetExtensionVersion( HSE_VERSION_INFO  *pVer);
该函数是扩展DLL文件第一次被加载到站点处理进程时被调用。

2. DWORD WINAPI HttpExtensionProc( LPEXTENSION_CONTROL_BLOCK  lpECB);
该函数是IIS服务每次触发ISAPI扩展时所调用的函数。也就是IIS服务增强服务时具体的实现内容通过本函数的调用。

3. BOOL WINAPI TerminateExtension( DWORD  dwFlags);
该函数是ISAPI扩展DLL文件从进程中被载时调用一次。

Example(validate credit number)

IIS Extension的编程和IIS Filter的编程很类似,都是在进行DLL编程

使用VS2010创建一个空的DLL项目

添加2个头文件StdAfx.cpp、StdAfx.h

StdAfx.cpp

// stdafx.cpp : source file that includes just the standard includes
//    validate.pch will be the pre-compiled header
//    stdafx.obj will contain the pre-compiled type information

#include "stdafx.h"

// TODO: reference any additional headers you need in STDAFX.H
// and not in this file

StdAfx.h

// stdafx.h : include file for standard system include files,
//  or project specific include files that are used frequently, but
//      are changed infrequently
//

#if !defined(AFX_STDAFX_H__3D1F2106_3F8E_45D4_BC4E_E93F382011FC__INCLUDED_)
#define AFX_STDAFX_H__3D1F2106_3F8E_45D4_BC4E_E93F382011FC__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000


// Insert your headers here
#define WIN32_LEAN_AND_MEAN        // Exclude rarely-used stuff from Windows headers

#include <windows.h>

// TODO: reference additional headers your program requires here

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_STDAFX_H__3D1F2106_3F8E_45D4_BC4E_E93F382011FC__INCLUDED_)

添加2个源文件validate.cpp、validate.def

validate.def

; Validate.def : Declares the module parameters for the DLL.

LIBRARY      "Validate"
DESCRIPTION  'Validate ISAPI Extension'

EXPORTS
    ; Explicit exports can go here

    HttpExtensionProc    @1
    GetExtensionVersion    @2

validate.cpp

// validate.cpp : Defines the entry point for the DLL application.
//

#include "StdAfx.h"
#include "httpext.h"
#include "stdio.h"

#define ERR_WRONG_NUMBER_OF_DIGITS   1
#define ERR_NOT_A_MASTERCARD         2
#define ERR_INVALID_CC               3
#define ERR_INVALID_INPUT            4

void WriteContext(EXTENSION_CONTROL_BLOCK *pECB, char *pszFormat, ...);

BYTE CheckCC(const char *pszNumber)
{
    int i = 0;
  if(strlen(pszNumber) != 16)
    return ERR_WRONG_NUMBER_OF_DIGITS;

  for(i = 0; i < 16; i++)
    if(!isdigit(pszNumber[i]))
      return ERR_INVALID_INPUT;

  if(pszNumber[0] != '5' || pszNumber[1] < '1' || pszNumber[1] > '5')
    return ERR_NOT_A_MASTERCARD;

  int nSum;
  for(i = 0, nSum = 0; i < 16; i += 2)
  {
    int nDigit = (pszNumber[i] - 48) * 2;
    nSum += (nDigit < 10 ? nDigit : nDigit / 10 + nDigit % 10) + (pszNumber[i + 1] - 48);
  }

  if(nSum % 10)
    return ERR_INVALID_CC;
    
  return 0;
}

BOOL APIENTRY DllMain(HANDLE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
{
    return TRUE;
}


BOOL WINAPI GetExtensionVersion(HSE_VERSION_INFO *pVer)
{
    pVer->dwExtensionVersion = HSE_VERSION;
    strncpy(pVer->lpszExtensionDesc, "Validate ISAPI Extension", HSE_MAX_EXT_DLL_NAME_LEN);

    return TRUE;
}

void StartContext(EXTENSION_CONTROL_BLOCK *pECB)
{
    WriteContext(pECB, "<html>\r\n<body>\r\n");
}

void EndContext(EXTENSION_CONTROL_BLOCK *pECB)
{
    WriteContext(pECB, "</body>\r\n</html>");
}

void WriteContext(EXTENSION_CONTROL_BLOCK *pECB, char *pszFormat, ...)
{
    char szBuffer[1024];
    va_list arg_ptr;
    va_start(arg_ptr, pszFormat); 
    vsprintf(szBuffer, pszFormat, arg_ptr);
    va_end(arg_ptr);
    
    DWORD dwSize = strlen(szBuffer);
    pECB->WriteClient(pECB->ConnID, szBuffer, &dwSize, 0);
}

DWORD WINAPI HttpExtensionProc(EXTENSION_CONTROL_BLOCK *pECB)
{
    StartContext(pECB);

    BYTE byRet = CheckCC(pECB->lpszQueryString);
    if(!byRet)
    {
        //this is a valid master card, echo a suitable string to the client
        WriteContext(pECB, "<p><b><font face='Verdana' color='#008000'>Congratulations!</font></b></p>");
        WriteContext(pECB, "<p><font size='2' face='Verdana'>%s is a valid matser card #</font></p>\r\n", pECB->lpszQueryString);
    }
    else
    {
        //this is an invalid master card, echo a proper string to the client!
        WriteContext(pECB, "<p><b><font face='Verdana' color='#800000'>Sorry!</font></b></p>");
        WriteContext(pECB, "<p><font size='2' face='Verdana'>What you have entered is an invalid master card#</font></p>\r\n");
    }

    EndContext(pECB);

    return HSE_STATUS_SUCCESS;
}

编译之

载入IIS

Relevant Link:

http://www.codeproject.com/Articles/1432/What-is-an-ISAPI-Extension
http://blog.csdn.net/mycoolx/article/details/6913048
http://msdn.microsoft.com/en-us/cc302003.aspx

 

9. IIS FTP匿名登录的自动化修复

需要注意的是

1. 对于WINDOWS IIS FTP API来说,IIS 6.0和IIS 7是两套不同的API,因此针对IIS FTP的匿名访问的禁用,需要同时准备2套API调用代码,因为我们并不知道客户端的IIS是什么版本的
2. 我们要修复(禁用)的是IIS FTP的匿名登录,仅仅是匿名登录,因为"可写"并不算系统配置的漏洞,这本来就是正常的功能,而匿名登录才是一个导致黑客入侵的攻击向量

0x1: 通过API方式编程实现IIS 6.0 FTP匿名登录禁用

IIS(Internet Information Service) 6.0属于windows操作系统的一部分,我们可以使用IIS Programmatic Administration SDK API去操作IIS

//IIS Programmatic Administration SDK的适用范围
http://msdn.microsoft.com/en-us/library/ms525568(v=vs.90).aspx
//MSDN官方的介绍
http://msdn.microsoft.com/en-us/library/ms525041(v=vs.90).aspx

Technology

1. Active Directory Service Interfaces (ADSI)
Automation-compliant languages like C, Visual C++, Visual Basic, Microsoft Visual Basic Scripting Edition (VBScript), Microsoft JScript, PerlScript
http://blog.sina.com.cn/s/blog_722787fe0100md61.html
ADSI(Active Directory Services Interface)是Microsoft新推出的一项技术,它统一了许多底层服务的编程接口,程序员可以使用一致的对象技术来访问这些底层服务。 ADSI把这些服务的公共部分提取出来,同时隔离出相异
的部分,程序员可以用统一的接口访问底层服务的公共部分,并延伸到底层服务的专有部分

2. Windows Management Instrumentation (WMI)
Automation-compliant languages like C, C++, Visual Basic, VBScript, or JScript, PerlScript
http://blog.csdn.net/hzy694358/article/details/6717042

3. System.DirectoryServices
.NET-compliant languages like C# and Visual Basic.NET

4. Admin Base Object (ABO) interfaces
C, C++, Visual Basic

5. Other IIS interfaces for low-level logging and service control
C, C++, Visual Basic

由于windows的IIS API接口是一个在不断发展的体系,因此对于不同版本的操作系统、不同版本的IIS,它们所能使用的API是不同的,在编程和使用的过程中我们需要特别关注不同API对操作系统版本、IIS版本的限制

1. ADSI
    1) C/C++/VB6.0 code
    2) Script code
2. WMI
    1) C/C++/VB6.0 code
    2) Script code
3. ABO
    1) C/C++/VB6.0 code

1. ADSI(Active Directory Service Interfaces)技术

Use ADSI to programmatically configure IIS in a script or compiled program. Changes take place immediately without a need to stop and start the server.

适用范围

1. IIS 4.0
2. IIS 5.0
3. IIS 5.1
4. IIS 6.0

code(disabled the ftp anonymous login switch)

// fixftp6.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"

#include <atlstr.h>
#include <initguid.h>
#include <objbase.h>
#include <iads.h>
#include <adshlp.h>
#include <atlbase.h>
#include <iiisext.h>
#include <iisext_i.c>
#include <comdef.h> 

#include <lm.h> 

#include <string>
#include <sstream>
#include <vector>
#include <iostream>

using namespace std;

#pragma comment(lib, "Activeds.lib")
#pragma comment(lib, "Adsiid.lib")
#pragma comment(lib, "netapi32.lib")

wstring logFilename;

// FTP 6.0 匿名访问
HRESULT SetIISFtpAnonymousState(bool anony, bool applyToAll, vector<wstring> &ftpNames)
{
    wstring restoreNames;
    HRESULT hr;
    IADs *pADs = NULL;
    IADsContainer* iContainer = NULL;
    IUnknown *pUnk = NULL;
    IDispatch *pDisp = NULL;
    

    //The ADsGetObject function binds to an object given its path and a specified interface identifier.
    hr = ADsGetObject( L"IIS://localhost/MSFTPSVC", IID_IADsContainer, (void **)&iContainer );
    if (FAILED(hr))
    {
        wprintf(L"ADsGetObject failed %x\n", hr);
        return hr;
    }

    //The IADsContainer::get__NewEnum method Retrieves an enumerator object for the container. The enumerator object implements the IEnumVARIANT interface to enumerate the children
of the container object.
hr = iContainer->get__NewEnum(&pUnk); if (FAILED(hr)) { wprintf(L"get__NewEnum failed\n"); return hr; } IEnumVARIANT *pEnum; //Retrieves pointers to the supported interfaces on an object. This method calls IUnknown::AddRef on the pointer it returns. hr = pUnk->QueryInterface(IID_IEnumVARIANT, (void**)&pEnum); if (FAILED(hr)) { wprintf(L"QueryInterface failed\n"); return hr; } BSTR className = NULL; IADs *iADs = NULL; ULONG lFetch; VARIANT var; //Initializes a variant. VariantInit(&var); /* Retrieves the specified items in the enumeration sequence. */ hr = pEnum->Next(1, &var, &lFetch); while(hr == S_OK) { if (lFetch == 1) { //Exposes objects, methods and properties to programming tools and other applications that support Automation. pDisp = V_DISPATCH(&var); //The IADs interface defines the basic object features, that is, properties and methods, of any ADSI object. Examples of ADSI objects include users, computers, services,
organization of user accounts and computers, file systems, and file service operations.
pDisp->QueryInterface(IID_IADs, (void**)&iADs); //get the class iADs->get_Class(&className); if (wcscmp(className, L"IIsFtpServer") == 0) { BSTR name = NULL; //get the name iADs->get_Name(&name); VARIANT status; VariantInit(&status); //initialize the state V_VT(&status) = VT_BOOL; /* The IADs::Get method retrieves a property of a given name from the property cache 获取当前FTP站点的匿名登录开关状态 */ iADs->Get(L"AllowAnonymous", &status); //loads into the property cache values of the supported properties of this ADSI object from the underlying directory store. iADs->GetInfo(); //是否需要将当前设置应用到所有FTP站点 if (applyToAll) {//all V_VT(&var) = VT_BOOL; //anony:用户传入的匿名访问开关 V_BOOL(&var) = anony; //如果当前FTP站点的匿名登录开关为"打开允许状态",并且传入的参数指示为"需要关闭" if (V_BOOL(&status) && anony == false) { //将匿名访问的登录开关设置为false iADs->Put(L"AllowAnonymous", var); iADs->SetInfo(); //记录设置的FTP列表 if (restoreNames.empty()) { restoreNames = wstring(name); } else { restoreNames += L'|'; restoreNames += wstring(name); } wprintf(L"FTP(%s) AllowAnonymous %s\n", name, anony ? L"enabled" : L"disabled"); } } else {//single for (vector<wstring>::iterator it = ftpNames.begin(); it < ftpNames.end(); it++) { if (*it == name) { V_VT(&var) = VT_BOOL; V_BOOL(&var) = anony; iADs->Put(L"AllowAnonymous", var); iADs->SetInfo(); wprintf(L"FTP(%s) AllowAnonymous %s\n", name, anony ? L"enabled" : L"disabled"); } } } } iADs->Release(); SysFreeString(className); } VariantClear(&var); pDisp->Release(); pDisp = NULL; //继续遍历 hr = pEnum->Next(1, &var, &lFetch); } if (!restoreNames.empty()) { WritePrivateProfileString(L"restore", L"ftp", restoreNames.c_str(), logFilename.c_str()); } return hr; } int _tmain(int argc, _TCHAR* argv[]) { HRESULT hr; //ADSI 基于 com如果忽略了调用 CoInitialize 或 OleInitialize,AdsGetObject,它将返回 HRESULT 的 MK_E_SYNTAX (0x800401e4,"无效语法"), hr = CoInitializeEx( NULL, COINIT_MULTITHREADED ); if ( FAILED( hr ) ) { wprintf( L"CoInitialize failed. Error 0x%0x\n", hr ); return hr; } vector<wstring> restoreFtpNames; SetIISFtpAnonymousState(false, true, restoreFtpNames); // 禁用所有 return 0; }

编译后运行

Relevant Link:

http://msdn.microsoft.com/en-us/library/ms524767(v=vs.90).aspx

2. WMI(Windows Management Instrumentation)技术

Use WMI to programmatically configure IIS in a script or compiled program. Changes take place immediately without needing to stop and start the server.

适用范围

1. IIS 6.0 

Windows Management Instrumentation是WBEM的Windows实现。通过WMI,我们可以获取关于硬件\软件的数据,也可以提供关于硬件或软件服务的数据给WMI

code(get system information)

// wmi_getsysinfo.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"

#include <iostream>
#include <comdef.h>
#include <Wbemidl.h>

# pragma comment(lib, "wbemuuid.lib")

using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
    HRESULT hres;

    /*
    Initialize COM.
    由于用C++编写WMI应用是基于COM技术的,所以必须初始化COM库 
    */
    hres =  CoInitializeEx(0, COINIT_MULTITHREADED); 
    if (FAILED(hres))
    {
        cout << "Failed to initialize COM library. " 
            << "Error code = 0x" 
            << hex << hres << endl;
        return 1;              // Program has failed.
    }

    /*
    Initialize
    初始化COM库安全级别
    */
    hres =  CoInitializeSecurity(
        NULL,     
        -1,      // COM negotiates service                  
        NULL,    // Authentication services
        NULL,    // Reserved
        RPC_C_AUTHN_LEVEL_DEFAULT,    // authentication
        RPC_C_IMP_LEVEL_IMPERSONATE,  // Impersonation
        NULL,             // Authentication info 
        EOAC_NONE,        // Additional capabilities
        NULL              // Reserved
        );
    if (FAILED(hres))
    {
        cout << "Failed to initialize security. " 
            << "Error code = 0x" 
            << hex << hres << endl;
        CoUninitialize();
        return 1;          // Program has failed.
    }

    // Obtain the initial locator to Windows Management
    // on a particular host computer.
    IWbemLocator *pLoc = 0;
    /*
    连接到WMI命名空间
    通过调用CoCreateInstance初始化WMI的定位器(IWbemLocator类型的实例)
    */
    hres = CoCreateInstance(
        CLSID_WbemLocator,             
        0, 
        CLSCTX_INPROC_SERVER, 
        IID_IWbemLocator, (LPVOID *) &pLoc);
    if (FAILED(hres))
    {
        cout << "Failed to create IWbemLocator object. "
            << "Error code = 0x"
            << hex << hres << endl;
        CoUninitialize();
        return 1;       // Program has failed.
    }

    IWbemServices *pSvc = 0;

    // Connect to the root\cimv2 namespace with the
    // current user and obtain pointer pSvc
    // to make IWbemServices calls.
    /*
    调用IWbemLocator::ConnectServer方法,通过这个定位器连接到WMI的命名空间,通过把一个IWbemServices的实例以参数形式传递给ConnectServer方法,就会创建这个服务。如我们需要一些BIOS信息,那么需要使用的
WMI提供程序是Win32_BIOS,则需要连接到ROOT//CIMV2命名空间中。
*/ hres = pLoc->ConnectServer( _bstr_t(L"ROOT\\CIMV2"), // WMI namespace NULL, // User name NULL, // User password 0, // Locale NULL, // Security flags 0, // Authority 0, // Context object &pSvc // IWbemServices proxy ); if (FAILED(hres)) { cout << "Could not connect. Error code = 0x" << hex << hres << endl; pLoc->Release(); CoUninitialize(); return 1; // Program has failed. } cout << "Connected to ROOT\\CIMV2 WMI namespace" << endl; // Set the IWbemServices proxy so that impersonation // of the user (client) occurs. /* 设置WMI服务的安全级别 根据上一步得到的服务,设置相应的服务安全级别。通常来说,如果我们没有设置适当的安全属性,COM安全方案不允许一个进程去访问另一个进程,因此如果我们要访问一个外部进程的对象,那么我们应该设置适当的
IWbemServices的安全级别。
*/ hres = CoSetProxyBlanket( pSvc, // the proxy to set RPC_C_AUTHN_WINNT, // authentication service RPC_C_AUTHZ_NONE, // authorization service NULL, // Server principal name RPC_C_AUTHN_LEVEL_CALL, // authentication level RPC_C_IMP_LEVEL_IMPERSONATE, // impersonation level NULL, // client identity EOAC_NONE // proxy capabilities ); if (FAILED(hres)) { cout << "Could not set proxy blanket. Error code = 0x" << hex << hres << endl; pSvc->Release(); pLoc->Release(); CoUninitialize(); return 1; // Program has failed. } //通过WQL使用WMI服务 IEnumWbemClassObject* pEnumerator = NULL; hres = pSvc->ExecQuery( bstr_t("WQL"), bstr_t("SELECT * FROM Win32_OperatingSystem"), WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumerator); if (FAILED(hres)) { cout << "Query for processes failed. " << "Error code = 0x" << hex << hres << endl; pSvc->Release(); pLoc->Release(); CoUninitialize(); return 1; // Program has failed. } else { IWbemClassObject *pclsObj; ULONG uReturn = 0; while (pEnumerator) { hres = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn); if(0 == uReturn) { break; } VARIANT vtProp; // Get the value of the Name property hres = pclsObj->Get(L"Name", 0, &vtProp, 0, 0); wcout << "Manufacturer Name : " << vtProp.bstrVal << endl; VariantClear(&vtProp); } } // Cleanup // ======== pSvc->Release(); pLoc->Release(); CoUninitialize(); return 0; }

Relevant Link:

http://msdn.microsoft.com/zh-cn/library/ms974579.aspx
http://blog.csdn.net/xscarlet/article/details/1755063
http://msdn.microsoft.com/en-us/library/aa394554(v=vs.85).aspx

3. System.DirectoryServices技术

4. System.Web.Management技术

5. ABO(Admin Base Objects)技术

Use ABO to programmatically configure IIS in a compiled program written in C, C++, or Visual Basic 6.0.

Using ABO is the fastest method of configuring IIS. It is faster than using ADSI or WMI because the ADSI and WMI providers are wrappers for ABO. 
//ADSI和WMI API本质上是对ABO的一层封装,所以使用ABO技术是最高效的一种方法

code(Creating Virtual Directories)

// abo_create_virtualpath.cpp : 定义控制台应用程序的入口点。
// Precompiled headers: Not Using Precompiled Headers 
//   (otherwise causes a C1010 error)
// Preprocessor Definitions: include UNICODE 
//   (otherwise causes multiple C2664 errors)

#define STRICT
#define UNICODE
#define INITGUID
#define WINVER 0x0400
#define _WIN32_DCOM

#include <WINDOWS.H>
#include <OLE2.H>
#include <winerror.h>
#include <stdio.h>
#include <stdlib.h>
#include <initguid.h>

#include "iadmw.h"
#include "iiscnfg.h"

// Just #define the data and paths to simplify the sample. 
// Note that all vroots of a new server must go under the root 
//   node of the virtual server.  In the following statements, 
//   the virtual server (1) is "/lm/w3svc/1".  The new vroot 
//   will go underneath "/lm/w3svc/1/root" 

// Also note that these strings are UNICODE 
/*
lm:        本地服务器名
W3SVC:    IIS服务器
2:        第2个站点
Root:    站点根目录
*/
#define NEW_VROOT_PATH  TEXT("newvroot") 
#define NEW_VROOT_PARENT TEXT("/lm/w3svc/1/root") 
#define NEW_VROOT_FULLPATH TEXT("/lm/w3svc/1/root/newvroot") 
#define NEW_VROOT_DIRECTORY TEXT("C:\\TEMP") 

int main (int argc, char *argv[]) 
{    
    HRESULT hres;
     
    IMSAdminBase *pcAdmCom = NULL;   // COM interface pointer 
    HRESULT hresError = 0;  // Holds the errors returned by the IMSAdminBase API calls 
    DWORD dwRefCount;  // Holds the refcount of the IMSAdminBase object  

    DWORD dwResult = 0; 

    METADATA_HANDLE hmdParentHandle;  // This is the handle to the parent object of the new vdir. 
    METADATA_HANDLE hmdChildHandle;  // This is the handle to the parent object of the new vdir. 

    if (argc < 2) 
    { 
        puts ("Usage: Create_vdir <machine name>\n  Ex: Create_Vdir adamston1\n\n"); 
        return -1; 
    } 

    printf("You will be adding a new VRoot path in the metabase. \n");
    printf("  Machine Name: %s\n", argv[1]);
    wprintf ( 
    L"  Path: %s\n" 
    L"  Full Path: %s/%s\n",     
    NEW_VROOT_PATH, 
    NEW_VROOT_PARENT, 
    NEW_VROOT_PATH); 

    // These are required for creating a COM object. 
    IClassFactory * pcsfFactory = NULL; 
    COSERVERINFO csiMachineName; 
    COSERVERINFO *pcsiParam = NULL; 

    // Fill the structure for CoGetClassObject. 
    csiMachineName.pAuthInfo = NULL; 
    csiMachineName.dwReserved1 = 0; 
    csiMachineName.dwReserved2 = 0; 
    pcsiParam = &csiMachineName;  

    // Allocate memory for the machine name. 
    csiMachineName.pwszName = (LPWSTR) HeapAlloc (GetProcessHeap(), HEAP_ZERO_MEMORY, (strlen (argv[1]) + 1) * sizeof (WCHAR) ); 
    if (csiMachineName.pwszName == NULL) 
    { 
        printf ("Error allocating memory for MachineName\n"); 
        return E_OUTOFMEMORY; 
    } 

    // Convert Machine Name from ASCII to Unicode. 
    dwResult = MultiByteToWideChar ( 
    CP_ACP, 
    0, 
    argv[1], 
    strlen (argv[1]) + 1, 
    csiMachineName.pwszName, 
    strlen (argv[1]) + 1); 

    if (dwResult == 0) 
    { 
        printf ("Error converting Machine Name to UNICODE\n"); 
        return E_INVALIDARG; 
     }

    // Initialize COM. 
    hresError = CoInitializeEx(NULL, COINIT_MULTITHREADED); 

    if (FAILED(hresError)) 
    { 
        printf ("ERROR: CoInitialize Failed! Error: %d (%#x)\n", hresError, hresError); 
        return hresError; 
    } 

    hresError = CoGetClassObject(GETAdminBaseCLSID(TRUE), CLSCTX_SERVER, pcsiParam, IID_IClassFactory, (void**) &pcsfFactory); 

    if (FAILED(hresError))  
    { 
        printf ("ERROR: CoGetClassObject Failed! Error: %d (%#x)\n", hresError, hresError); 
        return hresError; 
    } 
    else 
    { 
        hresError = pcsfFactory->CreateInstance(NULL, IID_IMSAdminBase, (void **) &pcAdmCom); 
        if (FAILED(hresError))  
        { 
            printf ("ERROR: CreateInstance Failed! Error: %d (%#x)\n", hresError, hresError); 
            pcsfFactory->Release(); 
            return hresError; 
        } 
        pcsfFactory->Release(); 
    } 

    /***********************************************/ 
    /* Important Section */ 

    // Open the path to the parent object. 
    hresError = pcAdmCom->OpenKey ( 
      METADATA_MASTER_ROOT_HANDLE, 
      NEW_VROOT_PARENT, 
      METADATA_PERMISSION_WRITE | METADATA_PERMISSION_READ, 
      1000, 
      &hmdParentHandle); 

    if (FAILED(hresError))  
    { 
        printf ("ERROR: Could not open the Parent Handle! Error: %d (%#x)\n", hresError, hresError); 
        dwRefCount = pcAdmCom->Release(); 
        return hresError; 
    } 

    printf ("Path to parent successfully opened\n"); 
/***********************************/ /* Add the new Key for the VROOT */ hresError = pcAdmCom->AddKey ( hmdParentHandle, NEW_VROOT_PATH); if (FAILED(hresError)) { printf ("ERROR: AddKey Failed! Error: %d (%#x)\n", hresError, hresError); hresError = pcAdmCom->CloseKey(hmdParentHandle); dwRefCount = pcAdmCom->Release(); return hresError; } printf ("New Child successfully added\n"); // Close the handle to the parent and open a new one to the child. // This isn't required, but when the handle is open at the parent, no other // metabase client can access that part of the tree or subsequent child. // Open the child key because it is lower in the metabase and doesn't conflict with as many // other paths. hresError = pcAdmCom->CloseKey(hmdParentHandle); if (FAILED(hresError)) { printf ("ERROR: Could not close the Parent Handle! Error: %d (%#x)\n", hresError, hresError); dwRefCount = pcAdmCom->Release(); return hresError; } hresError = pcAdmCom->OpenKey ( METADATA_MASTER_ROOT_HANDLE, NEW_VROOT_FULLPATH, METADATA_PERMISSION_WRITE | METADATA_PERMISSION_READ, 1000, &hmdChildHandle); if (FAILED(hresError)) { printf ("ERROR: Could not open the Child Handle! Error: %d (%#x)\n", hresError, hresError); dwRefCount = pcAdmCom->Release(); return hresError; } printf ("Path to child successfully opened\n"); /***********************************/ /* The vroot needs a few properties set at the new path in order */ /* for it to work properly. These properties are MD_VR_PATH, MD_KEY_TYPE */ /* and MD_ACCESSPERM. */ METADATA_RECORD mdrNewVrootData; // First, add the MD_VR_PATH - this is required to associate the vroot with a specific // directory on the filesystem mdrNewVrootData.dwMDIdentifier = MD_VR_PATH; // The inheritable attribute means that paths that are created underneath this // path will retain the property from the parent node unless overwritten at the // new child node. mdrNewVrootData.dwMDAttributes = METADATA_INHERIT; mdrNewVrootData.dwMDUserType = IIS_MD_UT_FILE; mdrNewVrootData.dwMDDataType = STRING_METADATA; // Now, create the string. - UNICODE mdrNewVrootData.pbMDData = (PBYTE) NEW_VROOT_DIRECTORY; mdrNewVrootData.dwMDDataLen = (wcslen (NEW_VROOT_DIRECTORY) + 1) * sizeof (WCHAR); mdrNewVrootData.dwMDDataTag = 0; // datatag is a reserved field. // Now, set the property at the new path in the metabase. hresError = pcAdmCom->SetData ( hmdChildHandle, TEXT ("/"), &mdrNewVrootData); if (FAILED(hresError)) { printf ("ERROR: Setting the VR Path Failed! Error: %d (%#x)\n", hresError, hresError); hresError = pcAdmCom->CloseKey(hmdChildHandle); dwRefCount = pcAdmCom->Release(); return hresError; } printf ("Successfully set the vrpath\n"); /***********************************/ // Second, add the MD_KEY_TYPE - this indicates what type of key we are creating - // The vroot type is IISWebVirtualDir mdrNewVrootData.dwMDIdentifier = MD_KEY_TYPE; // The inheritable attribute means that paths that are created underneath this // path will retain the property from the parent node unless overwritten at the // new child node. mdrNewVrootData.dwMDAttributes = METADATA_INHERIT; mdrNewVrootData.dwMDUserType = IIS_MD_UT_FILE; mdrNewVrootData.dwMDDataType = STRING_METADATA; // Now, create the string. - UNICODE mdrNewVrootData.pbMDData = (PBYTE) TEXT("IIsWebVirtualDir"); mdrNewVrootData.dwMDDataLen = (wcslen (TEXT("IIsWebVirtualDir")) + 1) * sizeof (WCHAR); mdrNewVrootData.dwMDDataTag = 0; // datatag is a reserved field. // Now, set the property at the new path in the metabase. hresError = pcAdmCom->SetData ( hmdChildHandle, TEXT ("/"), &mdrNewVrootData); if (FAILED(hresError)) { printf ("ERROR: Setting the Keytype Failed! Error: %d (%#x)\n", hresError, hresError); hresError = pcAdmCom->CloseKey(hmdChildHandle); dwRefCount = pcAdmCom->Release(); return hresError; }
printf (
"Successfully set the Keytype \n"); /***********************************/ // Now, add the MD_ACCESS_PERM - this tells whether we should read, write or // execute files within the directory. For now, we will simply add // READ permissions. mdrNewVrootData.dwMDIdentifier = MD_ACCESS_PERM; // The inheritable attribute means that paths that are created underneath this // path will retain the property from the parent node unless overwritten at the // new child node. mdrNewVrootData.dwMDAttributes = METADATA_INHERIT; mdrNewVrootData.dwMDUserType = IIS_MD_UT_FILE; mdrNewVrootData.dwMDDataType = DWORD_METADATA; //Create the access perm. DWORD dwAccessPerm = 1; // 1 is read only mdrNewVrootData.pbMDData = (PBYTE) &dwAccessPerm; mdrNewVrootData.dwMDDataLen = sizeof (DWORD); mdrNewVrootData.dwMDDataTag = 0; // datatag is a reserved field. // Now, set the property at the new path in the metabase. hresError = pcAdmCom->SetData ( hmdChildHandle, TEXT ("/"), &mdrNewVrootData); if (FAILED(hresError)) { printf ("ERROR: Setting the accessperm failed! Error: %d (%#x)\n", hresError, hresError); hresError = pcAdmCom->CloseKey(hmdChildHandle); dwRefCount = pcAdmCom->Release(); return hresError; } printf ("Successfully set the accessperm\n"); /************************************************/ // We're done!!! Just clean up. hresError = pcAdmCom->CloseKey(hmdChildHandle); if (FAILED(hresError)) { printf ("ERROR: Could not close the Child Handle! Error: %d (%#x)\n", hresError, hresError); dwRefCount = pcAdmCom->Release(); return hresError; } printf ("\nYou Have successfully installed a new VRoot at %S\n", NEW_VROOT_FULLPATH); // Release the object dwRefCount = pcAdmCom->Release(); return ERROR_SUCCESS; }

Relevant Link:

http://msdn.microsoft.com/en-us/library/ms525112(v=vs.90).aspx
http://msdn.microsoft.com/en-us/library/ms524657(v=vs.90).aspx
http://keicode.com/iis/iis21.php
http://msdn.microsoft.com/en-us/library/ms524657%28v=vs.90%29.aspx

6. other IIS management interfaces in administration scripts or applications that configure IIS programmatically技术

Relevant Link:

http://msdn.microsoft.com/en-us/library/ms525885(v=vs.90).aspx

0x2: 通过API方式编程实现IIS 7 FTP匿名登录禁用

在IIS 6使用的ADSI(active directory service interface)无法在在IIS 7.5下使用

在IIS 7.5下禁用FTP匿名登录有很多种方式,包括

1. WMI Scripts代码(实时生效)(有效)
2. 通过appcmd.exe进行IIS操作(命令行方式)(实时生效)(未成功,待研究)
3. 编辑IIS配置文件(非实时生效)(有效)
4. WMI COM API C++代码(实时生效)(未成功,待研究)
5. Microsoft.Web.Administration

1. WMI Scripts代码(实时生效)

' Creates and returns a reference to an Automation object.
Set adminManager = createObject("Microsoft.ApplicationHost.WritableAdminManager")
adminManager.CommitPath = "MACHINE/WEBROOT/APPHOST"

Set sitesSection = adminManager.GetAdminSection("system.applicationHost/sites", "MACHINE/WEBROOT/APPHOST")
' Get the site list of the IIS 7
Set sitesCollection = sitesSection.Collection


For i = 0 To CInt(sitesCollection.Count) -1
    Set siteElement = sitesCollection.Item(i)
    Set ftpServerElement = siteElement.ChildElements.Item("ftpServer")
    Set securityElement = ftpServerElement.ChildElements.Item("security")
    Set authenticationElement = securityElement.ChildElements.Item("authentication")
    Set anonymousAuthenticationElement = authenticationElement.ChildElements.Item("anonymousAuthentication")
    anonymousAuthenticationElement.Properties.Item("enabled").Value = False
Next

adminManager.CommitChanges()

直接运行即可

2. 通过appcmd.exe进行IIS操作(命令行方式)(实时生效)

appcmd是IIS 7.5提供的一个命令行管理工具,使用appcmd可以很方便地通过命令行的方式对IIS进行全方位的管理

pushd C:\Windows\System32\inetsrv
1. 开启匿名访问
appcmd set config /section:anonymousAuthentication /enabled:true  
 
2. 关闭匿名访问
appcmd set config /section:anonymousAuthentication /enabled:false

Relevant Link:

http://www.7edown.com/edu/article/soft_4646_1.html
http://blog.csdn.net/hbu_dcf/article/details/4888642

3. 编辑IIS配置文件(非实时生效)

通过编辑IIS的machine.xml、或者网站自己的web.xml的<anonymousAuthentication>节点,关闭IIS 7 FTP的匿名访问,这种方式需要通过重启IIS从而从新加载配置文件来更新设置

4. WMI COM API C++代码(实时生效)

从WMI本身的架构图我们可以知道,对于WMI Client Consumer来说,不论是使用WMI Scripts、还是使用WMI COM API C++方式对WMI 进行操作,在代码层面都是一样的,它们最终都走了WMI COM API的COM组件这个流程,关于WMI技术的相关知识,请参阅另一篇文章

http://www.cnblogs.com/LittleHann/p/4022669.html

所以从理论上来说,完全可以使用C++达到和WMI Scripts脚本代码同样的目的,从google和baidu的搜索结果上来看,对于使用WMI技术的C++代码,目前没有现成的example code可以借鉴,因此我们需要自己探索如何通过查阅和学习API文档进行功能的实现。
首先,我们需要抓住几个关键点

1. WMI的信息都保存在各自的命名空间中,我们如果需要IIS FTP的匿名访问的配置信息,就需要找到保存有IIS FTP匿名访问配置的指定的命名空间
2. WMI的操作有Put Data、Get Data两种,要进行IIS FTP匿名访问的配置获取,改变配置开关,需要使用不同的Class、Interface

WMI命名空间及类层次查询文档

http://www.wutils.com/wmi/classes/IIsFtpServerSetting.html

先解决第一个问题,通过论坛上一位技术员的评论,得知anonymous开关的命名空间为"ROOT\MicrosoftIISv2",

在"ROOT\MicrosoftIISv2"命名空间中,我们得到2个可能是和FTP匿名有关的Class,"IIsFtpServiceSetting"、和"IIsFtpServerSetting"

查看这2个class的Class.property成员,可以看到,确实有anonymous这个配置项

http://www.wutils.com/wmi/ROOT/MicrosoftIISv2/properties/AllowAnonymous.html

这说明FTP匿名访问的开关很可能和这2个class有关了,我们先尝试去枚举一下这2个配置项

1. WMI Scripts代码

strComputer = "."
Set objWMIService = GetObject _
    ("winmgmts:{authenticationLevel=pktPrivacy}\\" _
        & strComputer & "\root\microsoftiisv2")

Set colItems = objWMIService.ExecQuery _
    ("Select * from IIsFtpServerSetting")
 
For Each objItem in colItems
    Wscript.Echo "Access Execute: " & objItem.AccessExecute
    Wscript.Echo "Access Flags: " & objItem.AccessFlags
    Wscript.Echo "Access No Physical Directory: " & _
        objItem.AccessNoPhysicalDir
    Wscript.Echo "Access No Remote Execute: " & _
        objItem.AccessNoRemoteExecute
    Wscript.Echo "Access No Remote Read: " & _
        objItem.AccessNoRemoteRead
    Wscript.Echo "Access No Remote Script: " & _
        objItem.AccessNoRemoteScript
    Wscript.Echo "Access No Remote Write: " & _
        objItem.AccessNoRemoteWrite
    Wscript.Echo "Access Read: " & objItem.AccessRead
    Wscript.Echo "Access Script: " & objItem.AccessScript
    Wscript.Echo "Access Source: " & objItem.AccessSource
    Wscript.Echo "Access Write: " & objItem.AccessWrite
    Wscript.Echo "AD Connections Password: " & _
        objItem.ADConnectionsPassword
    Wscript.Echo "AD Connections User Name: " & _
        objItem.ADConnectionsUserName
    Wscript.Echo "Admin ACL Bin: " & objItem.AdminACLBin
    Wscript.Echo "Allow Anonymous: " & objItem.AllowAnonymous
    Wscript.Echo "Anonymous Only: " & objItem.AnonymousOnly
    Wscript.Echo "Anonymous Password Sync: " & _
        objItem.AnonymousPasswordSync
    Wscript.Echo "Anonymous User Name: " & _
        objItem.AnonymousUserName
    Wscript.Echo "Anonymous User Password: " & _
        objItem.AnonymousUserPass
    For Each objMessage in objItem.BannerMessage
        Wscript.Echo "Banner Message: " & objMessage
    Next
    Wscript.Echo "Caption: " & objItem.Caption
    Wscript.Echo "Cluster Enabled: " & objItem.ClusterEnabled
    Wscript.Echo "Connection Timeout: " & _
        objItem.ConnectionTimeout
    Wscript.Echo "Default Logon Domain: " & _
        objItem.DefaultLogonDomain
    Wscript.Echo "Description: " & objItem.Description
    Wscript.Echo "Disable Socket Pooling: " & _
        objItem.DisableSocketPooling
    Wscript.Echo "Don't Log: " & objItem.DontLog
    Wscript.Echo "Exit Message: " & objItem.ExitMessage
    Wscript.Echo "FTP Directory Browse Show Long Date: " & _
        objItem.FtpDirBrowseShowLongDate
    Wscript.Echo "FTP Log in Utf8: " & objItem.FtpLogInUtf8
    For Each objMessage in objItem.GreetingMessage
        Wscript.Echo "Greeting Message: " & objMessage
    Next
    Wscript.Echo "Log Anonymous: " & objItem.LogAnonymous
    Wscript.Echo "Log Ext File Bytes Received: " & _
        objItem.LogExtFileBytesRecv
    Wscript.Echo "Log Ext File Bytes Sent: " & _
        objItem.LogExtFileBytesSent
    Wscript.Echo "Log Ext File Client IP: " & _
        objItem.LogExtFileClientIp
    Wscript.Echo "Log Ext File Computer Name: " & _
        objItem.LogExtFileComputerName
    Wscript.Echo "Log Ext File Cookie: " & objItem.LogExtFileCookie
    Wscript.Echo "Log Ext File Date: " & objItem.LogExtFileDate
    Wscript.Echo "Log Ext File Flags: " & objItem.LogExtFileFlags
    Wscript.Echo "Log Ext File Host: " & objItem.LogExtFileHost
    Wscript.Echo "Log Ext File Http Status: " & _
        objItem.LogExtFileHttpStatus
    Wscript.Echo "Log Ext File Http SubStatus: " & _
        objItem.LogExtFileHttpSubStatus
    Wscript.Echo "Log Ext File Method: " & objItem.LogExtFileMethod
    Wscript.Echo "Log Ext File Protocol Version: " & _
        objItem.LogExtFileProtocolVersion
    Wscript.Echo "Log Ext File Referer: " & objItem.LogExtFileReferer
    Wscript.Echo "Log Ext File Server IP: " & _
        objItem.LogExtFileServerIp
    Wscript.Echo "Log Ext File Server Port: " & _
        objItem.LogExtFileServerPort
    Wscript.Echo "Log Ext File Site Name: " & _
        objItem.LogExtFileSiteName
    Wscript.Echo "Log Ext File Time: " & objItem.LogExtFileTime
    Wscript.Echo "Log Ext File Time Taken: " & _
        objItem.LogExtFileTimeTaken
    Wscript.Echo "Log Ext File URI Query: " & _
        objItem.LogExtFileUriQuery
    Wscript.Echo "Log Ext File Uri Stem: " & objItem.LogExtFileUriStem
    Wscript.Echo "Log Ext File User Agent: " & _
        objItem.LogExtFileUserAgent
    Wscript.Echo "Log Ext File User Name: " & objItem.LogExtFileUserName
    Wscript.Echo "Log Ext File Win32 Status: " & _
        objItem.LogExtFileWin32Status
    Wscript.Echo "Log File Directory: " & objItem.LogFileDirectory
    Wscript.Echo "Log File Local Time Rollover: " & _
        objItem.LogFileLocaltimeRollover
    Wscript.Echo "Log File Period: " & objItem.LogFilePeriod
    Wscript.Echo "Log File Truncate Size: " & _
        objItem.LogFileTruncateSize
    Wscript.Echo "Log Non-Anonymous: " & objItem.LogNonAnonymous
    Wscript.Echo "Log Odbc Data Source: " & objItem.LogOdbcDataSource
    Wscript.Echo "Log Odbc Password: " & objItem.LogOdbcPassword
    Wscript.Echo "Log Odbc Table Name: " & objItem.LogOdbcTableName
    Wscript.Echo "Log Odbc User Name: " & objItem.LogOdbcUserName
    Wscript.Echo "Log Plugin Clsid: " & objItem.LogPluginClsid
    Wscript.Echo "Log Type: " & objItem.LogType
    Wscript.Echo "Maximum Clients Message: " & _
        objItem.MaxClientsMessage
    Wscript.Echo "Maximum Connections: " & objItem.MaxConnections
    Wscript.Echo "Maximum Endpoint Connections: " & _
        objItem.MaxEndpointConnections
    Wscript.Echo "MS-DOS Directory Output: " & objItem.MSDOSDirOutput
    Wscript.Echo "Name: " & objItem.Name
    Wscript.Echo "Realm: " & objItem.Realm
    Wscript.Echo "Server AutoStart: " & objItem.ServerAutoStart
    Wscript.Echo "Server Command: " & objItem.ServerCommand
    Wscript.Echo "Server Comment: " & objItem.ServerComment
    Wscript.Echo "Server ID: " & objItem.ServerID
    Wscript.Echo "Server Listen Backlog: " & _
        objItem.ServerListenBacklog
    Wscript.Echo "Server Listen Timeout: " & _
        objItem.ServerListenTimeout
    Wscript.Echo "Server Size: " & objItem.ServerSize
    Wscript.Echo "Setting ID: " & objItem.SettingID
    Wscript.Echo "User Isolation Mode: " & objItem.UserIsolationMode
    Wscript.Echo "Win32 Error: " & objItem.Win32Error
Next

2. WMI COM API C++代码

// wmi_getsysinfo.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"

#include <iostream>
#include <comdef.h>
#include <Wbemidl.h>

# pragma comment(lib, "wbemuuid.lib")

using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
    HRESULT hres;

    /*
    Initialize COM.
    由于用C++编写WMI应用是基于COM技术的,所以必须初始化COM库 
    */
    hres =  CoInitializeEx(0, COINIT_MULTITHREADED); 
    if (FAILED(hres))
    {
        cout << "Failed to initialize COM library. " 
            << "Error code = 0x" 
            << hex << hres << endl;
        return 1;              // Program has failed.
    }

    /*
    Initialize
    初始化COM库安全级别
    */
    hres =  CoInitializeSecurity(
        NULL,     
        -1,      // COM negotiates service                  
        NULL,    // Authentication services
        NULL,    // Reserved
        RPC_C_AUTHN_LEVEL_DEFAULT,    // authentication
        RPC_C_IMP_LEVEL_IMPERSONATE,  // Impersonation
        NULL,             // Authentication info 
        EOAC_NONE,        // Additional capabilities
        NULL              // Reserved
        );
    if (FAILED(hres))
    {
        cout << "Failed to initialize security. " 
            << "Error code = 0x" 
            << hex << hres << endl;
        CoUninitialize();
        return 1;          // Program has failed.
    }

    // Obtain the initial locator to Windows Management
    // on a particular host computer.
    IWbemLocator *pLoc = 0;
    /*
    连接到WMI命名空间
    通过调用CoCreateInstance初始化WMI的定位器(IWbemLocator类型的实例)
    */
    hres = CoCreateInstance(
        CLSID_WbemLocator,             
        0, 
        CLSCTX_INPROC_SERVER, 
        IID_IWbemLocator, (LPVOID *) &pLoc);
    if (FAILED(hres))
    {
        cout << "Failed to create IWbemLocator object. "
            << "Error code = 0x"
            << hex << hres << endl;
        CoUninitialize();
        return 1;       // Program has failed.
    }

    IWbemServices *pSvc = 0;

    // Connect to the root\cimv2 namespace with the
    // current user and obtain pointer pSvc
    // to make IWbemServices calls.
    /*
    调用IWbemLocator::ConnectServer方法,通过这个定位器连接到WMI的命名空间,通过把一个IWbemServices的实例以参数形式传递给ConnectServer方法,就会创建这个服务 
    */
    hres = pLoc->ConnectServer(
        _bstr_t(L"ROOT\\microsoftiisv2"), // WMI namespace
        NULL,                    // User name
        NULL,                    // User password
        0,                       // Locale
        NULL,                    // Security flags                 
        0,                       // Authority       
        0,                       // Context object
        &pSvc                    // IWbemServices proxy
        );                            
    if (FAILED(hres))
    {
        cout << "Could not connect. Error code = 0x" 
            << hex << hres << endl;
        pLoc->Release();     
        CoUninitialize();
        return 1;                // Program has failed.
    }

    cout << "Connected to ROOT\\microsoftiisv2 WMI namespace" << endl;

    // Set the IWbemServices proxy so that impersonation
    // of the user (client) occurs.
    /*
    设置WMI服务的安全级别
    根据上一步得到的服务,设置相应的服务安全级别。通常来说,如果我们没有设置适当的安全属性,COM安全方案不允许一个进程去访问另一个进程,因此如果我们要访问一个外部进程的对象,那么我们应该设置适当的IWbemServices的安全级别。
    */
    hres = CoSetProxyBlanket(

        pSvc,                         // the proxy to set
        RPC_C_AUTHN_WINNT,            // authentication service
        RPC_C_AUTHZ_NONE,             // authorization service
        NULL,                         // Server principal name
        RPC_C_AUTHN_LEVEL_CALL,       // authentication level
        RPC_C_IMP_LEVEL_IMPERSONATE,  // impersonation level
        NULL,                         // client identity 
        EOAC_NONE                     // proxy capabilities     
        );
    if (FAILED(hres))
    {
        cout << "Could not set proxy blanket. Error code = 0x" 
            << hex << hres << endl;
        pSvc->Release();
        pLoc->Release();     
        CoUninitialize();
        return 1;               // Program has failed.
    }

    //通过WQL使用WMI服务
    IEnumWbemClassObject* pEnumerator = NULL;
    hres = pSvc->ExecQuery(
        bstr_t("WQL"), 
        bstr_t("Select * From IIsFtpServerSetting"),
        WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, 
        NULL,
        &pEnumerator);

    if (FAILED(hres))
    {
        cout << "Query for processes failed. "
            << "Error code = 0x" 
            << hex << hres << endl;
        pSvc->Release();
        pLoc->Release();     
        CoUninitialize();
        return 1;               // Program has failed.
    }
    else
    { 
        IWbemClassObject *pclsObj;
        ULONG uReturn = 0;

        while (pEnumerator)
        {
            hres = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);

            
            if(0 == uReturn)
            {
                break;
            }
            

            VARIANT vtProp;

            // Get the value of the Name property
            hres = pclsObj->Get(L"AllowAnonymous", 0, &vtProp, 0, 0);
            wcout << "Manufacturer Name : " << (bool)vtProp.boolVal << endl;
            VariantClear(&vtProp);
        }

    }

    // Cleanup
    // ========

    pSvc->Release();
    pLoc->Release();     
    CoUninitialize(); 

    return 0;
}

在开关FTP的匿名登录之后,程序的运行结果也相应发生了变化

我们已经明白了如何通过WMI"获取"IIS FTP的匿名登录设置信息,现在要继续学习如何去"设置改变"这个开关值

3. 遗留的问题

1. WMI Scripts代码可以在IIS 7.5 FTP工作正常
2. 从WMI的整体架构来看,理论上应该是WMI Scripts可以实现的功能,WMI COM API C++代码也应该可以实现
3. 但是实际的实验结果是:枚举IIS 7.5 FTP配置信息的代码只能工作在IIS 6下,在IIS 7.5下无法工作得到结果,难道是因为在IIS 7.5下不支持WMI的那套机制了?(我不敢乱作猜测,很有可能是我不知道)
4. 从IIS 6到IIS 7.5是一个比较大的跨越,不论是从软件架构、功能、还是API接口上来看,因此我们在针对IIS进行主机防御和修复工作的过程中,需要针对IIS 7.5的情况单独作研究

Relevant Link:

http://msdn.microsoft.com/en-us/library/ms525041(v=vs.90).aspx
http://msdn.microsoft.com/en-us/library/ms525885(v=vs.90).aspx
http://technet.microsoft.com/en-us/library/cc772200(v=ws.10).aspx
http://forums.iis.net/t/1173524.aspx
http://technet.microsoft.com/en-us/library/cc731244(v=ws.10).aspx
http://blogs.msdn.com/b/robert_mcmurray/archive/2012/10/03/programmatically-starting-and-stopping-ftp-sites-in-iis-7-and-iis-8.aspx
http://www.iis.net/downloads/community/2007/01/iis7-native-api-(cplusplus)-starter-kit
http://technet.microsoft.com/en-us/library/cc732976(v=ws.10).aspx

5. Microsoft.Web.Administration

IIS 7.0 and "above"(IIS 7.5) provide a comprehensive managed-code management application programming interface (API) that allows complete manipulation of the XML configuration files and convenience access to server objects.
IIS includes Microsoft.Web.Administration, which is a new a management API for the web server that enables editing configuration through complete manipulation of the XML configuration files. It also provides convenience objects to manage the server, its properties and state. The configuration editing aspect of the API provides programmatic access to read and write configuration properties in the IIS configuration file hierarchy and specific configuration files. The object management aspect of this API provides a series of top-level administration objects for direct management of the server

#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
#include <ahadmin.h>
#include <crtdbg.h>
#include <string>

using namespace std;

void PrintPropertiesOfElement(IAppHostElement *pElement)
{
    HRESULT hr = S_OK;

    IAppHostPropertyCollection *pProperties = NULL;
    IAppHostProperty *pProperty = NULL;

    hr = pElement->get_Properties(&pProperties);

    DWORD properties_count = 0;
    hr = pProperties->get_Count(&properties_count);

    VARIANT vtIndex;
    vtIndex.vt = VT_INT;
    for(DWORD i=0; i<properties_count; ++i)
    {
        vtIndex.intVal = i;
        hr = pProperties->get_Item(vtIndex, &pProperty);

        BSTR strName;
        BSTR strValue;
        hr = pProperty->get_Name(&strName);
        hr = pProperty->get_StringValue(&strValue);
        _tprintf(_T("name : %s,  value: %s\n"), strName, strValue);
    }
}

void PrintElementsOfCollection(IAppHostChildElementCollection *pCollection)
{
    HRESULT hr = S_OK;

    IAppHostElement *pElement = NULL;

    DWORD elements_count = 0;
    hr = pCollection->get_Count(&elements_count);

    VARIANT vtIndex;
    vtIndex.vt = VT_INT;
    for(DWORD i=0; i<elements_count; ++i)
    {
        vtIndex.intVal = i;
        hr = pCollection->get_Item(vtIndex, &pElement);

        BSTR strName;
        hr = pElement->get_Name(&strName);
        _tprintf(_T("element : %s\n"), strName);
    }
}

void PrintElementsOfCollection(IAppHostElementCollection *pCollection)
{
    HRESULT hr = S_OK;

    IAppHostElement *pElement = NULL;

    DWORD elements_count = 0;
    hr = pCollection->get_Count(&elements_count);

    VARIANT vtIndex;
    vtIndex.vt = VT_INT;
    for(DWORD i=0; i<elements_count; ++i)
    {
        vtIndex.intVal = i;
        hr = pCollection->get_Item(vtIndex, &pElement);

        BSTR strName;
        hr = pElement->get_Name(&strName);
        _tprintf(_T("element : %s\n"), strName);

        //PrintPropertiesOfElement(pElement);
    }
} 

int _tmain(int argc, _TCHAR* argv[])
{
    HRESULT                               hr          = S_OK;

    IAppHostWritableAdminManager        * pWMgr       = NULL; 
    DWORD   dwElementCount       = 0;
    USHORT i;

    IAppHostElement *pAdminSection = NULL;
    IAppHostElement *ServerElement = NULL;
    IAppHostElementCollection *pAdminSectionCollection = NULL; 
    IAppHostChildElementCollection *pChildElements = NULL;
    IAppHostElement *pElement = NULL; 
    IAppHostPropertyCollection   * pElemProps  = NULL;
    IAppHostProperty             * pElemProp   = NULL;

 
    BSTR bstrConfigCommitPath = SysAllocString(L"MACHINE/WEBROOT/APPHOST"); 
    BSTR bstrSectionName = SysAllocString(L"system.applicationHost/sites");
    BSTR bstrPropertyName_ftpserver     = SysAllocString( L"ftpServer" ); 
    BSTR bstrPropertyName_security     = SysAllocString( L"security" ); 
    BSTR bstrPropertyName_authentication     = SysAllocString( L"authentication" ); 
    BSTR bstrPropertyName_anonymousAuthentication     = SysAllocString( L"anonymousAuthentication" ); 
    BSTR bstrPropertyName_enabled    = SysAllocString( L"enabled" );

    VARIANT vtPropertyName;
    VARIANT vtValue;
     
    // Initialize
    hr = CoInitializeEx( NULL, COINIT_MULTITHREADED );

    // Create WritableAdminManager object
    hr = CoCreateInstance( __uuidof( AppHostWritableAdminManager ), NULL, CLSCTX_INPROC_SERVER, __uuidof( IAppHostWritableAdminManager ), (void**) &pWMgr );
    
    //"MACHINE/WEBROOT/APPHOST/Default Web Site"
    pWMgr->put_CommitPath ( bstrConfigCommitPath );

    //Gets an IIS 7 configuration section that has the requested name and configuration path.
    hr = pWMgr->GetAdminSection(bstrSectionName, bstrConfigCommitPath, &pAdminSection);
    //Represents a collection of elements that belongs to a collection.
    hr = pAdminSection->get_Collection(&pAdminSectionCollection);

    //foreach the ElementCollection
    hr = pAdminSectionCollection->get_Count( &dwElementCount );
    for ( i = 0; i < dwElementCount; i++ )
    {
        //get the item from the collection
        VARIANT vtItemIndex;
        vtItemIndex.vt = VT_I2;
        vtItemIndex.iVal = i;
        hr = pAdminSectionCollection->get_Item( vtItemIndex, &pElement );

        //get the child elements from the item
        hr = pElement->get_ChildElements(&pChildElements);        
        //get the ftpServer item from the child elements
        vtPropertyName.vt            = VT_BSTR;
        vtPropertyName.bstrVal       = bstrPropertyName_ftpserver;
        hr = pChildElements->get_Item( vtPropertyName, &ServerElement );

        //get the child elements from the ftpServer element
        hr = ServerElement->get_ChildElements(&pChildElements);          
        vtPropertyName.vt            = VT_BSTR;
        vtPropertyName.bstrVal       = bstrPropertyName_security;
        hr = pChildElements->get_Item( vtPropertyName, &ServerElement ); 
        
        //get the child elements from the security element
        hr = ServerElement->get_ChildElements(&pChildElements); 
        vtPropertyName.vt            = VT_BSTR;
        vtPropertyName.bstrVal       = bstrPropertyName_authentication;
        hr = pChildElements->get_Item( vtPropertyName, &ServerElement ); 

        //get the child elements from the authentication element
        hr = ServerElement->get_ChildElements(&pChildElements); 
        vtPropertyName.vt            = VT_BSTR;
        vtPropertyName.bstrVal       = bstrPropertyName_anonymousAuthentication;
        hr = pChildElements->get_Item( vtPropertyName, &ServerElement ); 

        //get the propertied from the authentication element
        hr = ServerElement->get_Properties( &pElemProps );
        //get the item from authentication properties
        vtPropertyName.vt            = VT_BSTR;
        vtPropertyName.bstrVal       = bstrPropertyName_enabled;        
        hr = pElemProps->get_Item( vtPropertyName, &pElemProp );
        //set value from the item
        vtValue.vt                   = VT_BOOL;
        vtValue.boolVal              = VARIANT_FALSE;
        hr = pElemProp->put_Value( vtValue );

        printf("anonymous switch: %s\n", vtValue.boolVal ? "enableed" : "disabled");
        //PrintElementsOfCollection(ServerElement);
    }
 

    // Commit the changes to the configuration system
    pWMgr->CommitChanges ( );
    return 0;
}

代码工作的很好,实现了禁用IIS7/7.5 FTP匿名登录的效果

Relevant Link:

http://stackoverflow.com/questions/20559426/iis-client-certificate-mapping-authentication
http://www.iis.net/learn/manage/scripting/how-to-use-microsoftwebadministration
http://www.codeproject.com/Articles/440548/IIS-security-settings-and-different-permission-usi 

 

10. IIS WEBDAV匿名访问的自动化修复

需要明白的是,WEBDAV是一种扩展的HTTP协议,WEBDAV在IIS中本质上也是一种网站的形式存在的,而WEBDAV所共享出的文件夹就是这个网站的根目录

WEBDAV不像FTP那样,是一个独立的服务程序,WEBDAV仅仅是一个扩展协议,允许客户端通过指定的格式进行访问,所以WEBDAV没有匿名登录这一说法,但是可以采用一定的手段对WEBDAV进行加固,大致有如下几个方向

1. 直接禁用IIS WEBDAV扩展开关
2. 单独针对每个WEBDAV站点进行目录读写、访问权限的细化设置

下面我们以禁用IIS WEBDAV扩展开关的为例展示修复代码

0x1: 通过API方式编程实现IIS 6 WEBDAV禁用

依然使用ADSI技术进行编程实现

// fixftp6.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"

#include <atlstr.h>
#include <initguid.h>
#include <objbase.h>
#include <iads.h>
#include <adshlp.h>
#include <atlbase.h>
#include <iiisext.h>
#include <iisext_i.c>
#include <comdef.h> 

#include <lm.h> 

#include <string>
#include <sstream>
#include <vector>
#include <iostream>

using namespace std;

#pragma comment(lib, "Activeds.lib")
#pragma comment(lib, "Adsiid.lib")
#pragma comment(lib, "netapi32.lib")

wstring logFilename;
 
// 禁用WEBDAV
HRESULT SetIISWebDAVState(bool disable) 
{
    HRESULT hr;
    IADs *pADs = NULL;
    IADsContainer* iContainer = NULL;
    IUnknown *pUnk = NULL;
    IDispatch *pDisp = NULL; 

    hr = ADsGetObject( L"IIS://localhost/w3svc", IID_IADsContainer, (void **)&iContainer );
    if (FAILED(hr)) 
    {
        wprintf(L"ADsGetObject failed %x\n", hr);
        return hr;
    }

    //This class corresponds to the IIsWebService IIS Admin object, and contains the methods and read-only properties for the object.
    IISWebService* webservice = NULL;
    hr = iContainer->QueryInterface(IID_IISWebService, (void**)&webservice);
    if (FAILED(hr)) 
    {
        wprintf(L"QueryInterface failed\n");
        return hr;
    } 

    if (!disable) 
    {
        hr = webservice->DisableWebServiceExtension(L"WEBDAV");
        if (FAILED(hr)) 
        {
            wprintf(L"Disable WebDAV failed\n");
        } 
        else 
        {
            hr = webservice->DisableExtensionFile(L"*.dll");
            if (!FAILED(hr))
            {
                wprintf(L"WebDAV disabled\n");
            }
        }
    } 
    else 
    {
        hr = webservice->EnableWebServiceExtension(L"WEBDAV");
        if (FAILED(hr)) 
        {
            wprintf(L"EnableWebServiceExtension failed\n");
        } 
        else 
        {
            wprintf(L"WebDAV enabled\n");
        }
    }
    return hr;
}

int _tmain(int argc, _TCHAR* argv[])
{
    HRESULT hr;
    //ADSI 基于 com如果忽略了调用 CoInitialize 或 OleInitialize,AdsGetObject,它将返回 HRESULT 的 MK_E_SYNTAX (0x800401e4,"无效语法"),
    hr = CoInitializeEx( NULL, COINIT_MULTITHREADED );
    if ( FAILED( hr ) )
    {
        wprintf( L"CoInitialize failed. Error 0x%0x\n", hr );
        return hr;
    }

    SetIISWebDAVState(false);

    return 0;
}

0x2: 通过API方式编程实现IIS 7.5 WEBDAV禁用

windows server 2003默认安装IIS 6,windows server 2008默认安装的IIS 7.5下,而在IIS 7.5环境下,WEBDAV是以一个"feature(功能)"的形式存在,所以在配置文件中就没有<webdav>这个元素项,通过IAppHostWritableAdminManager对IIS进行配置的这个方法无法使用,需要继续研究别的方法

undone

http://msdn.microsoft.com/en-us/library/windows/desktop/dd408159(v=vs.85).aspx

http://www.iis.net/learn/install/installing-publishing-technologies/installing-and-configuring-webdav-on-iis

http://www.iis.net/configreference/system.webserver/webdav


11. IIS 恶意Filter/Extension的自动化修复

undone

Copyright (c) 2014 LittleHann All rights reserved

 

posted @ 2014-10-11 19:46  郑瀚Andrew  阅读(3026)  评论(4编辑  收藏  举报