VYPR
Critical severity9.1NVD Advisory· Published Apr 21, 2026· Updated Apr 23, 2026

CVE-2026-40496

CVE-2026-40496

Description

FreeScout is a free self-hosted help desk and shared mailbox. Prior to version 1.8.213, attachment download tokens are generated using a weak and predictable formula: md5(APP_KEY + attachment_id + size). Since attachment_id is sequential and size can be brute-forced in a small range, an unauthenticated attacker can forge valid tokens and download any private attachment without credentials. Version 1.8.213 fixes the issue.

Affected products

1

Patches

1
dbdf8f2260b4

Change attachment token generation algorythm

3 files changed · +71 3
  • app/Attachment.php+15 2 modified
    @@ -23,6 +23,13 @@ class Attachment extends Model
     
         const MIME_TYPE_MAX_LENGTH = 127;
     
    +    // For backward compatibility.
    +    const TOKEN_TYPE_LEGACY = 1;
    +    // For backward compatibility.
    +    const TOKEN_TYPE_MD5    = 2;
    +    // Current way.
    +    const TOKEN_TYPE_SHA256 = 3;
    +
         // https://github.com/Webklex/laravel-imap/blob/master/src/IMAP/Attachment.php
         public static $types = [
             'message'     => self::TYPE_MESSAGE,
    @@ -121,6 +128,7 @@ public static function create($file_name, $mime_type, $type, $content, $uploaded
             $attachment->mime_type = $mime_type;
             $attachment->type = $type;
             $attachment->embedded = $embedded;
    +        $attachment->token_type = self::TOKEN_TYPE_SHA256;
             $attachment->save();
     
             $file_info = self::saveFileToDisk($attachment, $file_name, $content, $uploaded_file);
    @@ -271,8 +279,13 @@ public function url()
          */
         public function getToken()
         {
    -        // \Hash::make() may contain . and / symbols which may cause problems.
    -        return md5(config('app.key').$this->id.$this->size);
    +        if ($this->token_type == self::TOKEN_TYPE_MD5) {
    +            // Backward compatibility.
    +            // \Hash::make() may contain . and / symbols which may cause problems.
    +            return md5(config('app.key').$this->id.$this->size);
    +        } else {
    +            return hash_hmac('sha256', $this->id.$this->size.$this->file_name, config('app.key'));
    +        }
         }
     
         /**
    
  • app/Http/Controllers/OpenController.php+1 1 modified
    @@ -172,7 +172,7 @@ public function downloadAttachment($dir_1, $dir_2, $dir_3, $file_name, Request $
             }
     
             // Only allow download if the attachment is public or if the token matches the hash of the contents
    -        if ($token != $attachment->getToken() && (bool)$attachment->public !== true) {
    +        if ($token != $attachment->getToken() && $attachment->token_type != Attachment::TOKEN_TYPE_LEGACY) {
                 return \Helper::denyAccess();
             }
     
    
  • database/migrations/2026_04_04_010101_add_token_type_column_to_attachments_table.php+55 0 added
    @@ -0,0 +1,55 @@
    +<?php
    +
    +use Illuminate\Database\Migrations\Migration;
    +use Illuminate\Database\Schema\Blueprint;
    +use Illuminate\Support\Facades\Schema;
    +
    +class AddTokenTypeColumnToAttachmentsTable extends Migration
    +{
    +    /**
    +     * Run the migrations.
    +     *
    +     * @return void
    +     */
    +    public function up()
    +    {
    +        // We can't use Attachement::TOKEN_TYPE_... constants here
    +        // as they may not be available during the process of updating the application.
    +        
    +        Schema::table('attachments', function (Blueprint $table) {
    +            $table->unsignedTinyInteger('token_type')->default(3);
    +        });
    +
    +        DB::table('attachments')
    +            ->where('public', true)
    +            ->update(['token_type' => 1]);
    +
    +        DB::table('attachments')
    +            ->where('public', false)
    +            ->update(['token_type' => 2]);
    +
    +        Schema::table('attachments', function (Blueprint $table) {
    +            $table->dropColumn('public');
    +        });
    +    }
    +
    +    /**
    +     * Reverse the migrations.
    +     *
    +     * @return void
    +     */
    +    public function down()
    +    {
    +        Schema::table('attachments', function (Blueprint $table) {
    +            $table->boolean('public')->default(false);
    +        });
    +
    +        DB::table('attachments')
    +            ->where('token_type', 1)
    +            ->update(['public' => true]);
    +
    +        Schema::table('attachments', function (Blueprint $table) {
    +            $table->dropColumn('token_type');
    +        });
    +    }
    +}
    

Vulnerability mechanics

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

References

3

News mentions

0

No linked articles in our index yet.