深入解析ASP.NET加密漏洞与检测脚本

本文详细分析了ASP.NET加密实现中的填充预言机漏洞,解释了攻击者如何利用该漏洞解密和篡改数据,并提供了检测脚本和缓解措施,帮助管理员识别和修复不安全配置。

理解ASP.NET漏洞 | MSRC博客

安全研究与防御团队
作者:swiat | 2010年9月17日 | 7分钟阅读

我们最近的公告描述了一个近期公开披露的ASP.NET漏洞。这篇博客文章将为您提供有关该漏洞和缓解措施的更多信息,同时提供一个脚本,帮助您检测服务器上处于易受攻击配置的ASP.NET应用程序。

漏洞的影响

ASP.NET使用加密来隐藏敏感数据并保护其免受客户端篡改。然而,ASP.NET加密实现中的一个漏洞可能允许攻击者解密和篡改这些数据。

但攻击者能利用这种能力做什么?部分答案取决于被攻击的ASP.NET应用程序。例如,如果ASP.NET应用程序在ViewState对象中存储敏感信息(如密码或数据库连接字符串),这些数据可能会被泄露。ViewState对象经过加密并以隐藏表单变量的形式发送到客户端,因此它是此攻击的可能目标。

如果ASP.NET应用程序使用ASP.NET 3.5 SP1或更高版本,攻击者可以利用此加密漏洞请求ASP.NET应用程序内任意文件的内容。公开披露展示了使用此技术检索web.config的内容。攻击者将获得ASP.NET应用程序中工作进程有权访问的任何文件。

漏洞的工作原理

要理解此漏洞的工作原理,您需要了解加密预言机。在密码学上下文中,预言机是一个在您提问时提供提示的系统。在这种情况下,ASP.NET中存在一个充当填充预言机的漏洞。这允许攻击者将选择的密文发送到服务器,并通过检查服务器返回的错误代码来了解其是否被正确解密。

通过多次请求,攻击者可以学到足够的信息来成功解密其余密文。攻击者还可以更改明文并重新加密。

缓解措施——沉默预言机

此漏洞的缓解措施是使用ASP.NET的customErrors功能配置应用程序,使其无论服务器上遇到何种错误都返回相同的错误页面。

通过按照公告中的步骤将所有错误消息映射到单个错误页面,您使攻击者难以区分不同类型的错误,从而有效限制对预言机的访问。

如何检测易受攻击的ASP.NET应用程序

一些ASP.NET应用程序可能已配置为所有服务器错误返回相同的错误页面。要检测未以此方式配置且需要应用缓解措施的ASP.NET应用程序,请使用以下脚本:

注意:安装MS10-070安全更新后,不再需要此缓解措施,也无需运行此脚本。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'  DetectCustomErrorsDisabled.vbs Script
'  版本3.1
'
'  此脚本将帮助检测填充预言机ASP.NET漏洞的易受攻击配置,该漏洞在MS公告2416728中记录。
'
'  http://www.microsoft.com/technet/security/advisory/2416728.mspx
'
'  用法:
'      cscript DetectCustomErrorsDisabled.vbs [RemoteServerName]
'
'  注意:此脚本使用文件系统和Shell对象,应以管理员身份运行
'
'  脚本通过枚举所有web.config并评估是否通过使用统一的ASP.NET应用程序自定义错误响应来缓解填充预言机漏洞的侧信道泄漏。
'
'  注意:在IIS 7服务器上,此脚本需要安装IIS6兼容模式。
'
'  更多信息:http://blogs.technet.com/b/srd/archive/2010/09/17/understanding-the-asp-net-vulnerability.aspx
'
'  版本历史:
'  1.0 - 初始版本
'  2.0 - 添加了对应用程序/站点根配置的额外检查
'  3.0 - 添加了XML解析和路径检查的错误验证
'  3.1 - 添加了对缺失根web.config的检查
'
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
OPTION EXPLICIT
ON ERROR RESUME NEXT
DIM strServer
DIM objWebService, objWebServer, objDir, objFileSys
DIM physicalPath, dir, xmlDoc, nodeList, node, ret
DIM configFile, configFilePath, configLine
DIM childNodes, ErrPage500, ErrPage404, errFound
DIM index, errCount

strServer = "localhost"

' 解析命令行输入
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
IF WScript.Arguments.Length=1 THEN
    strServer = WScript.Arguments( 0 )
