记一次非DBA注入+动态下载实现任意文件读取-思路分享社区-Web安全-赤道学院

记一次非DBA注入+动态下载实现任意文件读取

注入漏洞:

  1. 所用语言:ASP 语言
  2. 注入类型:堆叠注入,报错注入,联合查询

相信各位应该都看到过这样的下载方式:

https://hackdoor.org/xxx/FileDownLoad.asp?file=326085

Image description

当我们点击了下载后,就弹出来了上面的链接地址,导致我们无法获取到绝对路径,获取不到绝对路径就没法运行我们的脚本文件,最终导致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 对应的数据信息。

Image description

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 的值 ( 这个条件不会被触发也就是代码不会被运行。)

Image description

在我们的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 中。

Image description

那么也就是说我们 Session 中的 FileUP 参数值是在数据库中。于是我找到了一个堆叠注入,从数据库中查询到了我们的内容。

Image description

通过上图我们发现 33 的 SetValue 参数值为 D ,那么这个 D 代表的是什么意思呢?

这个D 我们通过前面的代码就能知道:

Image description

session("FileUP") = rsSet("setvalue") & ":\FileUP\"  # 最终路径 D:\FileUP
 

原来他是拿的盘符来拼接路径;也就是我们所谓的写死路径。因为最后他再把这个存储路径加上这个查询出来的文件名就能读取到文件。

我们继续往下看

3. 读取 Doc_Folder 表中 FileId 对应的文件名称。其中 attNo 参数所对应的值就是我们所谓文件名了。

Image description

Image description

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中的最重要的一条规则就是不能跨盘符读取文件。

Image description

那么我们为了解决这个问题就得先把盘修改成去 C 盘读取。

解疑:为什么修改 Doc_Folder 表中 no 326085 的 attNo 值为 ../../../../../inetpub/wwwroot/DocWWW/index.asp

当我们修改了盘符后,路径就变为了:C:\FileUP\109../../../../../inetpub/wwwroot/DocWWW/index.asp,这个时候我们就会通过 ../../../ 进入到 C盘根目录,然后再进入到我们要去的网站目录,接着就能读取文件并且输出他的值了。

Image description

请登录后发表评论

    没有回复内容