HTTP header injection in CFITSIO filename parsing

HTTP header injection in CFITSIO filename parsing

The filename mini-language starts working before the file is judged to be a FITS file. That matters because a caller may think it is only passing a path through, while CFITSIO is already deciding whether the string means a local file, a protocol handler, a selector, or an output target.

A plain-looking call into fits_open_file can still trigger side effects if the string contains CFITSIO syntax. The parsing step is the trust boundary. Once untrusted input reaches it, the caller has lost control over whether CFITSIO opens a file, copies one, or reaches out over the network.

The ffopen() path turns a string into a parser input

ffopen() does not treat the filename as inert text. It applies the extended syntax, then routes the result to a registered handler. Prefixes such as http://, https://, ftp://, ftps://, mem://, shmem://, and other registered forms can change the open path completely.

That means a validation check placed after fits_open_file is too late to protect the call. The syntax has already been interpreted. Any logic that expects a literal filesystem path is now depending on behaviour that can change with CFITSIO version, build, and registered drivers.

Prefixes, selectors, and outfile clauses all matter before FITS validation

CFITSIO accepts more than protocol prefixes. It also parses selectors and output clauses, which can reshape what is opened or copied. A filename like input.fits(output.fits) is not just a read request. It instructs CFITSIO to copy data before the file is checked as FITS content.

That ordering is the ugly part. The copy happens first, validation comes later, and invalid content does not undo the earlier action. If the source is a sensitive local path, the effect is arbitrary file copy. If the source is remote, the effect is a fetch followed by a write to a path chosen by the caller or attacker.

How crafted filenames become network fetches and file copies

Once protocol handlers are in play, the filename string becomes a request object with too much authority. Some handlers use legacy raw socket code. TLS-backed variants hand request construction off to libcurl. Either way, the caller is no longer dealing with a local file open.

HTTP, FTP, and root:// handlers change the trust boundary

CFITSIO registers protocol handlers through its driver interface, and the prefix selects the backend. Plain http:// and ftp:// handling uses raw socket code that has been in the tree for years. TLS variants delegate to libcurl. That distinction matters less to the caller than the result: the string can cause a network connection.

root:// is another useful example because it shows that the parser is not limited to local disk semantics. Once a driver is registered, a matching prefix becomes an instruction to hand the request to that handler. If the application accepts user-controlled filenames, that is enough to cross from file I/O into SSRF territory.

outfile writes can turn a read into SSRF with persistence

The outfile form makes the issue sharper. A remote URL can be wrapped so that CFITSIO fetches the response and writes it to a local destination. That gives an attacker two things at once: a network request made by the server, and a file saved on disk.

That is an SSRF primitive with persistence. It is no longer just an outbound request that disappears into logs. The response can be written into a place the attacker can later access, or into a location that causes damage by overwriting something useful. If the target reads from paths the attacker can influence later, the copied file becomes a stepping stone rather than a one-off request.

Tighten the caller before CFITSIO sees untrusted input

The safe point is before the string reaches CFITSIO. After that, the parser has already had its say. Treat any filename parameter as active syntax, not as a plain path field.

Treat filename parameters as active syntax, not plain paths

If a parameter is supposed to be a literal filesystem path, reject anything that looks like CFITSIO syntax before the library call. That means blocking protocol prefixes, selector forms, parentheses used for output clauses, and any other extended filename markers your code does not need.

If the application only needs local file access, do not pass arbitrary user input straight into fits_open_file. That call should receive a pre-validated path, not something that can become a URL, a filtered FITS view, or a copy instruction. The safest assumption is that the syntax is dangerous until proven otherwise.

Test literal path opening, restricted protocol sets, and rejected outfile clauses

Test the exact behaviour you want, not the behaviour you hope for. A literal path opening test should confirm that an input such as /tmp/file.fits is treated as a local path and nothing more. A rejection test should fail any string containing http://, ftp://, root://, or an outfile clause if those forms are not required.

If the application genuinely needs CFITSIO extended syntax, restrict the allowed protocol set to the minimum needed and reject the rest. If it never needs remote fetches, do not leave the handlers exposed just because they exist. A parser that can copy files and reach out over the network is not a convenience feature when it is fed attacker-controlled input.

Related posts

Weekly Tech Digest | 06 Jul 2026

Stay updated with the latest in tech! This digest covers AI ethics, auto industry shifts, and the impact of politics on technology, exploring today's pressing issues.

wolfCOSE zero-allocation parsing in embedded C

wolfCOSE looks sensible only if you care about what your firmware actually has to carry. I like that, because on small targets the wrong crypto feature can cost more than the message itself, and there...

restic | v0.19.1

restic v0 19 1: safer FUSE mounts and mountpoint checks, robust backup source and exclude handling, clearer CLI JSON output, Windows SFTP deletion fixes