CVE-2019-14452 (published 2019-07-31T02:15Z)

Sigil before 0.9.16 is vulnerable to a directory traversal, allowing attackers to write arbitrary files via a ../ (dot dot slash) in a ZIP archive entry that is mishandled during extraction.

Severity: MEDIUM
CVSS v2 base score: 5.0 (AV:N/AC:L/Au:N/C:N/I:P/A:N)
CVSS v3 base score: 7.5, HIGH (CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N)

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/Sigil-Ebook/Sigil
commit_details: repo_url: https://github.com/Sigil-Ebook/Sigil
author: Kevin Hendricks
author_date: 2019-07-12 14:08:44-04:00
msg: harden plugin unzipping to zip-slip attacks
repository: repo_url: https://github.com/Sigil-Ebook/Sigil
repo_name: Sigil-Ebook/Sigil
description: Sigil is a multi-platform EPUB ebook editor
repo_language: C++
owner: Sigil-Ebook
file_changes: filename: Utility.cpp
change_type: ModificationType.MODIFY
diff: @@ -1,5 +1,6 @@
 /************************************************************************
 **
+**  Copyright (C) 2019   Kevin B. Hendricks, Stratford, Ontario Canada
 **  Copyright (C) 2009, 2010, 2011  Strahinja Markovic  <strahinja.markovic@gmail.com>
 **
 **  This file is part of Sigil.
@@ -715,6 +716,44 @@ bool Utility::UnZip(const QString &zippath, const QString &destpath)
 
             // If there is no file name then we can't do anything with it.
             if (!qfile_name.isEmpty()) {
+
+	        // for security reasons against maliciously crafted zip archives
+	        // we need the file path to always be inside the target folder 
+	        // and not outside, so we will remove all illegal backslashes
+	        // and all relative upward paths segments "/../" from the zip's local 
+	        // file name/path before prepending the target folder to create 
+	        // the final path
+
+	        QString original_path = qfile_name;
+	        bool evil_or_corrupt_epub = false;
+
+	        if (qfile_name.contains("\\")) evil_or_corrupt_epub = true; 
+	        qfile_name = "/" + qfile_name.replace("\\","");
+
+	        if (qfile_name.contains("/../")) evil_or_corrupt_epub = true;
+	        qfile_name = qfile_name.replace("/../","/");
+
+	        while(qfile_name.startsWith("/")) { 
+		  qfile_name = qfile_name.remove(0,1);
+	        }
+                
+	        if (cp437_file_name.contains("\\")) evil_or_corrupt_epub = true; 
+	        cp437_file_name = "/" + cp437_file_name.replace("\\","");
+
+	        if (cp437_file_name.contains("/../")) evil_or_corrupt_epub = true;
+	        cp437_file_name = cp437_file_name.replace("/../","/");
+
+	        while(cp437_file_name.startsWith("/")) { 
+		  cp437_file_name = cp437_file_name.remove(0,1);
+	        }
+
+	        if (evil_or_corrupt_epub) {
+		    unzCloseCurrentFile(zfile);
+		    unzClose(zfile);
+		    // throw (UNZIPLoadParseError(QString(QObject::tr("Possible evil or corrupt zip file name: %1")).arg(original_path).toStdString()));
+                    return false;
+	        }
+
                 // We use the dir object to create the path in the temporary directory.
                 // Unfortunately, we need a dir ojbect to do this as it's not a static function.
                 // Full file path in the temporary directory.

programming_language: C++
filename: sigil_exception.h
change_type: ModificationType.MODIFY
diff: @@ -1,5 +1,6 @@
 /************************************************************************
 **
+**  Copyright (C) 2019  Kevin B. Hendricks, Stratford, Ontario Canada
 **  Copyright (C) 2015  John Schember <john@nachtimwald.com>
 **  Copyright (C) 2009, 2010, 2011  Strahinja Markovic  <strahinja.markovic@gmail.com>
 **
@@ -132,4 +133,13 @@ public:
     EPUBLoadParseError(const std::string &msg) : std::runtime_error(msg) { };
 };
 
+
+/**
+ * Thrown for Invalid EPUB errors while loading and parsing content files.
+ */
+class UNZIPLoadParseError : public std::runtime_error {
+public:
+    UNZIPLoadParseError(const std::string &msg) : std::runtime_error(msg) { };
+};
+
 #endif // SG_EXCEPTION_H

