Overview

A vulnerability write-up and a reliable exploit solve different problems. A write-up demonstrates that a security boundary can be crossed. Exploit development turns that observation into a repeatable implementation that can identify the target state, satisfy the required preconditions, trigger the vulnerable path, handle failure conditions and produce a result that can be independently verified.

The difference matters. A proof of concept may work once in a controlled lab while still being unsuitable for reliable testing. Conversely, exploit code that hides the underlying vulnerability behind layers of automation can make the technical root cause harder to understand. Good security research benefits from keeping both views: first establish why the vulnerability exists, then engineer the exploitation path without losing that reasoning.

This article describes the workflow I use when moving from technical vulnerability analysis toward practical exploit development. The examples are drawn from research already preserved on this site, including Apache Spark command injection, Craft CMS object-instantiation abuse and a chained Zyxel router compromise.

1. Reproduce the exact vulnerable state

The first objective is not a payload. It is a controlled and reproducible vulnerable environment.

That means identifying the product version, configuration and execution path that are actually required. Configuration-dependent vulnerabilities are a good reminder of this. In CVE-2022-33891, the vulnerable Apache Spark path is reached when ACL support is enabled. The relevant code constructs a shell command using attacker-controlled user input. Without the configuration that reaches that code, testing a payload tells you very little.

A useful reproduction therefore records at least:

  • the exact vulnerable version or version range;
  • non-default settings required to reach the flaw;
  • authentication or privilege requirements;
  • the endpoint, protocol or feature that accepts attacker-controlled input;
  • the process identity and environment in which the vulnerable operation executes.

This information later becomes exploit logic. A module should not merely send a request and hope the target behaves like the lab system.

2. Reduce the issue to its root cause

Once the vulnerable state is reproducible, the next step is to separate the root cause from the final exploitation technique.

For Apache Spark, the critical primitive is straightforward: externally influenced input is concatenated into a command passed through a shell. A reverse shell is not the vulnerability. It is simply one way to demonstrate the consequence of the command-injection primitive.

Other vulnerabilities require a longer path. CVE-2023-41892 in Craft CMS begins with arbitrary object instantiation. That primitive by itself is important, but exploitability depends on finding usable classes and behavior in the target environment. The analysis eventually reaches an exploitation chain involving Imagick and Magick Scripting Language to turn object creation into arbitrary PHP execution.

Keeping these layers separate makes the research easier to validate:

  1. identify the unsafe primitive;
  2. prove that attacker input reaches it;
  3. establish what capabilities the primitive provides;
  4. only then build the chain required for the desired impact.

That structure also makes an exploit easier to maintain. If a product update changes the final gadget or execution method while leaving the original primitive intact, you know which layer needs to be revisited.

3. Build the smallest useful proof of concept

A first proof of concept should answer one question with as little complexity as possible: can the security boundary actually be crossed?

For command injection, creating a file or making a controlled outbound request is often more useful during early testing than immediately deploying a full interactive payload. For an information-disclosure issue, retrieve one predictable value. For authentication bypass, access one resource that should require authentication.

Minimal proofs of concept have several advantages:

  • fewer moving parts when debugging;
  • clearer evidence of the vulnerable data flow;
  • reduced dependence on target utilities or network egress;
  • easier comparison between patched and vulnerable builds;
  • cleaner evidence for responsible disclosure.

Only after the primitive is stable should the proof of concept grow into more complex exploitation.

4. Validate the execution context

Remote code execution is not a complete description of impact. The execution context matters.

Determine which operating-system account runs the vulnerable process, what files it can access, which network interfaces it can reach and whether the application itself holds credentials or secrets that expand the impact. The same command injection can have very different consequences when it executes as a constrained service account versus a privileged system account.

This is also where assumptions must be made explicit. If an exploit requires bash, curl, Python, a writable web root or outbound network access, those are implementation dependencies—not properties of the vulnerability itself.

A reliable implementation either checks those assumptions, avoids them, or documents them clearly.

5. Treat vulnerability chains as state transitions

