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
1Patches
1dbdf8f2260b4Change 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
3News mentions
0No linked articles in our index yet.