CVE-2024-23822: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal') (CWE-22)
value: Thruk is a multibackend monitoring webinterface.  Prior to 3.12, the Thruk web monitoring application presents a vulnerability in a file upload form that allows a threat actor to arbitrarily upload files to the server to any path they desire and have permissions for. This vulnerability is known as Path Traversal or Directory Traversal. Version 3.12 fixes the issue.

CWE description: The product uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the product does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.
CVSS v3 base score: 9.8
Published: 2024-01-29T16:15Z
Repository: https://github.com/sni/Thruk
Language: Perl
Files changed: plugins/plugins-available/panorama/lib/Thruk/Controller/panorama.pm
Commit date: 2024-01-26 09:28:23 +0100
Commit message: panorama: validate input folder for image uploads

Diff: diff --git a/plugins/plugins-available/panorama/lib/Thruk/Controller/panorama.pm b/plugins/plugins-available/panorama/lib/Thruk/Controller/panorama.pm
index 1800e6aaf..0072aba9d 100644
--- a/plugins/plugins-available/panorama/lib/Thruk/Controller/panorama.pm
+++ b/plugins/plugins-available/panorama/lib/Thruk/Controller/panorama.pm
@@ -2,10 +2,11 @@ package Thruk::Controller::panorama;
 
 use warnings;
 use strict;
 use Carp qw/confess/;
 use Cpanel::JSON::XS qw/decode_json/;
+use Cwd ();
 use Data::Dumper qw/Dumper/;
 use Encode qw(encode_utf8);
 use File::Copy qw/move copy/;
 use IO::Socket::INET ();
 use Module::Load qw/load/;
@@ -730,11 +731,18 @@ sub _task_upload {
         $c->stash->{text} = Thruk::Utils::Filter::json_encode({ 'msg' => 'fileupload is disabled in demo mode.', success => Cpanel::JSON::XS::false });
         return;
     }
 
     my $upload = $c->req->uploads->{$type};
-    my $folder = $c->stash->{'usercontent_folder'}.'/'.$location;
+    my $folder = Cwd::abs_path($c->stash->{'usercontent_folder'}.'/'.$location);
+
+    # make sure requested folder is below the usercontent folder
+    if(CORE::index(Cwd::abs_path($c->stash->{'usercontent_folder'}), $folder) != 0) {
+        # must be text/html result, otherwise extjs form result handler dies
+        $c->stash->{text} = Thruk::Utils::Filter::json_encode({ 'msg' => 'Fileupload contains illegal folder.', success => Cpanel::JSON::XS::false });
+        return;
+    }
 
     if(!-w $folder.'/.') {
         # must be text/html result, otherwise extjs form result handler dies
         $c->stash->{text} = Thruk::Utils::Filter::json_encode({ 'msg' => 'Fileupload must use existing and writable folder.', success => Cpanel::JSON::XS::false });
         return;

Vulnerable code:     my $folder = $c->stash->{'usercontent_folder'}.'/'.$location;
Fixed code: use Cwd ();
    my $folder = Cwd::abs_path($c->stash->{'usercontent_folder'}.'/'.$location);

    # make sure requested folder is below the usercontent folder
    if(CORE::index(Cwd::abs_path($c->stash->{'usercontent_folder'}), $folder) != 0) {
        # must be text/html result, otherwise extjs form result handler dies
        $c->stash->{text} = Thruk::Utils::Filter::json_encode({ 'msg' => 'Fileupload contains illegal folder.', success => Cpanel::JSON::XS::false });
        return;
    }