CVE-2025-53833

Recipe for Disaster: Cooking up RCE in LaRecipe

Alon Barad
Alon Barad
Software Engineer

Jan 19, 2026·5 min read

Executive Summary (TL;DR)

LaRecipe < 2.8.1 tries to 'fix' relative anchor links by prepending the current request URI. Unfortunately, it uses the full URI including the query string. By appending a Blade template payload (e.g., `?{{system('id')}}`) to the URL, an attacker can trick the server into rendering and executing malicious code. CVSS 10.0. Update immediately.

A critical Server-Side Template Injection (SSTI) vulnerability in the LaRecipe documentation package allows unauthenticated attackers to execute arbitrary code via simple query string manipulation.

The Hook: Documentation is Dangerous

We often treat documentation as a static, boring artifact—a collection of markdown files that developers write reluctantly and users read even more reluctantly. But in the modern Laravel ecosystem, documentation tools like LaRecipe are dynamic applications in their own right. They parse Markdown, highlight code, and—crucially—render Blade templates. That last part is where things get spicy.

LaRecipe is designed to make writing documentation easy. It takes your standard markdown files and wraps them in a beautiful UI. To do this, it processes the text, handles routing, and renders the output. It's a classic case of "complexity is the enemy of security."

In CVE-2025-53833, we aren't looking at a complex buffer overflow or a race condition that requires perfect timing. We are looking at a logic error so simple, so face-palmingly obvious in hindsight, that it reminds us why sanitizing input is the golden rule of web security. The vulnerability allows an unauthenticated attacker to turn a harmless documentation page into a remote shell with a single GET request.

The Flaw: A Good Intention Gone Wrong

The road to RCE is paved with good intentions. In this case, the developer wanted to solve a common annoyance: broken anchor links. When you write markdown, you often use relative links like [Setup](#setup) to jump to a specific section on the page. However, depending on how the routing is set up, these relative links might not behave as expected if the base URL isn't clear.

To fix this, LaRecipe implemented a replaceLinks method in src/Models/Documentation.php. The logic was straightforward: find any relative link starting with # and prepend the current page's URL to make it absolute. This ensures that clicking #setup actually takes the browser to http://site.com/docs/1.0#setup.

The fatal flaw was in how the application determined the "current page's URL." The code grabbed the entire request URI—path, parameters, and all—and blindly concatenated it into the HTML content before passing it to the template engine. This is the equivalent of taking a stranger's handwriting, photocopying it onto a check, and handing it to the bank teller without looking at what was written.

The Code: The Smoking Gun

Let's look at the vulnerable code in src/Models/Documentation.php. The method replaceLinks takes the raw content and performs a string replacement. Here is the offending line:

// Vulnerable Code
public static function replaceLinks($version, $content)
{
    // ...
    // The application grabs the FULL URI, including query strings.
    $content = str_replace('"#', '"'.request()->getRequestUri().'#', $content);
    // ...
    return $content;
}

The function request()->getRequestUri() returns the complete URI. If I visit /docs/1.0?foo=bar, the function returns /docs/1.0?foo=bar. If the markdown content contains <a href="#section">, the code transforms it into:

<a href="/docs/1.0?foo=bar#section">

Because this content is subsequently rendered by Laravel's Blade engine, any Blade syntax injected into that query string gets executed. The fix was embarrassingly simple: swap getRequestUri() for getPathInfo(), which returns only the path component, ignoring the query string.

// The Fix (Commit c1d0d56)
- $content = str_replace('"#', '"'.request()->getRequestUri().'#', $content);
+ $content = str_replace('"#', '"'.request()->getPathInfo().'#', $content);

The Exploit: From Query String to Shell

Exploiting this requires zero authentication and zero fancy tools. You just need a web browser or curl. The goal is to inject a Blade echo statement {{ ... }} into the query string. When the server processes the page, it sees our payload as part of the HTML structure it constructed to "fix" the links, and the Blade engine evaluates it.

Here is the attack chain visualized:

To pull this off, the targeted documentation page must actually have at least one internal anchor link (e.g., #introduction) for the str_replace to trigger. Since almost all documentation pages have headers that generate these anchors, the attack surface is effectively "every page."

The Payload:

curl "http://target.com/docs/1.0?{{system('cat /etc/passwd')}}"

If successful, the response body won't just contain the documentation; it will contain the contents of /etc/passwd rendered right inside the href attribute or seemingly dumped onto the page depending on where the anchor tag sits.

The Impact: Why You Should Panic

This is a CVSS 10.0 for a reason. It is unauthenticated Remote Code Execution. If your application is running as www-data, the attacker is now www-data. If you are running as root (please don't do that), the attacker owns the box.

In a Laravel context, RCE is particularly devastating. An attacker can:

  1. Read .env: Steal database credentials, AWS keys, and APP_KEYs.
  2. Access the Database: Using the stolen credentials, dump user data.
  3. Persist Access: Write a permanent webshell to public/shell.php.
  4. Lateral Movement: Use the compromised server to scan internal networks.

The EPSS score is already climbing past 16%, indicating that automated scanners and botnets are likely already looking for this. It's low-hanging fruit: a simple GET request that yields a shell.

The Fix: Stop the Bleeding

If you are using saleem-hadad/larecipe, check your composer.lock immediately. If the version is below 2.8.1, you are vulnerable.

Primary Mitigation: Update the package via Composer:

composer update saleem-hadad/larecipe

Emergency Mitigation (WAF): If you cannot deploy code right now, configure your WAF (Cloudflare, AWS WAF, ModSecurity) to block any request query strings containing {{ or }}. While this won't fix the underlying bug, it will block the most obvious Blade injection attempts.

Developer Takeaway: Never, ever, ever trust request()->getRequestUri() or $_SERVER['REQUEST_URI'] inside a context that will be passed to a template engine or an eval() equivalent. Always treat the URL as hostile user input.

Fix Analysis (1)

Technical Appendix

CVSS Score
10.0/ 10
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H
EPSS Probability
16.76%
Top 5% most exploited

Affected Systems

LaRecipe Documentation Package < 2.8.1Laravel applications using LaRecipe

Affected Versions Detail

Product
Affected Versions
Fixed Version
LaRecipe
Saleem Hadad
< 2.8.12.8.1
AttributeDetail
CWE IDCWE-1336 (SSTI)
CVSS v3.110.0 (Critical)
Attack VectorNetwork (HTTP GET)
EPSS Score16.76%
Exploit StatusPoC Available
ImpactRemote Code Execution
CWE-1336
Server-Side Template Injection

Improper Neutralization of Special Elements Used in a Template Engine

Vulnerability Timeline

Vulnerability Discovered & Reported
2025-07-14
Vendor Released Patch v2.8.1
2025-07-14
PoC & Nuclei Templates Published
2025-07-15

Subscribe to updates

Get the latest CVE analysis reports delivered to your inbox.