Recommended resources
The freely available resources this lesson will direct you to.
- OffensiveCon23 - Samuel Groß & Carl Smith - Advancements in JavaScript Engine Fuzzingwww.youtube.com
- 39C3 - Demystifying Fuzzer Behaviourwww.youtube.com
- [PDF] Effective Fuzzing of Web Applications for Server-Side Vulnerabilitieswww.usenix.org
- [PDF] A systematic review of fuzzing techniques - GoSec@CS.SJTUgosec.sjtu.edu.cn
Hello again! It's great to continue our journey into the architecture of security products.
In our last lesson, we designed a sophisticated DAST crawler capable of mapping the attack surface of a modern, authenticated Single-Page Application. That crawler provides us with our "map of the battlefield": a comprehensive list of every URL, parameter, form, and interactive element.
Now, it's time to go on the offensive. This lesson addresses the next logical step in the DAST pipeline: designing the engine that systematically attacks this mapped surface. Our learning outcome is to: Design a DAST fuzzing engine, comparing mutation-based and generation-based approaches for test case generation.
We will architect the core component responsible for creating thousands of malicious or malformed inputs—a process known as fuzzing—to probe for vulnerabilities. We'll explore the two dominant design philosophies, mutation-based and generation-based fuzzing, and analyze their architectural trade-offs from a first-principles perspective.
1. The Anatomy of a Fuzzing Engine
First, let's establish what "fuzzing" means in our DAST context. At its heart, fuzzing is the art of automated software testing by providing invalid, unexpected, or random data as inputs to a program and monitoring for exceptions like crashes, failed assertions, or, in our case, security vulnerabilities.
A DAST fuzzing engine takes the attack surface map from our crawler and, for each identified input point (e.g., a URL parameter id=123, a form field username=test), generates a multitude of test cases designed to trigger a bug. The general architecture, as shown in the systematic review paper "A systematic review of fuzzing techniques," involves a test case generator, a delivery module (our HTTP client), and a bug detector (or "oracle"). Our focus today is on the test case generator.
A systematic review of fuzzing techniques
To frame our discussion, let's look at the paper 'A systematic review of fuzzing techniques'. It provides clear, foundational definitions for the core concepts we'll be building upon.
Please read section 2.2.2. Mutation-based and generation-based, which provides a concise, high-level distinction between the two main approaches. Then, skim section 3.1. Sample generation techniques, which introduces 'Random mutation' and 'Grammar representation'. This will give you the formal terminology for our architectural discussion.
As the paper outlines, there are two primary strategies for generating these test cases: mutating existing data or generating new data from a model. Let's design an engine for each approach.
2. Mutation-Based Fuzzing: The Brute-Force Approach
Mutation-based fuzzing, often called "dumb" fuzzing, is the simplest and oldest approach. The core idea is to take a known-good input, or "seed," and apply a series of simple, often random, transformations to it.
Architectural Design:
A mutation-based fuzzing engine is straightforward to design. It consists of:
- Seed Input Source: This component provides the initial valid inputs. For our DAST tool, this would be the default or example values discovered by the crawler (e.g., from
placeholderattributes or sample form submissions). - Mutator Library: A collection of functions that perform simple transformations. Common mutators include:
- Bit/Byte Flipping: Randomly changing bits in the input.
- String Appending/Prepending: Adding characters, especially known "bad" strings (
',",;,--,<script>), to the start or end of an input. - Keyword Replacement: Swapping known keywords with others (e.g.,
truewithfalse). - Magic Number Insertion: Inserting boundary values like
0,-1,MAX_INT, etc.
- Fuzzing Loop: A simple loop that pulls a seed, randomly selects one or more mutators from the library, applies them, and sends the resulting test case to the target application.
Trade-offs:

