VYPR
High severity7.5NVD Advisory· Published May 29, 2026· Updated May 29, 2026

CVE-2026-47123

CVE-2026-47123

Description

FreeScout is a free help desk and shared inbox built with PHP's Laravel framework. Prior to 1.8.220, the email processing pipeline in FreeScout's FetchEmails command has two code paths for identifying agent (user) replies based on In-Reply-To / References headers. The notification reply path (notify-{thread_id}-{user_id}-...) extracts thread_id and user_id directly from the Message-ID without HMAC verification. An external attacker who can spoof the From address of a helpdesk agent can inject messages that FreeScout processes as legitimate agent replies — which are then automatically forwarded to customers via the legitimate SMTP server. This vulnerability is fixed in 1.8.220.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

FreeScout prior to 1.8.220 lacks HMAC verification in notification reply Message-ID, allowing agent impersonation via spoofed emails.

Vulnerability

FreeScout's FetchEmails command in versions prior to 1.8.220 processes incoming emails to identify agent replies. The notification reply path (matching notify-{thread_id}-{user_id}-... in the Message-ID) extracts thread_id and user_id directly from the In-Reply-To or References headers without HMAC verification [1]. In contrast, the customer reply path uses an HMAC hash derived from APP_KEY. This inconsistency allows an attacker to forge agent replies by crafting a Message-ID that matches the notification pattern.

Exploitation

An external attacker who can spoof the From address of a helpdesk agent can send an email to the helpdesk's mailbox with a crafted In-Reply-To header containing notify-{thread_id}-{user_id}-{timestamp}@domain. FreeScout's processMessage() function (lines 585–599) will parse this and treat it as a legitimate agent reply [1]. The attacker does not need authentication or prior access; only the ability to send an email with a spoofed sender address.

Impact

The forged agent reply is automatically forwarded to the customer via the legitimate SMTP server, as the UserReplied event triggers SendReplyToCustomer [1]. This enables an attacker to inject arbitrary content into customer conversations, potentially leading to information disclosure, phishing, or reputational damage. The attacker gains the ability to impersonate any agent whose email address they can spoof.

Mitigation

The vulnerability is fixed in FreeScout version 1.8.220 [1]. The fix adds HMAC verification to the notification reply path, requiring a 16-character hash derived from the thread ID [2]. Users should upgrade to 1.8.220 or later. No workaround is available for earlier versions.

AI Insight generated on May 29, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected products

2

Patches

2
d902f1903821

Check hash in replies to user email notifications - GHSA-6r38-6mcf-2ww3

2 files changed · +24 3
  • app/Console/Commands/FetchEmails.php+23 2 modified
    @@ -582,11 +582,32 @@ public function processMessage($message, $message_id, $mailbox, $mailboxes, $ext
                         continue;
                     }
                     // Is it a message from Customer or User replied to the notification
    -                preg_match('/^'.$this->formatMessageIdPrefix(\MailHelper::MESSAGE_ID_PREFIX_NOTIFICATION)."\-(\d+)\-(\d+)\-/", $prev_message_id, $m);
    +                preg_match('/^'.$this->formatMessageIdPrefix(\MailHelper::MESSAGE_ID_PREFIX_NOTIFICATION)."\-(\d+)\-(\d+)\-([a-z0-9]+)/", $prev_message_id, $m);
     
                     if (!$is_bounce && !empty($m[1]) && !empty($m[2])) {
    -                    // Reply from User to the notification
    +                    // Reply from User to the notification.
    +                    $prev_thread_id = '';
    +                    // Check hash.
    +                    // https://github.com/freescout-help-desk/freescout/security/advisories/GHSA-6r38-6mcf-2ww3
    +                    if (!empty($m[1]) && !empty($m[3])) {
    +                        $message_id_hash = $m[3];
    +                        if (strlen($message_id_hash) == 16) {
    +                            if ($message_id_hash == \MailHelper::getMessageIdHash($m[1])) {
    +                                $prev_thread_id = $m[1];
    +                            }
    +                        } else {
    +                            // No backward compatibility for security reasons.
    +                            //$prev_thread_id = $m[1];
    +                        }
    +                    }
    +
    +                    if (!$prev_thread_id) {
    +                        $this->logError('Invalid hash in the Message-ID: '.$prev_message_id);
    +                        $this->setSeen($message, $mailbox);
    +                        return;
    +                    }
                         $prev_thread = Thread::find($m[1]);
    +
                         $user_id = $m[2];
                         $user = User::find($user_id);
                         $message_from_customer = false;
    
  • app/Jobs/SendNotificationToUsers.php+1 1 modified
    @@ -123,7 +123,7 @@ public function handle()
                     }
                 }
     
    -            $message_id = \App\Misc\Mail::MESSAGE_ID_PREFIX_NOTIFICATION.'-'.$last_thread->id.'-'.$user->id.'-'.time().'@'.$mailbox->getEmailDomain();
    +            $message_id = \App\Misc\Mail::MESSAGE_ID_PREFIX_NOTIFICATION.'-'.$last_thread->id.'-'.$user->id.'-'.\MailHelper::getMessageIdHash($last_thread->id).'@'.$mailbox->getEmailDomain();
                 $headers['Message-ID'] = $message_id;
     
                 // If this is notification on message from customer, set customer as sender name
    