programming_language: C++
repo_url: https://github.com/Sigil-Ebook/Sigil
commit_details: repo_url: https://github.com/Sigil-Ebook/Sigil
author: Kevin Hendricks
author_date: 2019-06-27 11:47:42-04:00
msg: further harden against malicious epubs and produce error message
repository: repo_url: https://github.com/Sigil-Ebook/Sigil
repo_name: Sigil-Ebook/Sigil
description: Sigil is a multi-platform EPUB ebook editor
repo_language: C++
owner: Sigil-Ebook
file_changes: filename: ImportEPUB.cpp
change_type: ModificationType.MODIFY
diff: @@ -425,12 +425,41 @@ void ImportEPUB::ExtractContainer()
             // If there is no file name then we can't do anything with it.
             if (!qfile_name.isEmpty()) {
 
-	        // for security reasons we need the file path to always be inside the 
-                // target folder and not outside, so we will remove all relative upward 
-                // paths segments ".." from the file path before prepending the target 
-                // folder to create the final target path
-	        qfile_name = qfile_name.replace("../","");
-                cp437_file_name = cp437_file_name.replace("../","");
+	        // for security reasons against maliciously crafted zip archives
+                // we need the file path to always be inside the target folder 
+                // and not outside, so we will remove all illegal backslashes
+                // and all relative upward paths segments "/../" from the zip's local 
+                // file name/path before prepending the target folder to create 
+                // the final path
+
+	        QString original_path = qfile_name;
+	        bool evil_or_corrupt_epub = false;
+
+                if (qfile_name.contains("\\")) evil_or_corrupt_epub = true; 
+	        qfile_name = "/" + qfile_name.replace("\\","");
+
+                if (qfile_name.contains("/../")) evil_or_corrupt_epub = true;
+	        qfile_name = qfile_name.replace("/../","/");
+
+                while(qfile_name.startsWith("/")) { 
+		    qfile_name = qfile_name.remove(0,1);
+		}
+
+                if (cp437_file_name.contains("\\")) evil_or_corrupt_epub = true; 
+                cp437_file_name = "/" + cp437_file_name.replace("\\","");
+
+                if (cp437_file_name.contains("/../")) evil_or_corrupt_epub = true;
+	        cp437_file_name = cp437_file_name.replace("/../","/");
+
+                while(cp437_file_name.startsWith("/")) { 
+		    cp437_file_name = cp437_file_name.remove(0,1);
+		}
+
+                if (evil_or_corrupt_epub) {
+                    unzCloseCurrentFile(zfile);
+                    unzClose(zfile);
+                    throw (EPUBLoadParseError(QString(QObject::tr("Possible evil or corrupt epub file name: %1")).arg(original_path).toStdString()));
+                }
 
                 // We use the dir object to create the path in the temporary directory.
                 // Unfortunately, we need a dir ojbect to do this as it's not a static function.

programming_language: C++
repo_url: https://github.com/Sigil-Ebook/Sigil
commit_details: repo_url: https://github.com/Sigil-Ebook/Sigil
author: Kevin Hendricks
author_date: 2019-06-26 14:33:01-04:00
msg: do not allow zip files to have upward relative path sections
repository: repo_url: https://github.com/Sigil-Ebook/Sigil
repo_name: Sigil-Ebook/Sigil
description: Sigil is a multi-platform EPUB ebook editor
repo_language: C++
owner: Sigil-Ebook
file_changes: filename: ImportEPUB.cpp
change_type: ModificationType.MODIFY
diff: @@ -424,6 +424,14 @@ void ImportEPUB::ExtractContainer()
 
             // If there is no file name then we can't do anything with it.
             if (!qfile_name.isEmpty()) {
+
+	        // for security reasons we need the file path to always be inside the 
+                // target folder and not outside, so we will remove all relative upward 
+                // paths segments ".." from the file path before prepending the target 
+                // folder to create the final target path
+	        qfile_name = qfile_name.replace("../","");
+                cp437_file_name = cp437_file_name.replace("../","");
+
                 // We use the dir object to create the path in the temporary directory.
                 // Unfortunately, we need a dir ojbect to do this as it's not a static function.
                 QDir dir(m_ExtractedFolderPath);

programming_language: C++