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

目錄php

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. 引言html

0x0: 本文主要研究的技術點node

由於本文篇幅較長,這裏對全文的調研技術點作一個梳理總結ios

1. 基於IIS FTP、IIS WEBDAV的黑客入侵、GETSHELL
2. IIS FTP配置加固的原理
3. IIS的加強擴展(Filter、Extension)的原理及利用方式
4. 如何經過API方式對IIS 6、IIS 7.5進行配置修復,從而達到批量集羣自動漏洞修復的目的

0x1: 要經過FTP進行GETSHELL須要知足的條件nginx

基於FTP對目標主機進行GETSHELL本質上是一個寫磁盤的動做,要達到這個目的,須要知足幾個先決條件git

1. 目標服務器開啓了FTP匿名訪問
2. 目標服務器開啓了FTP寫權限(IIS FTP軟件層的邏輯控制)
3. 目標服務器的登陸賬號(以及所在組)對指定的目錄有寫的權限(ACL)
4. 目標服務器的"FTP根目錄"同時也是"WEB服務器的WEB目錄"

對於第3點,須要特別注意的是,在默認狀況下,磁盤文件的ACL設置中,是沒有IUSR_computername的,而在windows中,若是沒有明確禁止,則隱含的條件是默認容許,從這點也能夠看出,若是咱們想要明確的禁止這個FTP目錄遭到黑客的寫入,應該明確地添加IUSR_computername,並禁止寫權限程序員

當這4點同時知足時,黑客就能夠經過FTP的漏洞達到直接GETSHELL的目的web

Relevant Link:shell

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

0x2: 如何進行FTP匿名登陸 apache

當目標主機開啓了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

相關文章
相關標籤/搜索