CVE-2023-52076 (published 2024-01-25T16:15Z)

Atril Document Viewer is the default document reader of the MATE desktop environment for Linux. A path traversal and arbitrary file write vulnerability exists in versions of Atril prior to 1.26.2. This vulnerability is capable of writing arbitrary files anywhere on the filesystem to which the user opening a crafted document has access. The only limitation is that this vulnerability cannot be exploited to overwrite existing files, but that doesn't stop an attacker from achieving Remote Command Execution on the target system. Version 1.26.2 of Atril contains a patch for this vulnerability.

CVSS v3 base score: 7.8, HIGH (CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-22
cwe_name: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
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.
extended_description: 
            Many file operations are intended to take place within a restricted directory. By using special elements such as ".." and "/" separators, attackers can escape outside of the restricted location to access files or directories that are elsewhere on the system. One of the most common special elements is the "../" sequence, which in most modern operating systems is interpreted as the parent directory of the current location. This is referred to as relative path traversal. Path traversal also covers the use of absolute pathnames such as "/usr/local/bin" to access unexpected files. This is referred to as absolute path traversal.
         
         

Fix information:
repo_url: https://github.com/mate-desktop/atril
commit_details: repo_url: https://github.com/mate-desktop/atril
author: lukefromdc
author_date: 2023-12-25 01:40:42-05:00
msg: epub: Prevent path traversal when extracting files

Test each file's resolved path against the temporary directory
before extracting.
repository: repo_url: https://github.com/mate-desktop/atril
repo_name: mate-desktop/atril
description: A document viewer for MATE
repo_language: C
owner: mate-desktop
file_changes: filename: epub-document.c
change_type: ModificationType.MODIFY
diff: @@ -635,7 +635,7 @@ check_mime_type(const gchar* uri,GError** error)
         for (i = 0; i < g_strv_length (mimetypes); i++) {
            if (strcmp(mimeFromFile, mimetypes[i]) == 0)
                 return TRUE;
-	}
+        }
 
         /* fallback for malformed epub files */
         if (strcmp (mimeFromFile, "application/zip") == 0)
@@ -671,7 +671,7 @@ check_mime_type(const gchar* uri,GError** error)
 }
 
 static gboolean
-extract_one_file(EpubDocument* epub_document,GError ** error)
+extract_one_file(EpubDocument* epub_document, GFile *tmp_gfile, GError ** error)
 {
     GFile * outfile ;
     gsize writesize = 0;
@@ -698,6 +698,20 @@ extract_one_file(EpubDocument* epub_document,GError ** error)
     gfilepath = g_string_new(epub_document->tmp_archive_dir) ;
     g_string_append_printf(gfilepath,"/%s",(gchar*)currentfilename);
 
+    outfile = g_file_new_for_path (gfilepath->str);
+    g_autofree gchar *rpath = g_file_get_relative_path (tmp_gfile, outfile);
+
+    if (rpath == NULL)
+    {
+        g_set_error_literal (error,
+                             EV_DOCUMENT_ERROR,
+                             EV_DOCUMENT_ERROR_INVALID,
+                             _("epub file is invalid or corrupt"));
+        g_critical ("Invalid filename in Epub container - '%s'", (gchar *) currentfilename);
+        result = FALSE;
+        goto out;
+    }
+
     /*if we encounter a directory, make a directory inside our temporary folder.*/
     if (directory != NULL && *directory == '\0')
     {
@@ -725,7 +739,6 @@ extract_one_file(EpubDocument* epub_document,GError ** error)
         g_string_free(dir_create,TRUE);
     }
 
-    outfile = g_file_new_for_path(gfilepath->str);
     outstream = g_file_create(outfile,G_FILE_CREATE_PRIVATE,NULL,error);
     gpointer buffer = g_malloc0(512);
     while ( (writesize = unzReadCurrentFile(epub_document->epubDocument,buffer,512) ) != 0 )
@@ -738,10 +751,10 @@ extract_one_file(EpubDocument* epub_document,GError ** error)
     }
     g_free(buffer);
     g_output_stream_close((GOutputStream*)outstream,NULL,error);
-    g_object_unref(outfile) ;
-    g_object_unref(outstream) ;
+    g_object_unref(outstream);
 
 out:
+    g_object_unref(outfile);
     unzCloseCurrentFile (epub_document->epubDocument) ;
     g_string_free(gfilepath,TRUE);
     g_free(currentfilename);
@@ -753,6 +766,7 @@ extract_epub_from_container (const gchar* uri,
                              EpubDocument *epub_document,
                              GError ** error)
 {
+    GFile *tmp_gfile = NULL;
     GError *err = NULL;
     epub_document->archivename = g_filename_from_uri(uri,NULL,error);
 
@@ -814,9 +828,10 @@ extract_epub_from_container (const gchar* uri,
         goto out;
     }
 
+    tmp_gfile = g_file_new_for_path (epub_document->tmp_archive_dir);
     while ( TRUE )
     {
-        if ( extract_one_file(epub_document,&err) == FALSE )
+        if ( extract_one_file(epub_document, tmp_gfile, &err) == FALSE )
         {
             if (err) {
                 g_propagate_error (error, err);
@@ -837,6 +852,7 @@ extract_epub_from_container (const gchar* uri,
     }
 
 out:
+    g_clear_object (&tmp_gfile);
     unzClose(epub_document->epubDocument);
     return result;
 }

programming_language: C