Skip to content

Repository files navigation

RCEKit — prove RCE, don't guess it

Version 2.23.3 · MIT · Python 3.8+ · zero third-party dependencies

RCEKit is an RCE detection & confirmation toolkit for authorised penetration testing, red teaming, and security research. Point it at a target you are allowed to test — a URL or a captured HTTP request — and it tells you what actually executed, backed by proof, not a "maybe".

A confirmed verdict means the target computed or executed a value only execution could produce — random arithmetic, a template evaluation, a written token — checked against a payload-free control. Reflection, coincidence, and jitter can't fake it, so confirmed is something you can put in a report.


Proof, not "maybe"

RCEKit confirms RCE through multiple methods under one CLI. Below it is pointed at real, publicly-documented CVEs in production software — each verdict differenced against a payload-free control:

RCE class --methods Real-world target Verdict
OS command injection (results-based) reflected Webmin 1.910 — CVE-2019-15107 confirmed
Expression injection (OGNL) eval Apache Struts2 — S2-001 confirmed
Blind / out-of-band (Log4Shell/JNDI) OOB listener Log4Shell — CVE-2021-44228 confirmed
Blind command injection (no output) time Webmin 1.910 — CVE-2019-15107 needs-review
reflected — OS command injection, Webmin CVE-2019-15107 → confirmed

RCEKit confirming OS command injection on Webmin 1.910 (CVE-2019-15107): the shell computes arithmetic on random operands, the result is reflected in the response and absent from a payload-free control

eval — OGNL expression injection, Apache Struts2 S2-001 → confirmed

RCEKit confirming OGNL expression injection on Apache Struts2 (S2-001): the payload %{ab} evaluates to the product in the response while the literal ab does not

out-of-band — blind Log4Shell (CVE-2021-44228) via a DNS callback → confirmed

RCEKit auto-confirming a blind Log4Shell RCE (CVE-2021-44228) via an OOB DNS callback, correlating the DNS hit back to the exact payload

time — blind command injection, Webmin CVE-2019-15107 → needs-review

RCEKit measuring a linear timing response on Webmin 1.910 (CVE-2019-15107): response time tracks a controlled 0/N/2N delay series — a needs-review timing candidate, never confirmed on its own


Quick start

git clone https://github.com/kabiri-labs/rcekit.git
cd rcekit                    # Python 3.8+, standard library only — nothing to install

Or take just the one file — the payload corpus is built in, so rcekit.py runs on its own with nothing beside it. On a client jump box, an air-gapped host, or anywhere pip install is not an option:

curl -O https://raw.githubusercontent.com/kabiri-labs/rcekit/main/rcekit.py
python rcekit.py --doctor    # confirms the corpus it will run with

Put a FUZZ marker where your input lands (or select a parameter with -p when using a captured request), and ask RCEKit to prove RCE:

python rcekit.py --acknowledge-consent \
  --verify-url "https://target.example/lookup?host=FUZZ" \
  --methods reflected,eval
[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)

No external infrastructure, no config file. If nothing is vulnerable you get a clean negative, not a false alarm. That's the whole idea.

Don't take the GIFs on trustreproduce them yourself against dockerised Webmin and Struts2 targets in about five minutes.

Next: the field guide walks the real situations — captured requests, WAFs, filtered separators, quoted sinks, blind and no-egress targets — one worked example each.


Why RCEKit

Finding an RCE candidate is easy. Proving it — reliably, without crying wolf — is the hard part:

  • Scanners flag "possibly vulnerable" and bury the real finding under false positives.
  • Blind RCE usually forces you to stand up interactsh/Collaborator just to confirm.
  • Each RCE class (command injection, SSTI, code injection) needs a different confirmation trick.
  • A report full of "maybe" findings wastes triage time and burns your credibility.

RCEKit answers with two verdict tiers that are never merged:

Tier Meaning
confirmed Execution proven — the target returned a value it could only produce by executing your input, and that value is absent from a payload-free control.
needs-review A real candidate worth a look (e.g. a blind timing signal), but not proof on its own.

