Skip to main content
Vulnerability Database/CVE-2026-94028

CVE-2026-94028: Mealie Recipe Action SSRF Vulnerability

CVE-2026-94028 is a server-side request forgery flaw in mealie-recipes Mealie affecting versions up to 3.25.1. Attackers can exploit the Recipe Action Trigger to perform unauthorized requests. This article covers technical details, affected versions, and how to mitigate the risk.

Published:

CVE-2026-94028 Overview

CVE-2026-94028 is a Server-Side Request Forgery (SSRF) vulnerability in Mealie, an open-source self-hosted recipe manager. The flaw affects versions up to and including 3.25.1 and resides in the payload.model_dump function within mealie/routes/households/controller_group_recipe_actions.py, part of the Recipe Action Trigger component. An authenticated remote attacker can manipulate the url argument to coerce the server into issuing HTTP requests to arbitrary destinations, including internal network resources. The issue is tracked as [CWE-918] (Server-Side Request Forgery). A public exploit has been disclosed. The maintainers addressed the flaw in Mealie 3.26.0 via commit fb221afa258c8dd2c4ac95b1996c33ef9db3f477.

Critical Impact

An authenticated attacker can abuse Mealie's server-initiated HTTP features to reach internal network resources, cloud metadata endpoints, and other services not intended for external access.

Affected Products

  • mealie-recipes Mealie versions up to 3.25.1
  • Component: Recipe Action Trigger (controller_group_recipe_actions.py)
  • Fixed in Mealie 3.26.0

Discovery Timeline

  • 2026-09-20 - CVE-2026-94028 published to the National Vulnerability Database
  • 2026-09-21 - Last updated in NVD database

Technical Details for CVE-2026-94028

Vulnerability Analysis

The vulnerability exists in Mealie's Recipe Action Trigger feature, which allows configured actions to invoke external URLs. The payload.model_dump handler in mealie/routes/households/controller_group_recipe_actions.py accepts a user-supplied url value and forwards it to the server's HTTP client without validating that the target resolves to a public address. An authenticated user with permission to trigger recipe actions can supply a URL pointing at an internal IP range, loopback interface, or cloud metadata service. The server then performs the request on the attacker's behalf and may return response data or side-effects that would otherwise be unreachable.

Root Cause

Mealie's server-initiated HTTP paths — including recipe imports, image downloads, webhooks, and recipe actions — historically did not filter destination hosts. The Recipe Action Trigger passed the url field straight through the request pipeline. DNS resolution and address-family checks against private, loopback, link-local, and carrier-grade NAT ranges were absent, and there was no protection against DNS rebinding between the validation and fetch steps.

Attack Vector

Exploitation requires network reachability to the Mealie instance and low-privilege authenticated access. The attacker configures a recipe action with a url value referencing an internal target such as http://169.254.169.254/latest/meta-data/ (AWS metadata), http://127.0.0.1:<port> for co-located services, or an RFC1918 host on the same network segment. When the action fires, the Mealie server performs the outbound request from its own network position.

The upstream fix hardens all server-initiated HTTP paths and introduces host allow and disallow lists.

python
    HTTP_ALLOW_LIST: str = ""
    """Comma-separated hosts or CIDRs that server-initiated requests (recipe scraping, webhooks,
    recipe actions) may reach even if they resolve to an otherwise-blocked private/internal address.
    Use this to allow a known internal server."""

    HTTP_DISALLOW_LIST: str = ""
    """Comma-separated hosts or CIDRs that server-initiated requests may never reach, even if public.
    Takes precedence over `HTTP_ALLOW_LIST`."""

    @property
    def http_allow_list(self) -> list[str]:
        return [host.strip() for host in self.HTTP_ALLOW_LIST.split(",") if host.strip()]

    @property
    def http_disallow_list(self) -> list[str]:
        return [host.strip() for host in self.HTTP_DISALLOW_LIST.split(",") if host.strip()]

Source: GitHub Commit fb221af

Detection Methods for CVE-2026-94028

Indicators of Compromise

  • Outbound HTTP requests from the Mealie service to private address ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16), loopback (127.0.0.0/8), link-local (169.254.0.0/16), or carrier-grade NAT (100.64.0.0/10).
  • Recipe action records in the Mealie database whose url field targets internal hostnames, IP literals, or cloud metadata endpoints.
  • Unexpected requests originating from the Mealie container or host to cloud instance metadata services such as 169.254.169.254.

Detection Strategies

  • Inspect application logs and reverse-proxy egress logs for requests initiated by the Mealie process to non-public destinations.
  • Audit stored recipe actions and webhooks for URLs that reference internal infrastructure or IP-literal hosts.
  • Correlate authentication events with subsequent recipe-action creation to identify low-privilege accounts probing the SSRF surface.

Monitoring Recommendations

  • Restrict egress from the Mealie host at the network layer and alert on any denied connections to internal ranges.
  • Enable DNS query logging for the Mealie service and flag lookups resolving to private addresses.
  • Ingest Mealie application logs into a centralized log platform and build alerts on repeated outbound-request failures tied to a single user.

How to Mitigate CVE-2026-94028

Immediate Actions Required

  • Upgrade Mealie to version 3.26.0 or later, which contains commit fb221afa258c8dd2c4ac95b1996c33ef9db3f477.
  • Review and remove existing recipe actions and webhooks that reference internal hostnames or IP addresses.
  • Rotate any secrets that may have been exposed via cloud metadata endpoints reachable from the Mealie host.

Patch Information

The fix is delivered in Mealie release v3.26.0. It introduces address-family checks that block requests to private, loopback, link-local, and carrier-grade NAT ranges (including IPv6 equivalents), and adds HTTP_ALLOW_LIST and HTTP_DISALLOW_LIST settings to override defaults where required. Additional context is available in Pull Request #7914 and Issue #7831.

Workarounds

  • Place Mealie in an egress-restricted network segment that cannot reach internal services or cloud metadata endpoints.
  • Route outbound HTTP through a filtering proxy that denies requests to RFC1918, loopback, and link-local destinations.
  • Restrict recipe-action and webhook creation to trusted administrator accounts until the upgrade is applied.
bash
# Configuration example: harden egress after upgrading to 3.26.0
# Explicitly deny sensitive internal targets even if they become publicly routable
HTTP_DISALLOW_LIST="169.254.169.254,metadata.google.internal,10.0.0.0/8"

# Allow only a specific internal recipe source, if required
HTTP_ALLOW_LIST="recipes.internal.example.com"

Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.