CVE-2023-40028: Improper Link Resolution Before File Access ('Link Following') (CWE-59)
value: Ghost is an open source content management system. Versions prior to 5.59.1 are subject to a vulnerability which allows authenticated users to upload files that are symlinks. This can be exploited to perform an arbitrary file read of any file on the host operating system. Site administrators can check for exploitation of this issue by looking for unknown symlinks within Ghost's `content/` folder. Version 5.59.1 contains a fix for this issue. All users are advised to upgrade. There are no known workarounds for this vulnerability.
CWE description: The product attempts to access a file based on the filename, but it does not properly prevent that filename from identifying a link or shortcut that resolves to an unintended resource.
CVSS v3 base score: 6.5
Published: 2023-08-15T18:15Z
Repository: https://github.com/TryGhost/Ghost
Language: JSON
Files changed: ghost/core/package.json, ghost/core/test/regression/api/admin/db.test.js, yarn.lock
Commit date: 2023-08-15 13:01:11 +0200
Commit message: 🔒 Fixed arbitrary file read via symlinks in content import

refs https://github.com/TryGhost/Product/issues/3726
refs https://github.com/TryGhost/Ghost/security/advisories/GHSA-9c9v-w225-v5rg

- credits to https://github.com/ixSly

Diff: diff --git a/ghost/core/package.json b/ghost/core/package.json
index 254d6940c8..ec69d8ea85 100644
--- a/ghost/core/package.json
+++ b/ghost/core/package.json
@@ -158,11 +158,11 @@
     "@tryghost/url-utils": "4.4.0",
     "@tryghost/validator": "0.2.4",
     "@tryghost/verification-trigger": "0.0.0",
     "@tryghost/version": "0.1.22",
     "@tryghost/webmentions": "0.0.0",
-    "@tryghost/zip": "1.1.34",
+    "@tryghost/zip": "1.1.37",
     "amperize": "0.6.1",
     "analytics-node": "6.2.0",
     "bluebird": "3.7.2",
     "body-parser": "1.20.2",
     "bookshelf": "1.2.0",
diff --git a/ghost/core/test/regression/api/admin/db.test.js b/ghost/core/test/regression/api/admin/db.test.js
index 36a95ce4d6..611b700213 100644
--- a/ghost/core/test/regression/api/admin/db.test.js
+++ b/ghost/core/test/regression/api/admin/db.test.js
@@ -375,10 +375,19 @@ describe('DB API', function () {
         yearlyPrice.get('currency').should.equal('usd');
         yearlyPrice.get('interval').should.equal('year');
         yearlyPrice.get('stripe_price_id').should.equal('price_d04baebb73');
         yearlyPrice.get('stripe_product_id').should.equal('prod_d2c1708c21');
     });
+
+    it('Can not import a ZIP-file with symlinks', async function () {
+        await request.post(localUtils.API.getApiQuery('db/'))
+            .set('Origin', config.get('url'))
+            .set('Accept', 'application/json')
+            .expect('Content-Type', /json/)
+            .attach('importfile', path.join(__dirname, '/../../../utils/fixtures/import/symlinks.zip'))
+            .expect(415);
+    });
 });
 
 // The following tests will create a new clean database for every test
 describe('DB API (cleaned)', function () {
     beforeEach(async function () {
diff --git a/ghost/core/test/utils/fixtures/import/symlinks.zip b/ghost/core/test/utils/fixtures/import/symlinks.zip
new file mode 100644
index 0000000000..d0d195664d
Binary files /dev/null and b/ghost/core/test/utils/fixtures/import/symlinks.zip differ
diff --git a/yarn.lock b/yarn.lock
index 3682f0b0ca..90e439e1c1 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -7171,28 +7171,29 @@
   resolved "https://registry.yarnpkg.com/@tryghost/webhook-mock-receiver/-/webhook-mock-receiver-0.2.6.tgz#20a4141f5813287988770f5f1e394ad2af5c1063"
   integrity sha512-OB+6Wx2yoAafOe4/GEy3X1KEnNPysvijkZTDyZRjbs9qFA1gJ0IdxWL/oyoX4HgVuGDuVr/LM83zomsM5YUtBQ==
   dependencies:
     p-wait-for "3.2.0"
 
-"@tryghost/zip@1.1.34":
-  version "1.1.34"
-  resolved "https://registry.yarnpkg.com/@tryghost/zip/-/zip-1.1.34.tgz#c8ab1ac3bb5bc780f29c63723d3933f345f0ec65"
-  integrity sha512-tfv1Lcd46/W0AkbBj/ttZLCdqI1LQppvkVQ7l5+gSx2ZOHqnZmkoKwMOB0J/LxEEC1TzhqrW0Vzx+ZYtoPACzA==
-  dependencies:
-    archiver "^5.0.0"
-    extract-zip "^2.0.1"
-    fs-extra "^10.0.0"
-
 "@tryghost/zip@1.1.35":
   version "1.1.35"
   resolved "https://registry.yarnpkg.com/@tryghost/zip/-/zip-1.1.35.tgz#654239d0b48f2baac5679cec2930937b825c2f84"
   integrity sha512-Hy67p+q8lZu7ohRO6HbGAHDF7gs+aM7OwRqCpSA56ZkTWWf4JkSbJINhqBL2t4XaIGtDQjDKN/oz44RtJ96BBQ==
   dependencies:
     archiver "^5.0.0"
     extract-zip "^2.0.1"
     fs-extra "^10.0.0"
 
+"@tryghost/zip@1.1.37":
+  version "1.1.37"
+  resolved "https://registry.yarnpkg.com/@tryghost/zip/-/zip-1.1.37.tgz#55ca424a42615d0e53aa12b2b108e7f20ac36b42"
+  integrity sha512-L+MKGsbIgHgEeoiq6lLpirNflp8CX89xDSwBRucHR/T+QLwVgnof+aiTTuaTrrF+pOWetBDTvB/gN5t94+k6hA==
+  dependencies:
+    "@tryghost/errors" "^1.2.26"
+    archiver "^5.0.0"
+    extract-zip "^2.0.1"
+    fs-extra "^11.0.0"
+
 "@trysound/sax@0.2.0":
   version "0.2.0"
   resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad"
   integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==
 

Vulnerable code:     "@tryghost/zip": "1.1.34",
"@tryghost/zip@1.1.34":
  version "1.1.34"
  resolved "https://registry.yarnpkg.com/@tryghost/zip/-/zip-1.1.34.tgz#c8ab1ac3bb5bc780f29c63723d3933f345f0ec65"
  integrity sha512-tfv1Lcd46/W0AkbBj/ttZLCdqI1LQppvkVQ7l5+gSx2ZOHqnZmkoKwMOB0J/LxEEC1TzhqrW0Vzx+ZYtoPACzA==
  dependencies:
    archiver "^5.0.0"
    extract-zip "^2.0.1"
    fs-extra "^10.0.0"

Fixed code:     "@tryghost/zip": "1.1.37",

    it('Can not import a ZIP-file with symlinks', async function () {
        await request.post(localUtils.API.getApiQuery('db/'))
            .set('Origin', config.get('url'))
            .set('Accept', 'application/json')
            .expect('Content-Type', /json/)
            .attach('importfile', path.join(__dirname, '/../../../utils/fixtures/import/symlinks.zip'))
            .expect(415);
    });
"@tryghost/zip@1.1.37":
  version "1.1.37"
  resolved "https://registry.yarnpkg.com/@tryghost/zip/-/zip-1.1.37.tgz#55ca424a42615d0e53aa12b2b108e7f20ac36b42"
  integrity sha512-L+MKGsbIgHgEeoiq6lLpirNflp8CX89xDSwBRucHR/T+QLwVgnof+aiTTuaTrrF+pOWetBDTvB/gN5t94+k6hA==
  dependencies:
    "@tryghost/errors" "^1.2.26"
    archiver "^5.0.0"
    extract-zip "^2.0.1"
    fs-extra "^11.0.0"