CVE-2018-20332: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal') (CWE-22)
value: An issue has been discovered in the OpenWebif plugin through 1.2.4 for Enigma2 based devices. Reading of arbitrary files is possible with /file?action=download&file= followed by a full pathname, and listing of arbitrary directories is possible with /file?action=download&dir= followed by a full pathname. This is related to plugin/controllers/file.py in the e2openplugin-OpenWebif project.
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: 7.5
Published: 2018-12-21T09:29Z
Repository: https://github.com/E2OpenPlugins/e2openplugin-OpenWebif
Language: Python
Files changed: plugin/controllers/file.py, plugin/controllers/rest_fs_access.py
Commit date: 2017-09-09 11:35:27 +0200
Commit message: move utilities library and actually use it

Diff: diff --git a/plugin/controllers/file.py b/plugin/controllers/file.py
index 8bd4a84d..1f24cc0a 100644
--- a/plugin/controllers/file.py
+++ b/plugin/controllers/file.py
@@ -17,10 +17,12 @@ import json
 
 from twisted.web import static, resource, http
 
 from Components.config import config
 from Tools.Directories import fileExists
+from utilities import lenient_force_utf_8, sanitise_filename_slashes
+
 
 def new_getRequestHostname(self):
 	host = self.getHeader(b'host')
 	if host:
 		if host[0]=='[':
@@ -36,12 +38,12 @@ class FileController(resource.Resource):
 		action = "download"
 		if "action" in request.args:
 			action = request.args["action"][0]
 
 		if "file" in request.args:
-			filename = request.args["file"][0].decode('utf-8', 'ignore').encode('utf-8')
-			filename = re.sub("^/+", "/", os.path.realpath(filename))
+			filename = lenient_force_utf_8(request.args["file"][0])
+			filename = sanitise_filename_slashes(os.path.realpath(filename))
 
 			if not os.path.exists(filename):
 				return "File '%s' not found" % (filename)
 
 			if action == "stream":
diff --git a/plugin/controllers/rest_fs_access.py b/plugin/controllers/rest_fs_access.py
index 35091d3d..510575d1 100644
--- a/plugin/controllers/rest_fs_access.py
+++ b/plugin/controllers/rest_fs_access.py
@@ -41,15 +41,13 @@ import re
 import urlparse
 
 import twisted.web.static
 from twisted.web import http
 
+from utilities import MANY_SLASHES_REGEX
 import file
 
-MANY_SLASHES_PATTERN = r'[\/]+'
-MANY_SLASHES_REGEX = re.compile(MANY_SLASHES_PATTERN)
-
 #: default path from which files will be served
 DEFAULT_ROOT_PATH = os.path.abspath(os.path.dirname(__file__))
 
 #: CORS - HTTP headers the client may use
 CORS_ALLOWED_CLIENT_HEADERS = [
diff --git a/plugin/utilities.py b/plugin/controllers/utilities.py
similarity index 100%
rename from plugin/utilities.py
rename to plugin/controllers/utilities.py

Vulnerable code: 			filename = request.args["file"][0].decode('utf-8', 'ignore').encode('utf-8')
			filename = re.sub("^/+", "/", os.path.realpath(filename))
MANY_SLASHES_PATTERN = r'[\/]+'
MANY_SLASHES_REGEX = re.compile(MANY_SLASHES_PATTERN)

Fixed code: from utilities import lenient_force_utf_8, sanitise_filename_slashes

			filename = lenient_force_utf_8(request.args["file"][0])
			filename = sanitise_filename_slashes(os.path.realpath(filename))
from utilities import MANY_SLASHES_REGEX