From 1bd44f6b1f220cfb0c838bcfcaacaba4ea542b63 Mon Sep 17 00:00:00 2001 From: Ilia Ross Date: Thu, 15 Oct 2026 17:47:58 +0200 Subject: [PATCH] Fix archive extraction for directory-restricted users This PR fixes archive extraction bypassing Upload and Download directory restrictions. Automatic extraction is hidden and rejected when uploads are limited to specific directories. Unrestricted users keep the existing extraction behavior. ANT-2026-TPYPGDF0: Reported by Adam in email on October 11, 2026 at 01:05. --- CHANGELOG.md | 1 + updown/index.cgi | 14 ++++++++------ updown/lang/en | 1 + updown/upload.cgi | 2 ++ 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e67ee5ca..7b992955e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ * Fix two-factor authentication validation to use the Webmin configuration and data directories passed by the caller * Fix HTML escaping of module, script and session fields on the Webmin Actions Log details page * Fix mailbox attachment option injection +* Fix archive extraction bypassing Upload and Download directory restrictions * Fix path traversal and command injection in parameterized Custom Commands file editors * Fix MySQL/MariaDB manual editor ACL bypass * Fix the logged in users list in Users and Groups module showing nobody when `who` prints ISO dates under a non-C locale or `sshd pts/N` lines from the logind backend [forum.virtualmin.com/t/138162](https://forum.virtualmin.com/t/138162) diff --git a/updown/index.cgi b/updown/index.cgi index 1c390ea9c..b9652af39 100755 --- a/updown/index.cgi +++ b/updown/index.cgi @@ -204,12 +204,14 @@ if ($can_upload) { $upload_group, $form) ] ])); } - # Unzip files - print &ui_table_row($text{'index_zip'}, - &ui_radio("zip", 0, - [ [ 2, $text{'index_zipyes'} ], - [ 1, $text{'yes'} ], - [ 0, $text{'no'} ] ])); + if (&can_write_file("/")) { + # Offer extraction only when uploads are not directory-restricted + print &ui_table_row($text{'index_zip'}, + &ui_radio("zip", 0, + [ [ 2, $text{'index_zipyes'} ], + [ 1, $text{'yes'} ], + [ 0, $text{'no'} ] ])); + } # Email notification print &ui_table_row($text{'index_email2'}, diff --git a/updown/lang/en b/updown/lang/en index 8d2df08c8..af197a2e4 100644 --- a/updown/lang/en +++ b/updown/lang/en @@ -62,6 +62,7 @@ upload_extracted=, and extracted files .. upload_deleted=, and extracted files before deleting archive .. upload_notcomp=Not a ZIP or TAR file upload_eemail=Missing email address to notify +upload_eextractacl=Archive extraction is not allowed when uploads are limited to specific directories upload_saving=Saving file $1 .. upload_saved=.. wrote $1 upload_unzipping=Extracting file $1 .. diff --git a/updown/upload.cgi b/updown/upload.cgi index fc3827aa8..5412e0f4d 100755 --- a/updown/upload.cgi +++ b/updown/upload.cgi @@ -49,6 +49,8 @@ for($i=0; defined($in{"upload$i"}); $i++) { } } $found || &error($text{'upload_enone'}); +$in{'zip'} && !&can_write_file("/") && + &error($text{'upload_eextractacl'}); &can_write_file($in{'dir'}) || &error(&text('upload_eaccess', "$in{'dir'}", $!));