Page MenuHome GnuPG

gpgtar write outside --directory via symlink traversal
Open, HighPublic

Description

Gpgtar may follow the symlink to outside, when -C (--directory) option is used. This could be considered a security issue under recommended practice these days.

Reported-by: Oleh Konko https://1seal.org/research/

Major use cases (of no -C), it's gpgtar which creates the directory, so, this case has no problem.

For references, see other CVEs.

Event Timeline

gniibe mentioned this in Unknown Object (Maniphest Task).Mar 9 2026, 2:40 AM

I think the most compatible way is to assume that the -C directory does either not exist or is empty. If that is not the case the extraction shall stop or not extract files which would clobber an existing file. For strong backward compatibility a new option --clobber could be added. We can also check for an empty directory first.

to assume that the -C directory does either not exist or is empty

I had thought the same. But, I realized that our tests gnupg/tests/openpgp uses gpgtar with --directory=. with non-empty directory in many cases. Note that Kleo always creates the directory before the invocation of gpgtar and use --directory= option.

Here is the patch (as of now):

It doesn't introduce new option (yet), but simply allows --directory=. (or -C .) for the existing use case.

I updated the patch of mine above.

It is better to notify users (of existing use case of --directory, including . case) and to encourage examining about possibly dangerous scenario.
I mean, let it break with --directory option use cases, when the directory is not empty.

Here is updated patch, which introduces new --directory~. option (it looks strange intentionally). It specifically allows no checking for the current directory (for backward compatibility). I don't think it's needed to introduce more generous option like --clobber + --directory=<somewhere> which allows extracting to non-empty directory anywhere.

It is only possible to bypass the checking for the current directory. If there are existing use cases where it specifies the directory of non-empty, we request those users to fix usage and to make sure it's safe.

gniibe mentioned this in Unknown Object (Maniphest Task).Mar 16 2026, 7:01 AM

I located the place where tests/ requires the feature of gpgtar overriding existing files.
I fixed that in: rG268e435f921a: tests:openpgp: With gpgtar, extract tarball into an empty directory.

I still don't think that --directory~ is a good name for an option. It looks to similar to the ~USER shell pattern. What about --unsafe-directory which also avoids an option ambiguity regression on the CLI?

@werner My intention of rG268e435 is removing the need of --directory~ option, in our code.
With this change, we can focus on the behavior of --directory= option.

I think that it is acceptable breaking change when we will introduce the assumption of empty directory.

Well okay. What we could do is to add a --compliance-flag like we have for other components. This is not part of the stable API but it can be used if need arises.

werner renamed this task from Security (internal) - gpgtar write outside --directory via symlink traversal to gpgtar write outside --directory via symlink traversal.Sun, Mar 22, 3:06 PM
werner lowered the priority of this task from Unbreak Now! to High.
werner added a project: gpgtar.
gniibe mentioned this in Unknown Object (Maniphest Task).Mon, Mar 23, 3:43 AM
gniibe mentioned this in Unknown Object (Maniphest Task).Mon, Mar 30, 2:35 AM

I think we have a regression with this change. This is the old behaviour (gnupg 2.2 in this case, though)

$ gpgtar --extract --skip-crypto -v -C foo123 a.tar
gpgtar: extracting to 'foo123/'
gpgtar: error creating directory 'foo123/cry/org': No such file or directory

and this the new

$ gpgtar --extract --skip-crypto -v -C foo123 a.tar
gpgtar: Can't open (symlink?): foo123

We should have the same error message as with the old version if the directory does not exist.

With -C <DIRNAME> option, it is OK to have no <DIRNAME>.
This case should be allowed.

werner shifted this object from the Restricted Space space to the S1 Public space.Fri, Apr 17, 9:47 AM
werner changed the visibility from "g10code (Project)" to "Public (No Login Required)".
werner changed the edit policy from "Custom Policy" to "Contributor (Project)".