HTTP and FTP fetches via CFITSIO EFS

HTTP and FTP fetches via CFITSIO EFS

CFITSIO’s Extended Filename Syntax is not just a file name parser. It can turn a string into a protocol handler, an output path, or a filter expression before file validation ever gets a say. That makes fits_open_file a bigger attack surface than it looks at first glance.

How ffopen() turns a filename into a protocol handler

ffopen() sits in front of the normal open path and reads the filename string as a small language. A prefix can route the request to a registered driver, so a path that looks local may end up handled by http://, ftps://, mem://, shmem://, or another backend instead of a plain file open.

That matters because the caller often thinks it has passed a literal file path. CFITSIO does not treat every input that way. Once the parser sees a protocol prefix, it dispatches through the relevant driver hooks and can reach raw socket code for plain http:// and ftp://, or libcurl-backed handling for TLS variants.

For code that only intended to inspect a FITS file, that is enough to create side effects. A read-only open is no longer a quiet metadata lookup if the string itself can select a network path.

When a metadata query is enough to trigger network access

A minimal call site that opens a file and reads one metadata item can still cause network activity inside CFITSIO. The open step may already have selected a remote transport, and the later query can materialise extra parsing or fetch behaviour.

That turns a filename into an SSRF primitive when the input is attacker-controlled. If the application accepts a URL-shaped value and passes it to fits_open_file, CFITSIO may fetch the remote resource before the caller has a chance to reject it as invalid FITS. The remote content does not need to be a valid FITS file for the request to leave the machine.

The protocol handling is old enough to be boring, which is usually where the sharp edges hide. Plain http:// and ftp:// handling has been in the tree for years, while TLS-backed variants lean on libcurl. The result is still the same from an attacker’s point of view: a server-side fetch that happens because the parser accepted a string.

Outfile clauses copy before FITS validation stops them

The outfile clause is worse than a fetch gadget because it can copy data before validation fails. A source path wrapped in an extended filename can be opened, copied to an attacker-chosen destination, and only then rejected when it turns out not to be a FITS file.

That gives you a file copy primitive, not just a parser quirk. A local file such as /etc/passwd(/workspace/foo) can be copied into a readable location even though the source is not FITS. If the destination points somewhere sensitive, the damage is not subtle.

The same pattern works in the other direction for remote content. A URL such as https://example.com/anyfile(/workspace/grabbed.file) can pull remote bytes down and store them locally. If the destination path is writable, CFITSIO becomes an SSRF gadget with persistence, because the response is saved to disk rather than just returned to the caller.

That persistence changes the failure mode. It is no longer a one-off outbound request. It is a request plus a local artefact that can be read later, overwritten, or used to break files that should never have been touched.

Locking down the call site without breaking legitimate paths

The safest boundary is simple: treat filenames as literal file paths unless remote or filtered forms are genuinely required. If the application only needs local FITS files, reject protocol prefixes, bracketed expressions, and outfile clauses before handing the string to CFITSIO.

Use fits_open_diskfile when you want a plain on-disk path and nothing else. That narrows the accepted input shape and avoids the filename mini-language entirely. It is a blunt control, which is fine when the feature set should stay boring.

If remote access is legitimate, keep the accepted prefixes explicit and small. A short allowlist beats trying to untangle every form of EFS after it has already been parsed. The same applies to destination paths: never let attacker input decide where copied content lands, because the copy happens before FITS validation can save you.

Registration of extra drivers makes this sharper still. Custom prefixes through fits_register_driver expand what ffopen() can dispatch to, so the trust boundary needs to sit outside the parser, not after it. If a code path must accept EFS, isolate it, constrain it, and do not let untrusted strings wander into helper functions that only look like file opens.

Related posts

CVE-2026-3300 and WordPress admin account abuse

Everest Forms Pro CVE-2026-3300 is not a tidy bug, it is the sort of mess that turns form input into PHP and then acts surprised when attackers notice. I would not trust any site running the affected...

Review CI/CD install scripts for malicious code

Install scripts are the bit people wave through until they bite; in CI/CD, that means code runs under a trusted name before anyone has looked properly. I pin versions, record what landed, and treat...

Check npm and PyPI packages for compromise

A clean package yesterday means very little if a fresh update arrives with new maintainers, odd install scripts, or a version jump that does not make sense. I look for software supply chain trouble by...