-
Pros:
- Simple and Fast: The logic is trivial to implement, and generating a test case is computationally cheap, allowing for very high throughput (millions of tests per minute).
- No Protocol Knowledge Needed: The engine doesn't need to understand what a "valid email" or a "JWT token" is. It just mangles bytes.
-
Cons:
- Shallow Coverage: Most mutations will result in syntactically invalid inputs that are immediately rejected by the application's parser (e.g., mutating a JSON object will likely break its structure). This prevents the fuzzer from reaching deeper business logic.
- The Havoc Effect: A small change at the beginning of a structured input can render the rest of it meaningless, drastically limiting the effectiveness of the mutation.
39C3 - Demystifying Fuzzer Behaviour
The talk 'Demystifying Fuzzer Behaviour' provides excellent first-principles insights into why these limitations exist. Even though it's not specific to web apps, the core concepts are universal.
Please watch these two key segments: 07:49 - 09:39: This part explains mutational and generative fuzzing from an intuitive, input-space perspective. 18:21 - 21:11: This segment on the 'havoc effect' is crucial. It explains why a simple mutation (changing one character) can have unexpectedly large and destructive consequences on how the rest of the input is interpreted, which is the primary weakness of 'dumb' mutation-based fuzzing against structured data.
For a modern DAST tool, a purely mutation-based engine is insufficient. It might find simple bugs in simple inputs but will fail against applications that expect highly structured data like XML, JSON, or serialized objects.
3. Generation-Based Fuzzing: The Intelligent Approach
Generation-based fuzzing, or "smart" fuzzing, takes the opposite approach. Instead of mutating a valid input, it generates a test case from scratch based on a model, grammar, or specification that describes the input's expected structure.
Architectural Design:
The core challenge in designing a generation-based engine is obtaining the input model.
- Model/Grammar Definition: The engine needs a representation of the input format. This can be a formal grammar (like a Backus-Naur Form for a programming language) or a structural template (like a schema for a JSON object).
- Test Case Generator: This component uses the model to construct new inputs. It can intelligently place malicious payloads into specific fields while keeping the overall structure valid.
For a DAST engine, manually creating grammars for every input on every website is impossible. Therefore, a modern DAST fuzzer must be able to infer the input model automatically. This is where the design becomes truly interesting.
[PDF] Effective Fuzzing of Web Applications for Server-Side Vulnerabilities
The paper 'Effective Fuzzing of Web Applications for Server-Side Vulnerabilities' details the design of ATROPOS, a fuzzer that excels at this inference. Its architecture provides a blueprint for an intelligent, generation-based DAST fuzzing engine.
This paper is central to our design. Please focus on these sections: Section 3.2: Advanced Feedback Mechanisms: This is the core of the design. Read through how the fuzzer infers required keys, expected values (by hooking string comparisons), values for regular expressions, and keys from HTML. This is the 'smart' part of our smart fuzzer. Section 3.4: Bug Oracles Beyond Memory Corruption: Skim this section. While we are focused on test case generation, it's important to understand how a DAST fuzzer detects bugs. The concept of using application errors and warnings as a signal (an 'oracle') is a key part of the feedback loop. Section 5.5: Ablation Study: Review Table 3 in this section. It's a powerful demonstration of how much each intelligent generation feature contributes to the overall bug-finding capability, reinforcing the value of this design.
Drawing from the ATROPOS design, our generation-based DAST fuzzer would include these components:
- Runtime Feedback Collector: A component, likely instrumenting a headless browser or a server-side agent, that hooks into the application's execution to gather intelligence. This is the "brain" of the operation.
- Key Inference Hook: Monitors accesses to input arrays (like PHP's
$_GET,$_POST) to discover required parameter keys. If the code checks for$_GET['page'], the fuzzer learns thatpageis a necessary key. - Value Inference Hook: Hooks string comparison functions. If the code executes
if ($page == 'login'), the fuzzer learns thatloginis a highly interesting value for thepagekey. - Regex Inference Hook: Hooks functions that execute regular expressions. It can extract the regex pattern and use a library to generate strings that match it.
- Key Inference Hook: Monitors accesses to input arrays (like PHP's
- Input Model Store: A database that stores the inferred knowledge for each endpoint (e.g., Endpoint
/user.phprequires keyidwhich expects an integer, and keyactionwhich expects one of['view', 'delete']). - Grammar-Aware Generator: Uses the stored input model to generate structurally valid test cases. For example, it will always generate a request with the required keys, using the interesting values it has learned. It can then strategically place payloads (e.g., SQLi strings) into the values, knowing the rest of the request is valid.
4. Advanced Generation and Hybrid Engines
The most sophisticated fuzzers combine generation and mutation. They use generation to handle the high-level structure and mutation for the low-level data.
Hybrid Architecture:
- Structure Generation: Use a generation-based approach to create a syntactically valid input (e.g., a valid XML document or a JSON object conforming to an inferred schema).
- Field-Level Mutation: Once the valid structure is in place, apply mutation-based techniques to the values of individual fields. This focuses the "dumb" randomness where it's most effective, without breaking the entire input structure.
The Fuzzilli fuzzer, while for JavaScript engines, showcases another powerful generation technique: using an Intermediate Language (IL).
OffensiveCon23 - Samuel Groß & Carl Smith - Advancements in JavaScript Engine Fuzzing
The talk 'Advancements in JavaScript Engine Fuzzing' explains how Fuzzilli uses a custom IL to construct complex, valid JavaScript code. This concept of using a higher-level abstraction to generate test cases is a powerful pattern in generation-based fuzzer design.
Please watch these two parts: 00:50 - 03:04: Focus on the core idea of the 'FuzzIL' Intermediate Language. Understand how it makes it easy to generate and mutate code in a meaningful way. 16:35 - 20:34: Pay attention to the concept of 'Program Templates'. This is a form of generation that incorporates human expertise, allowing the fuzzer to create test cases that match a known-vulnerable code shape. This is an excellent example of human-assisted smart fuzzing.
For a DAST tool, a similar idea could be to define templates for common attacks. For example, a "second-order SQLi" template could instruct the fuzzer to first generate a request to create a user with a malicious username, and then generate a second request to an admin panel that might display that username without proper sanitization.
Conclusion
In this lesson, we designed the core of a DAST scanner: the fuzzing engine. We explored the fundamental trade-offs between speed and intelligence that drive the architecture of a test case generator.
Key Takeaways:
- Fuzzing engines in DAST tools are responsible for generating test cases to send to the application endpoints discovered by the crawler.
- Mutation-based fuzzing is simple and fast but often ineffective against applications with structured inputs due to its "dumb" nature and the havoc effect.
- Generation-based fuzzing is more complex but far more effective. For DAST, the key architectural challenge is designing an engine that can automatically infer the expected input structure through runtime feedback mechanisms.
- The most powerful fuzzing engines use a hybrid approach, leveraging generation to maintain correct input structure and applying mutation at the field level to explore vulnerabilities.
- Advanced techniques like using an Intermediate Language or programmatic templates allow human expertise to be encoded into the fuzzer, guiding it toward more complex and specific bug classes.
Preview of the Next Lesson:
We have now designed the two main components of a DAST tool: the crawler (static analysis of a running app) and the fuzzer (dynamic testing). We have also previously designed a SAST tool that analyzes source code. In our next and final lesson for this module, we will address the outcome: "Evaluate the architectural trade-offs across SAST, DAST, and SCA regarding coverage, accuracy, and CI/CD integration speed." We will bring all these concepts together, compare their strengths and weaknesses, and introduce Software Composition Analysis (SCA) to complete our overview of Application Security Testing tools.