Anything else is negative or inconclusive. The moment "confirmed" and "maybe" blur together, "confirmed" loses its meaning — so RCEKit keeps them apart, by design.


How RCEKit compares

The other tools in this space are built to get you in. RCEKit is built so the finding survives someone else's scrutiny — the client's retest, the triage queue, the report review. That difference shows up three times.

1. One injection point, every class, one run

You rarely know the class before you test. Covering an unknown sink with single-class tools means running each in turn and rebuilding the request for each one:

Can confirm RCEKit commix SSTImap Nuclei
OS command injection (its whole scope) per template
Expression injection / SSTI via its eval-based technique (its whole scope) per template
Blind — timing as a separate tier
Blind — out-of-band built-in listener via interactsh
No-egress — write & fetch back
All of the above, one CLI, one run

Coverage per each project's own documented technique list. SSTImap is the maintained successor to tplmap, which its author has marked unmaintained.

# 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

2. It argues with its own results

A tool reports what it found. RCEKit also reports what it refused to believeinconclusive is a verdict of its own, for evidence that showed up but could not be attributed to execution:

[detect] methods: reflected, eval
[detect] sent 13 probes: confirmed=0, inconclusive=2, negative=11

Those two would have been someone else's finding. Four mechanisms produce that verdict, and they run on every confirmation:

  • A payload-free control request. Evidence must be present with the payload and absent without it. Anything in both is inconclusive, not a finding.
  • A same-token inert control. A second request carries the identical random token in a non-executing form. A target that merely echoes input fails here — which is how a reflection is separated from an execution.
  • Random operands, never fixed strings. The oracle is a tag-wrapped sum or a boundary-fenced product computed fresh each run. Echoing the payload returns the literal $((a+b)); only execution returns the value.
  • Encoding-aware evidence search. A sink that base64-, hex-, URL-, HTML- or unicode-escapes its output still confirms — the raw body is checked first, so decoding only ever turns a missed hit into a hit, never the reverse.

And timing never self-confirms: a linear 0/N/2N regression is capped at needs-review and reported in its own tier, because it produces no computed value. confirmed and maybe are never merged into one word.

3. It is built for an authorised engagement, not a lab

The controls a client's rules of engagement actually ask about, in the tool rather than in your notes:

Consent gate Nothing exploitative generates or fires without --acknowledge-consent.
Execution plan Prints the exact payload count, safety tiers and any outbound callback destinations before the first request goes out.
Safe by default Reverse shells, credential access, cloud metadata, lateral movement and container escape are held back until you raise --verify-active-risk; persistence and backdoors need a second flag on top.
Cleanup commands The file method changes target state, so every finding prints the exact rm/del to undo it — paste it into the report.
Redacted audit trail Every run lands in exploit_audit.log, recording that a credential header was sent, never its value.
Watermarking --watermark stamps a traceable token into each payload, so a payload found in the client's logs months later is attributable to your run.
No third-party callbacks The OOB listener is yours. Nothing is routed through a public interaction server, which some engagements forbid outright.
One stdlib file rcekit.py runs alone — jump box, air-gapped host, anywhere pip install is not an option.

When to reach for something else

Want a shell rather than a verdict? commix and SSTImap continue into post-exploitation; RCEKit stops at proof by design. Sweeping thousands of hosts for known CVEs? That is Nuclei's job — and RCEKit writes Nuclei templates (--output-format nuclei), so it feeds your scanner instead of competing with it.


What it confirms

One CLI, one --methods flag, covering the main paths to RCE:

RCE class --methods How RCEKit proves it
OS command injection reflected Makes the shell compute $((a+b)) on random operands and collapse $(echo TAG); confirms the result, never the literal expression.
Code / expression injection — SSTI, SpEL, OGNL, Groovy, eval() (CWE-94) eval Injects a*b in every common template syntax (${…} {{…}} #{…} %{…} <%=…%> @(…), bare); confirms the product appears while the literal a*b does not.
Blind command injection (no output) time Fires a controlled 0/N/2N delay series and confirms the response time tracks the delay linearly; reported needs-review (jitter can't fake it, but timing isn't a computed value).
Internal / no-egress targets file Writes a random token to a web-reachable file and fetches it back — proving execution plus a write primitive, with no external listener.
Blind / out-of-band — Log4Shell/JNDI, exfil, async (OOB listener) Built-in HTTP/DNS listener receives callbacks and correlates each to the exact payload.

Mix them freely: --methods reflected,eval,time runs all three and reports each tier separately. If a combination builds no probes at all, RCEKit says so and exits non-zero — a run that tested nothing is never reported as a clean result.

Honest scope. RCEKit confirms RCE that is reachable by injecting into a request and interpreted by a shell or an evaluator. It does not cover memory-corruption bugs (buffer overflow, UAF), argument injection into a no-shell argv array, or confirm deserialization gadget chains beyond a timing signal — those are different problems. It aims to be excellent at the injection-driven RCE classes above rather than mediocre at everything.


Find your situation

Each row is a worked example in the field guide — the command, what it sends, and how to read what comes back.

Situation Go to
I have a URL and a parameter Point at a URL
I have a request saved from Burp Point at a captured request
The app is JSON / the payload keeps getting mangled Landing the payload intact
I don't know which class it is Choosing methods
The sink strips ; When the sink filters separators
My input lands inside 'quotes' Injecting inside quotes
The sink runs my input as the whole command Whole-command sinks
There's a WAF Working around a WAF
No output comes back at all Blind targets
No output and no egress No-egress targets
The sink is behind a login or a file upload Multi-step chains
I got needs-review / inconclusive / error Reading the results
It says the corpus is unusable Troubleshooting

Documentation

Verify it yourself Reproduce the confirmations above on your own machine, against dockerised vulnerable targets. Five minutes.
Field guide Example-driven walkthrough of every real situation, from a first probe to multi-step chains. Start here.
Payload generation & exports RCEKit as a payload generator: target profiles, and Burp / ffuf / Nuclei exports.
Reference Every flag, environment, category, context, encoding and code-execution sink.
CHANGELOG.md What changed in each release, and what to re-check when upgrading.
CONTRIBUTING.md How to add sinks, categories, encodings and detection methods.
SECURITY.md Reporting a vulnerability in RCEKit itself.

Safety & ethics

  • Consent gate — exploitation generation and verification require --acknowledge-consent; --detection-only is benign and does not.
  • Safe by default — verification fires only low-impact proofs; reverse shells, download-execute, credential access, lateral movement, container escape, cloud-metadata and OOB payloads are held back until you raise --verify-active-risk. Destructive payloads (persistence, backdoors) are never fired without --verify-allow-destructive. An execution plan prints exactly what will be sent before anything fires.
  • Safety tierssafe / intrusive / stateful, filtered by --max-safety.
  • Audit & logging — every exploitation/verification run is recorded in exploit_audit.log; --watermark embeds a traceable token; execution logs go to rcekit.log.
  • Corpus integrity — a corpus that is corrupt, or an explicit --template-file that is missing, makes RCEKit refuse to run and exit non-zero rather than silently generate nothing (--doctor checks it). Only an absent default corpus file falls back to the built-in copy, and it says so when it does.

This toolkit is intended for authorised penetration testing, security research, education, and defensive training only. Never use it against systems without explicit permission — unauthorized testing is illegal.

Development

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

Contributions welcome — new sinks/categories, encodings, environments, detection methods, bug fixes, and docs. Payload bases live in editable JSON templates (templates/payloads.json), so most coverage extends without touching the Python source. After changing the corpus, refresh the built-in copy that ships inside rcekit.py:

python tools/embed_corpus.py    # --check verifies it is current

The test suite fails if the two ever drift. See CONTRIBUTING.md.

License

MIT — see LICENSE.

About

Prove RCE, don't guess it — an RCE detection & confirmation toolkit for authorized pentesting. Point it at a URL or captured HTTP request and get a proof-backed "confirmed" verdict across command injection, SSTI/code injection, blind, and no-egress targets — multiple methods, low false positives, zero dependencies.

Topics

Resources

Contributing

Security policy

Stars

13 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages