CVE-2019-10767 (published 2019-11-21T17:15Z)

An attacker can include file contents from outside the `/adapter/xxx/` directory, where `xxx` is the name of an existent adapter like "admin". It is exploited using the administrative web panel with a request for an adapter file. **Note:** The attacker has to be logged in if the authentication is enabled (by default isn\'t enabled).

Severity: MEDIUM
CVSS v2 base score: 5.0 (AV:N/AC:L/Au:N/C:P/I:N/A:N)
CVSS v3 base score: 7.5, HIGH (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/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/ioBroker/ioBroker.js-controller
commit_details: repo_url: https://github.com/ioBroker/ioBroker.js-controller
author: Ingo Fischer
author_date: 2019-10-04 11:09:30+02:00
msg: some security enhancements to better prevent path traversing
repository: repo_url: https://github.com/ioBroker/ioBroker.js-controller
repo_name: ioBroker/ioBroker.js-controller
description: ioBroker controller
repo_language: TypeScript
owner: ioBroker
file_changes: filename: objectsUtils.js
change_type: ModificationType.MODIFY
diff: @@ -472,11 +472,17 @@ function sanitizePath(id, name, callback) {
     }
 
     if (id) {
-        id = id.replace(/\.\./g, ''); // do not allow to write in parent directories
+        id = id.replace(/[\]\[*,;'"`<>\\?\/]/g, ''); // remove all invalid characters from states plus slashes
     }
 
-    if (name.indexOf('..') !== -1) {
-        name = path.normalize(name);
+    if (name.includes('..')) {
+        name = path.normalize('/' + name);
+        name = name.replace(/\\/g, '/');
+    }
+    if (name.includes('..')) {
+        // Also after normalization we still have .. in it - should not happen if normalize worked correctly
+        name = name.replace(/\.\./g, '');
+        name = path.normalize('/' + name);
         name = name.replace(/\\/g, '/');
     }
     if (name[0] === '/') name = name.substring(1); // do not allow absolute paths

programming_language: JavaScript
filename: testObjects.js
change_type: ModificationType.MODIFY
diff: @@ -341,6 +341,39 @@ function register(it, expect, context) {
         });
     });
 
+    it(textName + 'should read file and prevent path traversing', done => {
+        const objects = context.objects;
+        objects.readFile(testId, '../../myFile/abc1.txt', err => {
+            expect(err).to.be.not.ok;
+            expect(data).to.be.equal('dataInFile');
+            objects.readFile(testId, '/myFile/abc1.txt', err => {
+                expect(err).to.be.not.ok;
+                expect(data).to.be.equal('dataInFile');
+                objects.readFile(testId, '/../../myFile/abc1.txt', err => {
+                    expect(err).to.be.not.ok;
+                    expect(data).to.be.equal('dataInFile');
+                    objects.readFile(testId, 'myFile/../blubb/../myFile/abc1.txt', err => {
+                        expect(err).to.be.not.ok;
+                        expect(data).to.be.equal('dataInFile');
+                        objects.readFile(testId, '/myFile/../blubb/../myFile/abc1.txt', err => {
+                            expect(err).to.be.not.ok;
+                            expect(data).to.be.equal('dataInFile');
+                            objects.readFile(testId, '../blubb/../myFile/abc1.txt', err => {
+                                expect(err).to.be.not.ok;
+                                expect(data).to.be.equal('dataInFile');
+                                objects.readFile(testId, '/../blubb/../myFile/abc1.txt', err => {
+                                    expect(err).to.be.not.ok;
+                                    expect(data).to.be.equal('dataInFile');
+                                    done();
+                                });
+                            });
+                        });
+                    });
+                });
+            });
+        });
+    });
+
     it(textName + 'should unlink file', done => {
         const objects = context.objects;
         objects.unlink(testId, 'myFile/abc1.txt', err => {

programming_language: TypeScript