the_open_door
coffee
ESEN
← Back to index

Ghost file read via symlink (CVE-2023-40028)

Ghost is a CMS widely used for blogs and newsletters. Up to version 5.59.1, an authenticated user (with image-upload permission) could read arbitrary files from the server by abusing how Ghost handles attachments: CVE-2023-40028.

The idea. Ghost lets you upload an image file and later serves it back by its URL. The flaw is that it doesn't validate that the uploaded file is really an image, nor that it isn't a symbolic link. If, instead of a .png, you upload a symlink pointing to, say, /etc/passwd, requesting that "image" makes Ghost return the content of the pointed-to file. It's an arbitrary file read (a server-side LFI) with the privileges of the Ghost process.

How it's exploited. PoCs automate the process (authenticate against the admin API, create the link, upload it and request it). The flow is:

git clone https://github.com/0xyassine/CVE-2023-40028.git
./CVE-2023-40028.sh -u <admin@domain> -p <password>
# then you give the path to read, with ../ up to the root:
# ../../../../../../etc/passwd

Why the target matters. Not every file is readable: the Ghost process runs as an unprivileged user, so /etc/shadow or root-owned files are out of reach. The payoff is reading the application config (config.production.json), which usually stores database, SMTP or API credentials in cleartext, perfect for a credential reuse against SSH or other services.

Caveat. The endpoint being authenticated means you need a valid account first (here, a credential leaked in a repository). The vulnerability raises "CMS user" access to "filesystem reader"; it does not by itself grant command execution.

How to detect it. The signature is in the upload pattern: an authenticated user uploading through the admin API an "image file" that is really a symlink (or whose name contains ../ toward system paths), and then requesting it back. The response gives away the abuse: when asking for that "image", the server returns plain text (the content of config.production.json, /etc/passwd) instead of binary image data. Repeated reads of the app configuration after uploading anomalous files, or an image Content-Type with a text body, are the signal.

Defence. Update Ghost to 5.59.1 or later; in general, validate that an uploaded file isn't a symbolic link and resolve the real path before serving it.

Machines where it's used