cd128c568320

Merge pull request #1001 from TimoStramann/patch-1

https://github.com/freescout-help-desk/freescoutFreeScoutJan 15, 2021via body-scan-shorthand
1 file changed · +7 7
  • resources/lang/de.json+7 7 modified
    @@ -744,18 +744,18 @@
         "Auto Replies": "Automatische Antworten",
         "Add a phone number": "Eine Telefonnummer hinzufügen",
         "Company": "Unternehmen",
    -    "Social Profiles": "Social-Media Profile",
    -    "Add a social profile": "Eine Social-Media Profil hinzufügen",
    +    "Social Profiles": "Social-Media Profil",
    +    "Add a social profile": "Ein Social-Media-Profil hinzufügen",
         "Age": "Alter",
         "Gender": "Geschlecht",
    -    "Male": "Männlich",
    -    "Female": "Weiblich",
    +    "Male": "männlich",
    +    "Female": "weiblich",
         "Country": "Land",
         "Address": "Adresse",
    -    "State": "Staat",
    +    "State": "Bundesland",
         "City": "Stadt",
         "ZIP": "Postleitzahl",
         "Notes": "Notizen",
         "Access denied": "Zugang verweigert",
    -    "Deleting :name will deactivate workflows they are tied to and assign their conversations to:": "Beim Löschen dieses Benutzers werden auch die ihm zugeordneten Workflows deaktiviert und seine Gespräche werden zugewiesen an:"
    -}
    \ No newline at end of file
    +    "Deleting :name will deactivate workflows they are tied to and assign their conversations to:": "Durch das Löschen von :name werden diesem Benutzer zugeordnete Workflows deaktiviert und neue Gespräche werden zugeordnet zu:"
    +}
    

Vulnerability mechanics

Root cause

"Missing HMAC verification on the notification reply Message-ID path allows an attacker to inject arbitrary thread_id and user_id values."

Attack vector

An external attacker who can spoof the From address of a helpdesk agent (no SPF/DKIM/DMARC validation is performed by FreeScout) sends an email to the helpdesk IMAP mailbox with a crafted `In-Reply-To` header such as `<notify-1-1-0@attacker.com>`. The regex in `processMessage()` extracts `thread_id=1` and `user_id=1` without any HMAC verification, causing FreeScout to treat the email as a legitimate agent reply. The forged body is then forwarded to the customer via the helpdesk's own SMTP server with valid DKIM/SPF, enabling phishing and agent impersonation [ref_id=1].

Affected code

The vulnerability resides in `app/Console/Commands/FetchEmails.php` within the `processMessage()` function, specifically the notification reply path at lines 585–599. The notification Message-ID format (`FS_notify-{thread_id}-{user_id}-...`) was parsed without HMAC verification, unlike the customer reply path which correctly validates a hash derived from `APP_KEY` via `MailHelper::getMessageIdHash()` [ref_id=1]. The patch modifies the regex to capture a third hash segment and adds a hash check before accepting the thread_id [patch_id=3107109].

What the fix does

The patch [patch_id=3107109] modifies the regex in `FetchEmails.php` to capture a third hash segment (`([a-z0-9]+)`) from the notification Message-ID. It then computes `MailHelper::getMessageIdHash($m[1])` (an HMAC based on `APP_KEY`) and compares it to the extracted hash; only on a match is the `$prev_thread_id` accepted. If the hash is missing or invalid, the message is logged as an error and skipped. The corresponding change in `SendNotificationToUsers.php` replaces the timestamp in the generated Message-ID with this hash, ensuring that only notifications created by the legitimate server carry a valid hash.

Preconditions

  • inputAttacker must know a valid thread_id (enumerable via open tracking endpoint, per GHSA-qjr9-6v9q-3r72)
  • inputAttacker must know the agent's email address (enumerable via password reset, per GHSA-jvmv-2qcp-7855)
  • networkAttacker must be able to send email with a spoofed From header (achievable when the target domain does not enforce strict DMARC p=reject)
  • configFreeScout performs no SPF/DKIM/DMARC validation on incoming email

Generated on May 29, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

2

News mentions

0

No linked articles in our index yet.