题目简述

先来看题,很经典的一个文件上传:

Debug

看起来没什么问题,那就先debug一下,先上传一个正常的文件,发现返回了地址,点击查看观察地址组成,发现这个地址被images.php处理过,不能直接利用:

尝试访问一个不存在的地址,发现返回错误:

发现有函数file_get_contents(),搜索发现,此函数可能会导致文件包含或者目录穿越,尝试发现有目录穿越漏洞,以下是关于目录穿越的知识:

什么是目录遍历?

目录遍历(目录穿越)是一个Web安全漏洞,攻击者可以利用该漏洞读取运行应用程序的服务器上的任意文件。 这可能包括应用程序代码和数据,后端系统的登录信息以及敏感的操作系统文件。

在某些情况下,攻击者可能能够在服务器上写入任意文件,(如,FFFTP是一款小型的FTP客户端软件。FFFTP客户端没有正确地验证FTP服务器所返回的LIST命令响应中包含有目录遍历序列(斜线)的文件名,如果用户受骗从恶意的FTP服务器下载的目录包含有带有恶意文件名的文件的话,就可能导致向用户系统的任意位置写入文件)从而允许他们修改应用程序数据或行为,并最终完全控制服务器。

漏洞原理

若web要显示一个商品的图像,有时候开发者会用通过HTML加载,如:

使用filename参数加载图像文件,图片文件位置可能映射在 /var/www/images/ 上,所以真实的路径是 /var/www/images/214.png

这就导致了攻击者可以读取服务器上的任意文件:

https://www.*****.com/loadImage?filename=../../../etc/passwd

filename的参数值与真实路径组合起来就是:

/var/www/images/../../../etc/passwd

其等价于:

/etc/passwd

在Unix操作系统上,../ 是一个标准的返回上一级路径的语法;

在Windows操作系统上, ../ 和 ..\ 都是返回上一级的语句。

实验
0x00 基础目录遍历

查看商品的图片,可以看到是通过filename参数来确定图片的:

右键打开图片,同时抓包,可以修改filename参数值,利用 ../ 返回上一级来遍历任意文件:

filename=../../../etc/passwd

0x01 绝对路径

web网站有时候会采取目录遍历的防御措施,如过滤 ../ 上一级等关键字,然后简单的过滤通常会被绕过。有时候可以直接采用绝对路径,无须../返回上一级目录遍历:

filename=/etc/passwd

0x02 双写../绕过

有时候,防御措施是直接将 ../ 替换为空,可以直接采用双写/复写直接绕过:

filename=….//….//….///etc/passwd

如果在….//中,将../替换为空,最后的路径就变成:

....//  => ../

0x03 URL编码绕过

也可以采用URL编码来绕过服务器对 . 或者 / 的检测:

.   =>  %2e

/   =>  %2f

% => %25  (双重URL编码)

先尝试编码 / ,失败:

全编码,失败:

双重编码,即将%编码,成功:

0x04 绝对路径配合../

有些web在获取filename图片文件的时候,会首先判断是否以一个固定的路径开头:

那么就可以配合../来返回上一级遍历任意文件:

filename=/var/www/images/../../../etc/passwd

0x05 截断文件后缀

某些web对filename的文件类型作了限制,只有当后缀为图片时才解析

这时候就可以利用 %00 来截断:

总之,目录遍历漏洞不会仅限于一个绕过姿势,通常会配合多个姿势进行组合攻击。

防御

防止文件路径遍历漏洞的最有效方法是避免将用户提供的输入完全传递给文件系统API。

如果认为不可避免的是将用户提供的输入传递给文件系统API,则应同时使用两层防御,以防止受到攻击:

    应用程序应在处理之前验证用户输入。理想情况下,验证应与允许值的白名单进行比较。如果所需的功能无法做到这一点,则验证应验证输入内容仅包含允许的内容,例如纯字母数字字符。
    验证提供的输入后,应用程序应将输入附加到基本目录,并使用平台文件系统API规范化路径。验证规范化路径以预期的基本目录开头。

以下是一些简单的Java代码示例,用于根据用户输入来验证文件的规范路径:

File file = new File(BASE_DIRECTORY, userInput);
if (file.getCanonicalPath().startsWith(BASE_DIRECTORY)) {
    // process file
}

初步尝试

抓包,并在burpsuit的repeater里构造重发数据包来目录穿越,发现返回了一个php文件:

访问这个文件发现需要审计代码:

<?php

highlight_file(__FILE__);

if (isset($_GET['param1']) && isset($_GET['param2'])) {
    $param1 = $_GET['param1'];
    $param2 = $_GET['param2'];

    if ($param1 !== $param2) {
        
        $md5Param1 = md5($param1);
        $md5Param2 = md5($param2);

        if ($md5Param1 == $md5Param2) {
            echo "O.O!! " . getenv("FLAG");
        } else {
            echo "O.o??";
        }
    } else {
        echo "o.O?";
    }
} else {
    echo "O.o?";
} 

审计发现,需要传param1和param2两个值,这两个值需要不一样,但是他们的md5要一样

获得flag

上网搜索发现这样的字符,构造url访问,得到flag:

踩坑总结

1.当时做的时候尝试访问各种url,尝试泄露,即使知道file_get_content()也不会使用,看了其他人的wp后知道目录穿越就会做了
2.附上一些字符不同但md5相同的字符做备用:

附上一个5位数和一个6位数的0e开头md5全数字的值:

byGcY
0e591948146966052067035298880982
sonZ7y
0e463306343746311593316642162425

只包含一种字符的0e开头的md5值:

1FVWc
0ef715f5943144181839f9f321584156
2uM8z
0e0081018231f666742748f3540f4761
3za1b
0e02f196724391824188f3f90f732735

只包含两种字符的0e开头的md5值:

00c84
0e17d931d17f1948d37d691693995ff1
01bDI
0ee26113820055502806791698800f47
01NxP
0e72096d884f956d9618012679405269
02K7w
0e543091322e0f42ef1126374409f755
04072
0e8f06d06387944ff396030861fd2698
044Dt
0e91946379408e6f2f62152293e4f596
05nnu
0ef580f7a2f4176f57544f8091f33936
05wPJ
0e49528768d18f101816843669079475

⬆︎TOP