From cf432879a14568c4bb44cd2f9e5a9bd0e168edc1 Mon Sep 17 00:00:00 2001 From: Ilia Ross Date: Thu, 23 Apr 2026 18:00:07 +0200 Subject: [PATCH] Fix unsafe mailbox attachment handling --- mailboxes/detach.cgi | 17 +++++++++++++++-- mailboxes/detachall.cgi | 5 +++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/mailboxes/detach.cgi b/mailboxes/detach.cgi index a9bc8af6e..ea419b7c8 100755 --- a/mailboxes/detach.cgi +++ b/mailboxes/detach.cgi @@ -68,8 +68,21 @@ if ($in{'scale'}) { else { # Just output the attachment print "X-no-links: 1\n"; + $fn = $attach->{'filename'} ? &decode_mimewords($attach->{'filename'}) + : "attachment"; + $fn =~ s/[\r\n\0"\\\/]//g; + $fn ||= "attachment"; @download = split(/\t+/, $config{'download'}); - if ($in{'type'}) { + if ($attach->{'type'} =~ /^image\/svg(\+xml)?/i || + $in{'type'} =~ /^image\/svg(\+xml)?/i || + $fn =~ /\.svgz?$/i) { + # SVG can execute scripts when served from the Webmin origin. + print "Content-Disposition: Attachment; filename=\"$fn\"\n" + if ($in{'save'}); + print "Content-type: text/plain\n\n"; + print $attach->{'data'}; + } + elsif ($in{'type'}) { # Display as a specific MIME type print "Content-type: $in{'type'}\n\n"; print $attach->{'data'}; @@ -78,7 +91,7 @@ else { # Auto-detect type if ($in{'save'}) { # Force download - print "Content-Disposition: Attachment; filename=\"$attach->{'filename'}\"\n"; + print "Content-Disposition: Attachment; filename=\"$fn\"\n"; } if ($attach->{'type'} eq 'message/delivery-status') { print "Content-type: text/plain\n\n"; diff --git a/mailboxes/detachall.cgi b/mailboxes/detachall.cgi index 2f863406d..6a6a8b825 100755 --- a/mailboxes/detachall.cgi +++ b/mailboxes/detachall.cgi @@ -37,6 +37,11 @@ foreach $a (@attach) { else { $fn = "file".(++$n).".".&type_to_extension($a->{'type'}); } + $fn =~ s/[\r\n\0]//g; + $fn =~ s/\\/\//g; + $fn =~ s/^.*\///g; + $fn =~ /^\.+$/ && ($fn = ""); + $fn ||= "file".(++$n); # Write the file &open_tempfile(FILE, ">$temp/$fn", 0, 1);