注入漏洞:
- 所用语言:ASP 语言
- 注入类型:堆叠注入,报错注入,联合查询
相信各位应该都看到过这样的下载方式:
https://hackdoor.org/xxx/FileDownLoad.asp?file=326085
当我们点击了下载后,就弹出来了上面的链接地址,导致我们无法获取到绝对路径,获取不到绝对路径就没法运行我们的脚本文件,最终导致getshell失败。
那么我们下面就来讲讲关于这种动态下载的利用方案中的一个,任意文件读取漏洞。
一. 动态下载的原理是什么?
1. PHP Demo
$FileId = $_GET['id'];
$db->bind('FileId', $FileId);
$Query_File_Path = $db->query('SELECT * FROM cycc_FileList WHERE FileId = :FileId');
if (!$Query_File_Path) {
echo '档案文件不存在!';
die;
}
$FilePATH = end($Query_File_Path)['FilePath'];
$FileName = end($Query_File_Path)['Filename'];
header('Pragma: public'); //1 Public指示响应可被任何缓存区缓存。
header('Expires: 0'); //2 浏览器不会响应缓存
header('Cache-Control:must-revalidate, post-check=0, pre-check=0'); //3
header('Content-Type:application/force-download'); //4 请求该页面就出现下载保存窗口。
header('Content-Type:application/octet-stream'); //5 二进制流,不知道下载文件类型。
header('Content-Type:application/vnd.ms-excel;'); //6
header('Content-Type:application/download'); //7 提示用户将当前文件保存到本地。
header("Content-Disposition:attachment;filename= $FileName"); //8
header('Content-Transfer-Encoding:binary'); //9
readfile($FilePATH);
1. 接收外界的FileID(FileId 一般是随机,或者加密,有序或者无序的字母+数字)
2. 查询FileId 在数据表:cycc_FileList 对应的数据信息。
3. 获取数据的存储路径【FilePath】赋予给 $FilePATH 变量
4. 获取数据中的【Filename】赋予给 $FileName 变量
那么接下来的操作就是正题来了。
5. 设置响应header头,告诉浏览器当前是在下载文件,这个时候浏览器就会弹出叫你保存文件的对话框。
6. 调用 readfile
函数读取 【$FilePath】中的路径然后输出内容到数据流。
浏览器就会将得到的响应包内容存储到你保存的指定文件名中。
大概原理就是如此,但是每个程序员开发这个的方式不同,所以并非都是如此,比如很多程序员不会将文件路径存储到数据库中,而是在readfile写死一个路径,只需要拼接你传入的文件名即可。例如下方案例:
$FilePath = '/upload/temp/jpg/'.$FileName;
readfile($FilePath);
这就是很典型的案例之一。下面我将要介绍的案例大概就如此。
2. ASP Demo
on error resume next
' 接受外部传入的FileID参数值
sFile = replace(request.QueryString("file"),"'","")
Set conn = Server.CreateObject("ADODB.connection")
conn.Open Application("DSN")
if session("FileUP")="" then
Set rsSet = server.createobject("ADODB.Recordset")
SQL = " select setvalue from Doc_Set where Number=33"
rsSet.open SQL, conn, 1,3
if not rsSet.eof then
session("FileUP") = rsSet("setvalue") & ":\FileUP\"
end if
rsSet.close
set rsSet = Nothing
end if
Set rsATT = server.createobject("ADODB.Recordset")
SQL = " Select * From Doc_Folder where no=" & sFile
rsATT.open SQL, conn, 1, 3
if not rsATT.eof then
sFile = trim(rsATT("attNo"))
end if
rsATT.close
set rsATT = nothing
set conn = nothing
if sFile<>"" then
if err.number<>0 then
response.write sFile & "<BR>" & err.number & ":" & err.Description & "<BR>"
end if
sYear=left(sFile,3)
sfile = Session("FileUP") & sYear & "\" & sFile
Set fso = Server.CreateObject("Scripting.FileSystemObject")
If (fso.FileExists(sfile))=false Then
response.write "<script>alert('檔案不存在!');</script>"
response.write "<script>history.back();</script>"
else
Set objFile = fso.GetFile(sfile)
Set objStream = Server.CreateObject("ADODB.Stream")
objStream.Open
objStream.Type = 1
objStream.LoadFromFile sfile
'下載檔案
Response.Clear
BrowseMsg = replace(Request.ServerVariables("HTTP_USER_AGENT"),"'","")
MSIE = false
MSIE8 = false
if inStr(BrowseMsg,"MSIE")>0 then
MSIE = true
end if
if inStr(request.servervariables("HTTP_USER_AGENT"), "Trident/7.0; rv:11.0")>0 then
MSIE = true
end if
if Application("GovSName")="xxxx" or Application("GovSName")="xxx" then
if inStr(request.servervariables("HTTP_USER_AGENT"), "Trident/4.0;")>0 then
MSIE = false
MSIE8 = true
end if
end if
if Application("CharSet")="utf-8" and MSIE then
Response.AddHeader "Content-Disposition","attachment; filename=" & Server.urlencode(objFile.Name)
elseif Application("CharSet")="utf-8" and MSIE8 then
Response.AddHeader "Content-Disposition","attachment; filename=" & Server.urlencode(replace(objFile.Name,"."& fso.GetExtensionName(sfile),"")) &"."& fso.GetExtensionName(sfile)
else
Response.AddHeader "Content-Disposition","attachment; filename=" & objFile.Name
end if
Response.AddHeader "Expires:",0
Response.ContentType="application/save-as"
Response.ContentType="application/octet-stream"
Response.CharSet = "UTF8"
Response.BinaryWrite objStream.Read
Response.End
objStream.Close
Set objStream = Nothing
set objFile=Nothing
end if
set fso=Nothing
end If
我们来仔细品味该段代码。
1. 接收外部传入的 file
参数,并将其赋予给 sFile
变量
sFile = replace(request.QueryString("file"),"'","")
2. 链接数据库查询数据表:Doc_set 中 Number Id为 33 的值 ( 这个条件不会被触发也就是代码不会被运行。)
在我们的session中存在这个FileUP参数,并且不为空,我从前面的代码中看到了他被赋值。
Set conn = Server.CreateObject("ADODB.connection")
conn.Open Application("DSN")
if session("FileUP")="" then
Set rsSet = server.createobject("ADODB.Recordset")
SQL = " select setvalue from Doc_Set where Number=33"
rsSet.open SQL, conn, 1,3
if not rsSet.eof then
session("FileUP") = rsSet("setvalue") & ":\FileUP\"
end if
rsSet.close
set rsSet = Nothing
end if
根据他的代码意思就是从数据表:Doc_set 取出所有数据,并进行while循环,然后将数据库中的所有参数+值赋予到我们的Session 中。
那么也就是说我们 Session
中的 FileUP
参数值是在数据库中。于是我找到了一个堆叠注入,从数据库中查询到了我们的内容。
通过上图我们发现 33 的 SetValue 参数值为 D ,那么这个 D 代表的是什么意思呢?
这个D 我们通过前面的代码就能知道:
session("FileUP") = rsSet("setvalue") & ":\FileUP\" # 最终路径 D:\FileUP
原来他是拿的盘符来拼接路径;也就是我们所谓的写死路径。因为最后他再把这个存储路径加上这个查询出来的文件名就能读取到文件。
我们继续往下看
3. 读取 Doc_Folder 表中 FileId 对应的文件名称。其中 attNo
参数所对应的值就是我们所谓文件名了。
Set rsATT = server.createobject("ADODB.Recordset")
SQL = " Select * From Doc_Folder where no=" & sFile # 这个sFile 就是我们前面的FileId = 326085
rsATT.open SQL, conn, 1, 3
if not rsATT.eof then
sFile = trim(rsATT("attNo"))
end if
rsATT.close
set rsATT = nothing
set conn = nothing
这个时候我们需要记住的就是 sFile 从原先的 326085 变更为了 1092100624 。
sFile = '1092100624'
4. 获取sFile前三位的年份号,最终组合路径
sYear=left(sFile,3) # 取值为 109
sfile = Session("FileUP") & sYear & "\" & sFile # 组合得到最终路径:D:\FileUP\109\1092100624
我们这个时候要记住,sFile 被再次赋值为 :sFile = ‘D:\FileUP\109\1092100624’
5. 判断文件是否存在,然后如果存在则读取文件内容输出文件流到页面中。
Set fso = Server.CreateObject("Scripting.FileSystemObject")
If (fso.FileExists(sfile))=false Then
response.write "<script>alert('檔案不存在!');</script>"
response.write "<script>history.back();</script>"
else
Set objFile = fso.GetFile(sfile) # 读取文件内容
Set objStream = Server.CreateObject("ADODB.Stream")
objStream.Open
objStream.Type = 1
objStream.LoadFromFile sfile
'下載檔案
Response.Clear
最终整个过程完毕,那么我们应该如何才能实现任意文件读取呢?
二. 论如何任意文件读取?
1. 修改 Doc_set 表中的 Number 33 值为 C
2. 修改 Doc_Folder 表中 no 326085 的 attNo 值为 ../../../../../inetpub/wwwroot/DocWWW/index.asp
解疑:为什么 Doc_set 表中的 Number 33 值为 C
其中的原因很简单,我们的网站在C盘,存储的目录在D盘,任意文件读取漏洞在Windows中的最重要的一条规则就是不能跨盘符读取文件。
那么我们为了解决这个问题就得先把盘修改成去 C 盘读取。
解疑:为什么修改 Doc_Folder 表中 no 326085 的 attNo 值为 ../../../../../inetpub/wwwroot/DocWWW/index.asp
当我们修改了盘符后,路径就变为了:C:\FileUP\109../../../../../inetpub/wwwroot/DocWWW/index.asp,这个时候我们就会通过 ../../../ 进入到 C盘根目录,然后再进入到我们要去的网站目录,接着就能读取文件并且输出他的值了。
没有回复内容