END IF

IF WScript.Arguments.Length>1 THEN
    WScript.Echo "非法参数数量"
    WScript.Echo "用法:cscript.exe DetectCustomErrorsDisabled.vbs [RemoteServerName]"
    WScript.Quit( 1 )
END IF

' 初始化
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
SET objFileSys = CreateObject("Scripting.FileSystemObject")
SET objWebService = GetObject( "IIS://" & strServer & "/W3SVC" )
IF Err <> 0 THEN
    WScript.Echo "找不到IIS ADSI对象。请确保已安装IIS和IIS6管理兼容性。"
    WScript.Quit (1)
END IF
SET xmlDoc = CreateObject("Microsoft.XMLDOM")

IF IsNull(objFileSys) THEN
    WScript.Echo "创建FileSystemObject失败。请以管理员身份运行脚本。"
    WScript.Quit (1)
END IF

IF IsNull(objWebService) THEN
    WScript.Echo "连接到IIS ADSI提供程序失败。请确保已安装IIS6管理兼容性角色服务。"
    WScript.Quit (1)
END IF

WScript.Echo("枚举可能具有自定义错误关闭的ASP.NET配置路径。")
WScript.Echo ("")

' 搜索Web服务器以查找不安全的配置
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
FindASPNetConfig(objWebService)

' 搜索Web服务器上所有可能的web.config文件路径
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
SUB FindASPNetConfig(WebService)
    FOR EACH objWebServer IN WebService
        IF objWebserver.Class = "IIsWebServer" THEN
            EnumDirectories(objWebServer)
        END IF
    NEXT
END SUB

' 递归遍历虚拟目录和Web目录
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
SUB EnumDirectories(objDir)
    DIM objSubDir
    ' 第一次调用来自IIsWebServer,因此我们可以跳过
    FOR EACH objSubDir IN objDir
        IF (objSubDir.Class = "IIsWebVirtualDir") THEN
            GetPhysicalPaths(objSubDir)
            EnumDirectories(objSubDir)
        END IF
    NEXT
END SUB

' 获取Web和虚拟目录的物理路径
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
SUB GetPhysicalPaths(objDir)
    physicalPath = objDir.Path
    CALL EnumWebConfig(physicalPath,1)
END SUB

' 递归搜索web.config文件
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
SUB EnumWebConfig(Path,IsRoot)
    IF NOT objFileSys.FolderExists(Path) THEN
        IF IsRoot THEN
            WScript.Echo Path & ": ** 发现易受攻击的配置 **"
        END IF
        EXIT SUB
    END IF

    configFilePath = Path & "\web.config"
    IF objFileSys.FileExists(configFilePath) THEN
        CALL ProcessWebConfig(configFilePath,IsRoot)
    ELSEIF IsRoot = 1 THEN
        WScript.Echo Path & ": ** 发现易受攻击的配置 **"
    END IF

    FOR EACH dir IN objFileSys.GetFolder(Path).SubFolders
        CALL EnumWebConfig(dir.Path,0)
    NEXT
END SUB

' 跳过默认具有写入访问权限的已知身份
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
SUB ProcessWebConfig(Path,IsRoot)
    xmlDoc.async="false"
    xmlDoc.load(Path)
    errFound = 0
    SET nodeList = xmlDoc.getElementsByTagName("customErrors")

    IF IsRoot = 1 AND nodeList.length = 0 THEN
        ' 根web.config未设置defaultRedirect,因此此配置应具有启用了customErrors并存在defaultRedirect的customErrors部分。否则这是易受攻击的配置。
        errFound = errFound + 1
    ELSEIF IsRoot = 1 THEN
        ret = CheckRootCustomErrorsSection(nodeList, Path)
        errFound = errFound + ret
    END IF

    DIM count
    FOR count=0 TO nodeList.length-1
        ret = CheckCustomErrorsDisabled(nodeList.Item(count), Path)
        errFound = errFound + ret
        ret = CheckCustomErrorsAreHomogenous(nodeList.Item(count), Path)
        errFound = errFound + ret
    NEXT

    IF errFound > 0 THEN
        WScript.Echo Path & ": ** 发现易受攻击的配置 **"
    ELSE
        WScript.Echo Path & ": 正常"
    END IF
END SUB

