目錄
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
