Boîte à outils de détection et de confirmation de RCE qui teste des URL ou des requêtes HTTP capturées pour l'injection de commandes, le SSTI, les chemins aveugles et OOB, en renvoyant des verdicts hiérarchisés avec preuve.
confirmed signifie que la cible a exécuté l'entrée. negative signifie que les sondes l'ont atteinte.
Version 2.40.0 · MIT · Python 3.8+ · aucune dépendance tierce
RCEKit est une boîte à outils de détection et de confirmation de RCE pour les tests d'intrusion autorisés, le red teaming et la recherche en sécurité. Pointez-le vers une cible que vous êtes autorisé à tester — une URL ou une requête HTTP capturée — et chaque résultat revient avec le niveau qu'il a mérité.
Chaque confirmed repose sur une valeur que RCEKit a générée aléatoirement pour cette sonde et
que cette réflexion ne peut pas produire : un résultat calculé présent dans la réponse et
absent d'un contrôle sans payload, ou un callback hors bande portant un jeton
que seule la cible a jamais détenu. Les signaux plus faibles conservent leurs propres niveaux et ne sont jamais
promus dans celui-ci. Et une exécution qui n'a pas pu tester quelque chose ne le rapporte jamais comme
propre.
RCEKit confirme le RCE via plusieurs méthodes sous une seule CLI. Ci-dessous, il est pointé vers des CVE réelles et publiquement documentées dans des logiciels de production — chaque verdict étant différencié par rapport à un contrôle sans payload :
| Classe de RCE | --methods | Cible réelle | Verdict |
|---|
| Injection de commande OS (basée sur les résultats) | reflected | Webmin 1.910 — CVE-2019-15107 | confirmed |
| Injection d'expression (OGNL) | eval | Apache Struts2 — S2-001 | confirmed |
| Recherche d'expression (Log4Shell/JNDI) | lookup | Apache Solr 8.11.0 (Log4j 2.14.1) — CVE-2021-44228 | lookup-sink |
| Injection de commande aveugle (sans sortie) | time | Webmin 1.910 — CVE-2019-15107 | needs-review |
Chaque ligne est reproduite par tests/bench/, qui exécute RCEKit
contre ces builds sous Docker et vérifie le verdict et son contrôle
négatif. Dernière exécution verte en 2.36.0 (2026-09-20) : 3/3 cas. Il s'agit d'une
affirmation ponctuelle, pas continue -- le benchmark est exécuté selon une cadence,
pas à chaque changement.
Chaque contrôle est le véritable test de la ligne. Struts2 sondé avec reflected revient
negative, car S2-001 réévalue l'OGNL et il n'y a pas de shell derrière.
Le signal time de Webmin est maintenu à needs-review sur une cible où il se trouve
être correct. Et Solr sondé avec oob revient negative bien qu'il soit
exploitable -- oob construit des commandes shell et un sink ${jndi:...} n'en exécute
aucune, ce qui est la lacune que lookup existe pour combler, mesurée plutôt
qu'affirmée.
La ligne Log4Shell indique lookup-sink, pas confirmed : ce que le callback prouve
est que le sink a résolu un URI choisi par RCEKit. Atteindre le RCE nécessite un serveur qui
répond à la recherche avec une classe chargeable, et au niveau de risque par défaut seul
jndi:dns:// sort -- une recherche de nom, sans connexion au-delà pour qu'un tel
serveur puisse y répondre.
reflected — injection de commande OS, Webmin CVE-2019-15107 → confirmed
eval — injection d'expression OGNL, Apache Struts2 S2-001 → confirmed
lookup-sink
time — injection de commande aveugle, Webmin CVE-2019-15107 → needs-review
RCEKit a deux formes prises en charge, et aucune n'est un repli pour l'autre.
Installez-le — pipx garde la CLI dans son propre environnement, ce qui est ce que vous
voulez pour un outil plutôt qu'une bibliothèque :```bash
pipx install rcekit # or: pip install rcekit
rcekit --doctor # confirms the corpus it will run with
**Ou prenez simplement le fichier unique.** Le corpus de payloads est intégré au module, donc
`rcekit.py` s'exécute seul sans rien d'autre — aucune étape d'installation, aucun
site-packages, rien à laisser derrière. Sur une jump box client, un hôte
air-gapped, ou partout où `pip install` n'est pas une option :```bash
curl -O https://raw.githubusercontent.com/kabiri-labs/rcekit/main/rcekit.py
python rcekit.py --doctor # same corpus, same check, zero installation
Les deux exécutent le même code et rapportent les mêmes verdicts. Travailler depuis une copie de travail est la troisième méthode, et ne nécessite aucune installation non plus :```bash git clone https://github.com/kabiri-labs/rcekit.git cd rcekit # Python 3.8+, standard library only
Placez un marqueur `FUZZ` là où votre entrée atterrit (ou sélectionnez un paramètre avec `-p` lors de l'utilisation d'une requête capturée), et demandez à RCEKit de prouver la RCE :```bash
rcekit --acknowledge-consent \
--verify-url "https://target.example/lookup?host=FUZZ" \
--methods reflected,eval
--no-remote : désactive la récupération à distance des métadonnées de paquets.--no-cache : ignore le cache local des métadonnées.--offline : force le mode hors ligne ; échoue si des métadonnées manquent.--timeout <sec> : délai d'expiration du réseau par requête.--retries <n> : nombre de tentatives en cas d'échec de récupération.--proxy <url> : proxy HTTP(S) à utiliser pour les requêtes réseau.--insecure : désactive la vérification du certificat TLS (à utiliser avec prudence).--verbose : active la journalisation détaillée.--quiet : supprime la sortie non essentielle.--json : émet les résultats au format JSON pour un traitement ultérieur.--output <file> : écrit la sortie dans un fichier au lieu de stdout.--config <file> : charge la configuration depuis un fichier YAML ou TOML.--profile <name> : sélectionne un profil de configuration nommé.--dry-run : affiche les actions sans les exécuter.--force : écrase les fichiers existants ou ignore les vérifications de sécurité.--yes : répond automatiquement oui aux invites de confirmation.--no-color : désactive la sortie colorée du terminal.--log-level <level> : définit le niveau de journalisation (debug, info, warn, error).--log-file <file> : écrit les journaux dans le fichier spécifié.--version : affiche les informations de version et quitte.--help : affiche le message d'aide et quitte.```
[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)
### À partir d'une requête capturée — la forme que possèdent la plupart des cibles réelles
Un `--verify-url` transporte une URL et rien d'autre. La plupart des sinks qui valent la peine d'être testés se trouvent
derrière un POST avec un cookie de session, un type de contenu et un corps, et RCEKit prend
cette requête dans son intégralité : enregistrez-la depuis votre proxy ou les devtools de votre navigateur et nommez
le champ dans lequel injecter.```bash
rcekit --acknowledge-consent \
-r search.req -p q \
--methods reflected,eval
| -s | --server | SERVER | http://localhost:8080 | URL du serveur MCP |
| -t | --token | TOKEN | - | Jeton d'authentification |
| -c | --config | CONFIG | - | Chemin du fichier de configuration |
| -v | --verbose | - | - | Activer la journalisation verbeuse |
| -q | --quiet | - | - | Supprimer la sortie non essentielle |
| --timeout | - | TIMEOUT | 30 | Délai d'expiration de la requête en secondes |
| --retry | - | RETRY | 3 | Nombre de tentatives en cas d'échec |
| --no-color | - | - | - | Désactiver la sortie colorée |
| --version | - | - | - | Afficher la version et quitter |
| --help | - | - | - | Afficher le message d'aide et quitter |
# Démarrer le serveur MCP sur le port par défaut
mcp-server start
# Démarrer avec un port et un hôte personnalisés
mcp-server start --port 9090 --host 0.0.0.0
# Se connecter à un serveur distant
mcp-server connect --server https://api.example.com --token YOUR_TOKEN
# Exécuter avec un fichier de configuration
mcp-server start --config ./config.yaml
# Activer la journalisation verbeuse
mcp-server start --verbose
Le serveur MCP peut être configuré à l'aide d'un fichier de configuration YAML :
# config.yaml
server:
host: "0.0.0.0"
port: 8080
timeout: 30
max_connections: 100
auth:
enabled: true
token: "your-secret-token"
token_expiry: 3600
logging:
level: "info"
format: "json"
output: "stdout"
tools:
enabled:
- "file_read"
- "file_write"
- "http_request"
disabled:
- "shell_exec"
security:
allowed_paths:
- "/home/user/projects"
- "/tmp"
max_file_size: 10485760 # 10MB
rate_limit:
enabled: true
requests_per_minute: 60
| Variable | Description | Valeur par défaut |
|---|---|---|
MCP_SERVER_HOST | Nom d'hôte du serveur | localhost |
MCP_SERVER_PORT | Port du serveur | 8080 |
MCP_AUTH_TOKEN | Jeton d'authentification | - |
MCP_LOG_LEVEL | Niveau de journalisation | info |
MCP_TIMEOUT | Délai d'expiration de la requête | 30 |
MCP_MAX_CONNECTIONS | Nombre maximal de connexions | 100 |
Lire le contenu d'un fichier.
Paramètres :
| Paramètre | Type | Requis | Description |
|---|---|---|---|
path | string | Oui | Chemin d'accès au fichier |
encoding | string | Non | Encodage du fichier (par défaut : utf-8) |
max_size | integer | Non | Taille maximale du fichier en octets |
Exemple :
{
"tool": "file_read",
"params": {
"path": "/home/user/document.txt",
"encoding": "utf-8"
}
}
Écrire du contenu dans un fichier.
Paramètres :
| Paramètre | Type | Requis | Description |
|---|---|---|---|
path | string | Oui | Chemin d'accès au fichier |
content | string | Oui | Contenu à écrire |
mode | string | Non | Mode d'écriture (write, append) |
encoding | string | Non | Encodage du fichier (par défaut : utf-8) |
Exemple :
{
"tool": "file_write",
"params": {
"path": "/home/user/output.txt",
"content": "Hello, World!",
"mode": "write"
}
}
Effectuer une requête HTTP.
Paramètres :
| Paramètre | Type | Requis | Description |
|---|---|---|---|
url | string | Oui | URL de la requête |
method | string | Non | Méthode HTTP (GET, POST, PUT, DELETE) |
headers | object | Non | En-têtes de la requête |
body | string | Non | Corps de la requête |
timeout | integer | Non | Délai d'expiration en secondes |
Exemple :
{
"tool": "http_request",
"params": {
"url": "https://api.example.com/data",
"method": "GET",
"headers": {
"Authorization": "Bearer YOUR_TOKEN"
}
}
}
Exécuter une commande shell.
Paramètres :
| Paramètre | Type | Requis | Description |
|---|---|---|---|
command | string | Oui | Commande à exécuter |
timeout | integer | Non | Délai d'expiration en secondes |
cwd | string | Non | Répertoire de travail |
Exemple :
{
"tool": "shell_exec",
"params": {
"command": "ls -la",
"timeout": 10,
"cwd": "/home/user"
}
}
Le serveur MCP prend en charge plusieurs méthodes d'authentification :
Exemple avec un jeton :
curl -H "Authorization: Bearer YOUR_TOKEN" http://localhost:8080/api/tools
Exemple avec une clé API :
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:8080/api/tools
Restreindre l'accès aux fichiers à des répertoires spécifiques :
security:
allowed_paths:
- "/home/user/projects"
- "/tmp"
denied_paths:
- "/etc"
- "/var"
- "/root"
Configurer la limitation de débit pour éviter les abus :
security:
rate_limit:
enabled: true
requests_per_minute: 60
burst_size: 10
# Cloner le dépôt
git clone https://github.com/example/mcp-server.git
cd mcp-server
# Créer un environnement virtuel
python -m venv venv
source venv/bin/activate # Sur Windows : venv\Scripts\activate
# Installer les dépendances
pip install -r requirements.txt
# Installer en mode développement
pip install -e .
# Exécuter tous les tests
pytest
# Exécuter avec couverture
pytest --cov=mcp_server --cov-report=html
# Exécuter des tests spécifiques
pytest tests/test_server.py -v
# Construire le paquet
python -m build
# Le paquet sera créé dans le répertoire dist/
ls dist/
git checkout -b feature/amazing-feature)git commit -m 'Add some amazing feature')git push origin feature/amazing-feature)Ce projet est sous licence MIT - voir le fichier LICENSE pour plus de détails.
[detect] CONFIRMED execution (3): [reflected/unix/raw] ; echo RKHWNHK$((114157+752773))RKXGFIH$(echo RKHSEIF)RKHWNHK (target computed 'RKHWNHK866930RKXGFIHRKHSEIFRKHWNHK' — random operands, absent from control)
La méthode, le chemin, les en-têtes, le corps et les cookies sont réutilisés tels que capturés, et chaque valeur est encodée pour le contexte dans lequel elle atterrit — une feuille JSON, un champ de formulaire et un cookie ne sont pas échappés de la même manière. Supprimez `-p` et marquez l'emplacement avec `FUZZ` ou `*` à la place, si vous préférez.
### Tout ce que l'outil possède
Deux choses ne sont accessibles qu'à partir d'une requête capturée : **l'énumération des points d'injection** (`--auto-params`), et tout sink qui nécessite une session. Ainsi, l'exécution la plus complète que RCEKit peut effectuer part de `-r`, et non d'une URL — ce qui vaut la peine d'être su avant de conclure qu'une cible est propre.```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
| -s | --server | SERVER | http://localhost:8080 | URL du serveur MCP |
| -t | --token | TOKEN | None | Jeton d'authentification Bearer |
| -k | --insecure | | False | Désactiver la vérification du certificat TLS |
| -v | --verbose | | False | Activer la journalisation de débogage |
| -h | --help | | | Afficher le message d'aide et quitter |
# Se connecter à un serveur local
mcp-client --server http://localhost:8080
# Se connecter avec authentification
mcp-client --server https://mcp.example.com --token "your-token-here"
# Se connecter à un serveur distant avec TLS auto-signé
mcp-client --server https://192.168.1.100:8443 --insecure
# Activer la journalisation de débogage
mcp-client --server http://localhost:8080 --verbose
Une fois connecté, vous pouvez utiliser les commandes suivantes :
| Commande | Description |
|---|---|
help | Afficher le message d'aide |
tools | Lister les outils disponibles |
resources | Lister les ressources disponibles |
prompts | Lister les invites disponibles |
call <tool> <json> | Appeler un outil avec des arguments JSON |
read <uri> | Lire une ressource |
prompt <name> <json> | Obtenir une invite avec des arguments |
clear | Effacer l'écran |
exit ou quit | Quitter le client |
mcp> tools
mcp> call search_files {"query": "*.py", "path": "/home/user"}
mcp> read file:///home/user/document.txt
mcp> prompt code_review {"language": "python", "code": "def hello(): pass"}
Le client est construit sur une architecture modulaire :
mcp-client/
├── main.py # Point d'entrée et interface CLI
├── client.py # Implémentation du client MCP
├── transport.py # Couche de transport (stdio, HTTP, WebSocket)
├── protocol.py # Implémentation du protocole JSON-RPC
├── commands.py # Commandes interactives
└── utils.py # Fonctions utilitaires
# Cloner le dépôt
git clone https://github.com/example/mcp-client.git
cd mcp-client
# Créer un environnement virtuel
python -m venv venv
source venv/bin/activate # Sur Windows : venv\Scripts\activate
# Installer les dépendances de développement
pip install -r requirements-dev.txt
# Installer le package en mode développement
pip install -e .
# Exécuter tous les tests
pytest
# Exécuter avec couverture
pytest --cov=mcp_client --cov-report=html
# Exécuter des tests spécifiques
pytest tests/test_client.py -v
Le projet utilise black pour le formatage et ruff pour le linting :
# Formater le code
black mcp_client/
# Vérifier le linting
ruff check mcp_client/
# Corriger automatiquement les problèmes de linting
ruff check --fix mcp_client/
Si vous ne parvenez pas à vous connecter au serveur :
Pour les certificats auto-signés, utilisez l'option --insecure :
mcp-client --server https://self-signed.example.com --insecure
Activez la journalisation de débogage pour voir les détails de la communication :
mcp-client --server http://localhost:8080 --verbose
Cela affichera les messages JSON-RPC envoyés et reçus, ce qui peut aider à diagnostiquer les problèmes.
Les contributions sont les bienvenues ! Veuillez suivre ces étapes :
git checkout -b feature/amazing-feature)git commit -m 'Add amazing feature')git push origin feature/amazing-feature)Ce projet est sous licence MIT - voir le fichier LICENSE pour plus de détails.
Avertissement : Cet outil est destiné à des fins éducatives et de test. Utilisez-le de manière responsable et conformément à toutes les lois et réglementations applicables.``` [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
Ce que chaque option ouvre :
| | |
|---|---|
| `--auto-params all` | chaque valeur de requête, feuille JSON, champ de formulaire, partie multipart, cookie et en-tête, au lieu d'un seul champ nommé |
| `--point-order thorough` | chaque en-tête non hop-by-hop, pas seulement ceux à fort rendement |
| `--methods ...,lookup,deser` | les sinks d'expression-lookup et de désérialisation, que les méthodes en forme de shell ne peuvent pas atteindre |
| `--oob-host` | un hôte de callback pour les méthodes aveugles. Nécessite un domaine qui vous est délégué ; le port 53 nécessite root |
| `--verify-active-risk stateful` | le barreau le plus élevé — ajoute les formes de sonde qui font que la cible récupère depuis une adresse que RCEKit n'a pas choisie |
| `--probe-depth full` | chaque forme de break-out par sink, pas seulement les moins coûteuses |
| `--detect-json` | les mêmes verdicts en JSON lisible par machine |
**Cela représente beaucoup de requêtes.** La ligne de coût s'affiche avant que quoi que ce soit ne se déclenche, et
`--max-points` / `--max-payloads` la bornent. Exécutez-le contre une instance que vous êtes
autorisé à casser : `--verify-active-risk stateful` est le niveau pour une cible jetable, pas pour la production.
Aucune infrastructure externe, aucun fichier de configuration.
**Ne prenez pas les GIFs pour argent comptant** — [reproduisez-les vous-même](https://github.com/kabiri-labs/rcekit/blob/main/docs/verify-it-yourself.md)
contre des cibles Webmin et Struts2 dockerisées en environ cinq minutes.
**Ensuite :** le [**guide de terrain**](https://github.com/kabiri-labs/rcekit/blob/main/docs/guide.md) parcourt les situations réelles — requêtes
capturées, WAFs, séparateurs filtrés, sinks entre guillemets, cibles aveugles et sans egress —
un exemple travaillé pour chacune.
---
## Ce que signifie un verdict
Trouver un *candidat* RCE est facile. Rapporter un candidat qui survit au retest de quelqu'un d'autre est la partie difficile, et cela échoue dans deux directions : un « possiblement vulnérable » qui s'avère être une réflexion, et un « non vulnérable » issu d'une exécution qui n'a en réalité rien testé.
RCEKit répond avec **huit verdicts qui ne sont jamais fusionnés les uns dans les autres** :
| Verdict | Ce qu'il affirme |
|---|---|
| **`confirmed`** | La cible a exécuté l'entrée. Elle a renvoyé une valeur qu'elle ne pouvait pas produire autrement — calculée à partir d'opérandes aléatoires propres à cette sonde — et cette valeur est absente d'un contrôle sans payload. |
| **`deserialization-sink`** | La cible a reconstruit un graphe d'objets fourni par l'attaquant. Prouvé, mais concernant une *propriété différente* : atteindre l'RCE depuis là dépend de gadgets du classpath, donc cela n'est jamais appelé RCE. |
| **`lookup-sink`** | La cible a résolu un URI que RCEKit lui a fourni — une expression `${jndi:…}` a atteint un lookup, prouvé sur un callback portant un jeton que seule cette sonde détenait. C'est un sink, pas une exécution : atteindre l'RCE depuis là nécessite un serveur répondant avec une classe chargeable. |
| **`needs-review`** | Un signal réel qui n'est pas une preuve à lui seul — une régression temporelle linéaire, une empreinte de parseur. Cela vaut votre temps, mais jamais le mot « confirmed ». |
| **`inconclusive`** | La preuve est apparue, mais n'a pas pu être attribuée à une exécution — le contrôle sans payload la portait aussi. |
| **`negative`** | Des sondes ont été construites, ont atteint la cible, et n'ont rien trouvé. |
| **`error`** | Rien n'a atteint la cible. |
| **`nothing-tested`** | Aucune sonde n'a été construite du tout. |
Dès que `confirmed` et `maybe` se confondent, `confirmed` ne signifie plus rien — donc
rien n'est jamais promu vers le haut. Une régression temporelle reste `needs-review` quelle que soit
la propreté de la pente. Un callback de désérialisation reste `deserialization-sink` quelle que soit
votre certitude que le classpath est exploitable.
### L'autre moitié : une exécution qui n'a rien testé n'est jamais propre
Les deux dernières lignes sont celles que les autres outils n'ont pas, et elles comptent plus qu'elles
n'en ont l'air. Un scanner qui n'a pas pu atteindre la cible, ou qui n'a construit aucune sonde parce que
vos options les excluaient toutes, n'a **rien** appris sur la cible —
et afficher `negative` dans ce cas est un mensonge qui se lit exactement comme une absence de danger.
Donc `error` et `nothing-tested` sont des verdicts de premier ordre, l'exécution se termine avec un code non nul,
et RCEKit indique lequel des deux s'est produit et pourquoi :```
[!] 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.
Il se déclenche partout où une exécution peut silencieusement devenir vide : une méthode qui ne s'applique pas aux environnements sélectionnés, un échelon --sink-shape pour lequel le shell choisi n'a pas de syntaxe, une sélection --bridges entièrement retenue par le plafond de sécurité, un corps de requête qui a rompu la livraison avant même d'arriver.
Une exécution qui n'a été que partiellement aveuglée reçoit le même traitement un niveau en dessous. Si vous avez demandé un oracle de second ordre et que le point de terminaison observé n'a jamais répondu, les verdicts des sondes tiennent toujours — mais l'exécution vous indique qu'ils ont été décidés sans jamais lire le canal que vous lui avez indiqué, plutôt que de les laisser passer pour un négatif de second ordre.
Une seule CLI, un seul flag --methods, couvrant les principaux chemins vers l'RCE :
| Classe RCE | --methods | Comment RCEKit le prouve |
|---|---|---|
| Injection de commande OS | reflected | Fait calculer au shell $((a+b)) sur des opérandes aléatoires et réduit $(echo TAG) ; confirme le résultat, jamais l'expression littérale. Écrit dans le dialecte propre au sink — POSIX, cmd.exe ou PowerShell. |
Injection de code / d'expression — SSTI, SpEL, OGNL, Groovy, eval() (CWE-94) | eval | Injecte a*b dans chaque syntaxe de template courante (${…} {{…}} #{…} %{…} <%=…%> @(…), nue) ; confirme que le produit apparaît tandis que le littéral a*b n'apparaît pas. |
| Injection de commande aveugle (sans sortie) | time | Déclenche une série de délais contrôlée 0/N/2N et confirme que le temps de réponse suit le délai linéairement ; rapporté needs-review — la gigue ne peut pas le simuler, mais le timing n'est pas une valeur calculée. |
| Cibles internes / sans sortie réseau | file | Écrit un jeton aléatoire et le récupère via n'importe quel chemin de relecture — une racine web, un paramètre LFI, un gestionnaire de téléchargement ou d'export, un aperçu adossé à /tmp. Prouve l'exécution plus une primitive d'écriture, sans listener externe. |
| Primitive d'upload / d'écriture — PUT-a-JSP, upload non vérifié (CWE-434) | write | Écrit un one-liner qui calcule un produit via votre propre requête d'upload, puis récupère le fichier : le produit est un RCE confirmed, la source qui revient verbatim est needs-review — écriture de fichier arbitraire, servie mais non interprétée. |
| Sinks de désérialisation — fastjson, shiro, weblogic (CWE-502) | deser | Prouve que le point de terminaison désérialise des données de l'attaquant, via un gadget DNS non exécutant ou un différentiel de forme d'erreur. Rapporté comme , comme RCE. |
Trois choses élargissent la portée de ces méthodes, sans changer ce que l'une d'elles appellera confirmed :
--observe-url) — lorsque la charge utile atterrit sur une requête et s'exécute sur une autre : SSTI stocké rendu sur une page de profil, une charge utile écrite dans un log qu'un moteur de template rend plus tard, une tâche mise en file. Le point de terminaison observé est différencié par rapport à un instantané pris avant l'envoi de toute sonde.--bridges) — COPY … FROM PROGRAM, xp_cmdshell, expect://. Un pont est un porteur, pas un oracle : il enveloppe la commande que les méthodes construisent déjà, donc les mêmes niveaux s'appliquent à travers lui.-p all) — query, feuilles JSON, champs de formulaire, parties multipart, cookies, en-têtes et segments de chemin, chacun encodé selon l'endroit où il atterrit, avec le coût des sondes affiché avant tout déclenchement. Un corps GraphQL est ordonné selon ce qui peut réellement confirmer : les variables qu'un resolver lit avant le document d'opération lui-même.Mélangez les méthodes librement : --methods reflected,eval,time exécute les trois et rapporte chaque niveau séparément.
Périmètre honnête. RCEKit confirme les RCE qui sont atteignables en injectant dans une requête et interprétés par un shell ou un évaluateur. Il ne couvre pas les bugs de corruption mémoire (débordement de tampon, UAF) ni l'injection d'arguments dans un tableau
argvsans shell — ce sont des problèmes différents. Les chaînes de gadgets de désérialisation restent aussi hors périmètre :--methods deserprouve qu'un point de terminaison désérialise des données de l'attaquant et le dit dans son propre niveau, mais quel gadget (le cas échéant) transforme cela en exécution dépend du classpath de la cible, et RCEKit ne prétend pas le savoir. Il vise à être excellent sur les classes de RCE pilotées par injection ci-dessus plutôt que médiocre en tout.
Les autres outils de ce domaine sont conçus pour vous faire entrer. RCEKit est conçu pour que la découverte survive à l'examen d'un tiers — le retest du client, la file de triage, la revue du rapport. Cette différence apparaît trois fois.
Vous connaissez rarement la classe avant de tester. Couvrir un sink inconnu avec des outils mono-classe signifie exécuter chacun à tour de rôle et reconstruire la requête pour chacun :
| Peut confirmer | RCEKit | commix | SSTImap | Nuclei |
|---|---|---|---|---|
| Injection de commande OS | ✅ | ✅ (tout son périmètre) | — | par template |
| Injection d'expression / SSTI | ✅ | via sa technique basée sur eval | ✅ (tout son périmètre) | par template |
| Aveugle — timing | ✅ comme niveau séparé | ✅ | ✅ | — |
| Aveugle — hors bande | ✅ listener intégré | — | — | via interactsh |
| Sans sortie réseau — écrire & récupérer | ✅ n'importe quel chemin de relecture | ✅ (racine web) | — | — |
Sinks cmd.exe et PowerShell | ✅ sondes par dialecte | ✅ (cmd) | — | par template |
| Upload → écrire-puis-exécuter | ✅ écriture vs. exécution, niveaux séparés | — | — | par template |
| Second ordre — atterrit ici, s'exécute là | ✅ | — | — | — |
| Pont de langage de requête vers l'OS | ✅ | — | — | par template |
| Sink de désérialisation | ✅ niveau propre, jamais appelé RCE | — | — | par template |
| Tout ce qui précède, une CLI, une exécution | ✅ | — | — | — |
Couverture selon la liste des techniques documentées de chaque projet. SSTImap est le successeur maintenu de tplmap, que son auteur a marqué comme non maintenu.```bash
python rcekit.py --acknowledge-consent -r request.txt -p host --methods reflected,eval,time
### 2. Il conteste ses propres résultats
Un outil rapporte ce qu'il a trouvé. RCEKit rapporte aussi **ce qu'il a refusé de croire** —
`inconclusive` est un verdict à part entière, pour les preuves qui se sont manifestées mais qui n'ont pas pu
être attribuées à une exécution :```
[detect] methods: reflected, eval
[detect] sent 13 probes: confirmed=0, inconclusive=2, negative=11
Ces deux cas auraient été la découverte de quelqu'un d'autre. Cinq mécanismes produisent ce verdict, et ils s'exécutent à chaque confirmation :
inconclusive, pas une découverte.$((a+b)) ; seule l'exécution retourne la valeur.confirmed.Le même instinct joue dans l'autre sens. Le timing ne s'auto-confirme jamais, un callback de désérialisation n'est jamais appelé RCE, et une exécution qui n'a construit aucune sonde n'est jamais qualifiée de négative.
Les contrôles sur lesquels les règles d'engagement d'un client portent réellement, dans l'outil plutôt que dans vos notes :
| Portail de consentement | Rien d'exploitant ne se génère ni ne se déclenche sans --acknowledge-consent. |
| Plan d'exécution | Affiche le nombre exact de sondes, les formes de sinks, les niveaux de sûreté et toute destination de callback sortante avant que la première requête ne parte. |
| Sûr par défaut | Les reverse shells, l'accès aux identifiants, les métadonnées cloud, le mouvement latéral et l'évasion de conteneur sont retenus jusqu'à ce que vous leviez --verify-active-risk ; la persistance et les backdoors nécessitent un second flag en plus. Les ponts qui créent un objet sur la cible sont soumis au même plafond. |
| Commandes de nettoyage | file, write et les ponts à état modifient l'état de la cible, donc chaque découverte — y compris un needs-review — affiche quoi exécuter pour l'annuler. |
| Les identifiants restent en place | La récupération en lecture de file ne transporte les en-têtes Authorization/Cookie de l'exécution que vers la même origine, et le dit à voix haute lorsqu'il les retient. La récupération du canal observé n'en envoie aucun, sauf si vous lui fournissez une requête avec --observe-request. |
| Piste d'audit expurgée | Chaque exécution atterrit dans exploit_audit.log, enregistrant qu'un en-tête d'identifiant a été envoyé, jamais sa valeur. |
| Filigrane | --watermark appose un jeton traçable dans chaque payload, afin qu'un payload retrouvé dans les logs du client des mois plus tard soit attribuable à votre exécution. |
| Aucun callback tiers | L'écouteur OOB est le vôtre. Rien ne passe par un serveur d'interaction public, ce que certains engagements interdisent purement et simplement. |
| Un seul fichier stdlib | rcekit.py s'exécute seul — jump box, hôte air-gapped, partout où pip install n'est pas une option. |
Vous voulez un shell plutôt qu'un verdict ? commix et SSTImap poursuivent en post-exploitation ; RCEKit s'arrête à la preuve par conception. Balayer des milliers d'hôtes pour des CVE connues ? C'est le travail de Nuclei — et RCEKit écrit des templates Nuclei (--output-format nuclei), donc il alimente votre scanner au lieu de le concurrencer. Vous savez déjà que l'injection est du SQL et vous voulez la base de données elle-même ?
sqlmap règne sur ce terrain — les ponts de RCEKit existent pour prouver que l'OS est atteignable depuis un paramètre texte, pas pour exploiter la base de données.
Chaque ligne est un exemple concret dans le guide de terrain — la commande, ce qu'elle envoie, et comment lire ce qui revient.
| Situation | Aller à |
|---|---|
| J'ai une URL et un paramètre | Pointer vers une URL |
| J'ai une requête sauvegardée depuis Burp | Pointer vers une requête capturée |
| L'app est en JSON / le payload ne cesse d'être altéré | Faire atterrir le payload intact |
| Je ne sais pas de quelle classe il s'agit | Choisir les méthodes |
Le sink supprime ; | Quand le sink filtre les séparateurs |
Mon entrée atterrit à l'intérieur de 'quotes' | Injecter à l'intérieur de quotes |
| Le sink exécute mon entrée comme la commande entière | Sinks à commande entière |
| La cible est Windows ou le sink est PowerShell | Sinks Windows et PowerShell |
| Il y a un WAF | Contourner un WAF |
| Aucune sortie ne revient du tout | Cibles aveugles |
| Aucune sortie et aucune sortie réseau | Cibles sans sortie réseau |
| La requête stocke un fichier au lieu d'exécuter quoi que ce soit | Cibles à upload et primitive d'écriture |
| Vérifiez-le vous-même | Reproduisez les confirmations ci-dessus sur votre propre machine, contre des cibles vulnérables dockerisées. Cinq minutes. |
| Guide de terrain | Parcours guidé par l'exemple de chaque situation réelle, de la première sonde aux chaînes multi-étapes. Commencez ici. |
| Génération de payloads & exports | RCEKit comme générateur de payloads : profils de cibles, et exports Burp / ffuf / Nuclei. |
| Référence | Chaque flag, environnement, catégorie, contexte, encodage et sink d'exécution de code. |
| CHANGELOG.md | Ce qui a changé à chaque version, et ce qu'il faut revérifier lors d'une mise à niveau. |
| CONTRIBUTING.md | Comment ajouter des sinks, catégories, encodages et méthodes de détection. |
| SECURITY.md | Signaler une vulnérabilité dans RCEKit lui-même. |
RCEKit exploite, et c'est là tout l'intérêt. Une vulnérabilité est confirmée en faisant faire la chose à la cible, parce que c'est la seule preuve qu'une signature ne peut pas falsifier et qu'une version corrigée ne peut pas produire par accident. Ce qui borne une exécution n'est pas une réticence à exploiter. Ce sont deux faits structurels et un interrupteur.
Il ne prend aucun payload arbitraire de votre part. Les sondes sont construites par le moteur pour servir un oracle — une arithmétique sur des opérandes aléatoires propres à cette sonde, un nom que seule cette exécution aurait pu choisir. Il n'existe aucune entrée qui transforme la détection en autre chose, parce qu'il n'y a pas de telle entrée à fournir.
Tout ce qui va au-delà du calcul d'une valeur déclare le niveau dont il a besoin, donc un seul flag décide jusqu'où va une exécution : --verify-active-risk safe | intrusive | stateful. Une méthode ou une forme de sonde unique au-dessus de ce niveau est retenue par son nom, avec le flag qui l'enverrait — une échelle qui se réduit en silence est indiscernable d'une cible sans rien à trouver. Contre une instance jetable, élevez le niveau et obtenez tout ce que l'outil possède.
--acknowledge-consent ; --detection-only est bénin et ne le nécessite pas.--verify-active-risk. Les payloads destructeurs (persistance, backdoors) ne sont jamais
déclenchés sans --verify-allow-destructive. Un plan d'exécution affiche exactement
ce qui sera envoyé avant que quoi que ce soit ne se déclenche.safe / intrusive / stateful. Les payloads du corpus sont
filtrés par --max-safety ; les méthodes de détection et leurs formes de sondes déclarent
les mêmes échelons et sont filtrées par --verify-active-risk, donc une méthode qui
fait sortir la cible ou laisse quelque chose derrière elle est soumise au même
ordonnancement que chaque payload du corpus. Le pré-vol nomme le niveau dont chaque élément
retenu a réellement besoin. file et write sont contrôlés par leur propre configuration
à la place : ni l'un ni l'autre ne fait quoi que ce soit tant que vous ne nommez pas un répertoire
dans lequel écrire et une URL depuis laquelle le relire.exploit_audit.log ; --watermark intègre un jeton traçable ; les logs d'exécution vont dans
rcekit.log.--template-file explicite manquant, fait que RCEKit refuse de s'exécuter et se termine avec un code non nul
plutôt que de ne rien générer en silence (--doctor le vérifie). Seul un fichier de corpus
par défaut absent retombe sur la copie intégrée, et il le dit lorsqu'il le fait.Ce toolkit est destiné uniquement aux tests d'intrusion autorisés, à la recherche en sécurité, à l'éducation et à la formation défensive. Ne l'utilisez jamais contre des systèmes sans permission explicite — les tests non autorisés sont illégaux.
python -m unittest discover -s tests # dependency-free test suite
Les contributions sont les bienvenues — nouveaux sinks/catégories, encodages, environnements, méthodes de détection, corrections de bugs et documentation. Les bases de payloads résident dans des modèles JSON modifiables
(`templates/payloads.json`), de sorte que la plupart des couvertures s'étendent sans toucher au code source Python. Après avoir modifié le corpus, actualisez la copie intégrée qui est livrée dans
`rcekit.py` :```bash
python tools/embed_corpus.py # --check verifies it is current
La suite de tests échoue si les deux divergent. Voir CONTRIBUTING.md.
MIT — voir LICENSE.
deserialization-sink| Aveugle / hors bande — exfil, async | oob | Un listener HTTP/DNS intégré reçoit les callbacks et corrèle chacun à la charge utile exacte ; chaque sonde porte son propre jeton. |
| Sinks de recherche d'expression — Log4Shell/JNDI | lookup | Le sink résout un URI ${jndi:…} au lieu d'exécuter une commande, donc les sondes shell d'oob n'atteignent rien. Le prouve sur le callback seul et rapporte lookup-sink, jamais confirmed. Seul jndi:dns:// est envoyé — une résolution de nom et rien d'autre — donc ce qui est prouvé est la recherche, pas une chaîne de gadgets. |
| Le payload s'exécute plus tard, sur une autre requête | Quand l'exécution se produit sur une autre requête |
| Le point d'injection est du SQL et le sink est l'hôte de la base de données | Ponts de langage de requête |
| Le point de terminaison prend un objet sérialisé | Sinks de désérialisation |
| Le sink est derrière un login ou un upload de fichier | Chaînes multi-étapes |
J'ai obtenu needs-review / inconclusive / error | Lire les résultats |
| Il dit que le corpus est inutilisable | Dépannage |