FUNCTION CheckRootCustomErrorsSection(xmlnodelist, path)
    errCount = 0
    FOR index=0 TO xmlnodeList.length-1
        ret = CheckRootCustomErrorsDisabled(nodeList.Item(index), Path)
        errCount = errCount + ret
    NEXT
    CheckRootCustomErrorsSection = errCount
END FUNCTION

FUNCTION CheckRootCustomErrorsDisabled(xmlnode, path)
    IF StrComp (LCase(xmlnode.getAttribute("mode")), "off") = 0 THEN
        CheckRootCustomErrorsDisabled = 1
        EXIT FUNCTION
    ELSEIF IsNull(xmlnode.getAttribute("defaultRedirect")) THEN
        CheckRootCustomErrorsDisabled = 1
        EXIT FUNCTION
    ELSE
        CheckRootCustomErrorsDisabled = 0
    END IF
END FUNCTION

FUNCTION CheckCustomErrorsDisabled(xmlnode, path)
    IF StrComp (LCase(xmlnode.getAttribute("mode")), "off") = 0 THEN
        ' 不安全配置
        CheckCustomErrorsDisabled = 1
    ELSE
        CheckCustomErrorsDisabled = 0
    END IF
END FUNCTION

FUNCTION CheckCustomErrorsAreHomogenous(xmlnode, path)
    IF xmlnode.childNodes.length=0 AND len(xmlNode.getAttribute("defaultRedirect"))>0 THEN
        CheckCustomErrorsAreHomogenous = 0
        EXIT FUNCTION
    END IF

    SET childNodes = xmlnode.childNodes
    ErrPage404 = ""
    ErrPage500 = ""

    DIM count
    FOR count=0 TO childNodes.length-1
        CALL GetErrorPage(childNodes.Item(count))
    NEXT

    IF StrComp(ErrPage404,"") = 0 AND StrComp(ErrPage500,"") = 0 AND IsNull(xmlNode.getAttribute("defaultRedirect")) THEN
        ' 在这种情况下缺少defaultRedirect将导致配置易受攻击
        CheckCustomErrorsAreHomogenous = 1
        EXIT FUNCTION
    ELSEIF StrComp(ErrPage404,"") = 0 AND StrComp(ErrPage500,"") <> 0 AND StrComp(ErrPage500, xmlNode.getAttribute("defaultRedirect")) <> 0 THEN
        CheckCustomErrorsAreHomogenous = 1
        EXIT FUNCTION
    ELSEIF StrComp(ErrPage500,"") = 0 AND StrComp(ErrPage404,"") <> 0 AND StrComp(ErrPage404, xmlNode.getAttribute("defaultRedirect")) <> 0 THEN
        CheckCustomErrorsAreHomogenous = 1
        EXIT FUNCTION
    ELSEIF StrComp(ErrPage404, ErrPage500) <> 0 THEN
        CheckCustomErrorsAreHomogenous = 1
        EXIT FUNCTION
    ELSE
        CheckCustomErrorsAreHomogenous = 0
    END IF
END FUNCTION

SUB GetErrorPage(xmlnode)
    IF xmlnode.nodeType <> 1 THEN
        EXIT SUB
    ELSEIF IsNull(xmlnode.getAttribute("statusCode")) THEN
        ' 什么都不做
    ELSEIF StrComp(xmlnode.getAttribute("statusCode"), "500") = 0 THEN
        ErrPage500 = xmlnode.getAttribute("redirect")
    ELSEIF StrComp(xmlnode.getAttribute("statusCode"), "404") = 0 THEN
        ErrPage404 = xmlnode.getAttribute("redirect")
    END IF
END SUB

致谢

非常感谢Levi Broderick、Nazim Lala和Stefan Schackow对本文的贡献。

-Kevin Brown, MSRC工程团队

2010年9月18日更新
对包含的脚本进行了多项改进,以捕获其他边界情况并提供额外的错误处理。澄清了攻击者只能从ASP.NET应用程序中检索文件。

2010年9月21日更新
更新了脚本以添加对缺失根web.config文件的检查,并将脚本的zip文件附加到帖子中以便直接下载。

2010年9月29日更新
添加了说明,澄清安装安全更新后不再需要缓解措施和检测脚本。安全更新现已可用 - 请参阅MS10-070获取更多信息。

DetectCustomErrorsDisabled.zip

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计