Single-request vulnerabilities are the easiest to automate. Real systems often require chaining several weaknesses or behaviors together.

The Zyxel router chained RCE research is a useful example. The exploitation path combines an unauthenticated local-file disclosure with a weak supervisor-password derivation mechanism. The information obtained in the first stage provides what is required for the second stage, ultimately enabling privileged access over a management service.

It is useful to model such a chain as explicit state transitions rather than one large exploit routine:

Unauthenticated network access

Retrieve router configuration

Extract device-specific information

Derive supervisor credentials

Authenticate to management service

Privileged command execution

Each transition should have its own success condition. If configuration retrieval fails, credential derivation should never run. If the required management service is not reachable, the exploit should explain that rather than reporting a generic failure at the final stage.

This approach improves both reliability and diagnostics.

6. Convert the proof of concept into an exploit workflow

The point at which a proof of concept becomes exploit development is usually not the addition of a payload. It is the addition of control logic.

A practical exploit should consider:

Target identification

Determine whether the remote service is the expected product and, where possible, whether the observed version or behavior is compatible with the exploitation path.

Preconditions

Check authentication state, configuration requirements, exposed services and other conditions that can be evaluated safely before triggering the vulnerability.

Encoding and transport

Payloads frequently cross several parsers: HTTP, JSON, XML, shell syntax, application escaping and sometimes an additional scripting language. Characters that are harmless in one layer may terminate or transform input in another. Encoding should therefore be designed around the complete parser chain.

Deterministic success checks

Do not equate an HTTP 200 response with successful exploitation. Prefer a condition that directly confirms the intended result: command output, a callback, a created artifact, a changed state or a follow-up request that proves access.

Failure reporting

Differentiate between not vulnerable, target not recognized, required configuration absent, payload failed and network failure. That distinction is valuable both to researchers and to defenders using the tooling for validation.

7. Keep implementation provenance separate from research references

Exploit code has its own lifecycle. A local research implementation may become a pull request, and a merged contribution may later exist as an official module in a framework.

The site now preserves those states separately from the normal research references. For Metasploit work, the preferred representation is:

Official Metasploit module
Contribution pull request

The official module shows the maintained implementation that is available to users. The pull request preserves the contribution and review history. A local development copy is only used when there is no official Metasploit equivalent.

This distinction avoids presenting several URLs for the same implementation as if they were independent artifacts.

8. Preserve the technical narrative

Automation should not replace the explanation of the vulnerability.

A strong research record should still make it possible to understand:

  • where attacker-controlled data enters the application;
  • which validation or authorization boundary fails;
  • how the vulnerable code path is reached;
  • what the primitive allows;
  • which additional steps are necessary for practical exploitation;
  • what privileges and environmental assumptions are involved;
  • how the vendor corrected the issue.

The exploit is evidence and implementation. The research article is the technical argument.

Keeping both makes the work more useful after product versions, framework modules and public infrastructure change.

9. A practical checklist

Before considering an exploit-development cycle complete, I use the following questions as a final sanity check:

  • Can the vulnerable state be reproduced from a clean environment?
  • Is the root cause clearly separated from the payload or gadget used for exploitation?
  • Does the minimal proof of concept demonstrate the issue without unnecessary dependencies?
  • Are authentication, privileges and configuration preconditions explicit?
  • Is the execution context known?
  • Does every stage in a vulnerability chain have a clear success condition?
  • Can the implementation distinguish an unsupported target from an exploitation failure?
  • Are payload encoding and parser boundaries understood?
  • Is successful exploitation verified deterministically?
  • Are the maintained implementation and contribution history linked without duplication?
  • Does the research remain understandable without running the exploit code?

Closing perspective

Exploit development is most useful when it remains connected to the underlying vulnerability analysis. The goal is not simply to make a payload execute. It is to translate a security finding into a reliable, testable implementation while preserving the reasoning that explains why the attack works.

That combination—root-cause analysis, controlled proof of concept, reliable implementation and clear provenance—is what turns an isolated vulnerability test into reusable security research.