mirror of https://github.com/ansible/ansible.git
Prevent roles from using symlinks to overwrite files outside of the installation directory (#81780)
* Sanitize linkname during role installs * Add tests * add clog fragpull/81784/head
parent
989ea7a9f0
commit
ddf0311c63
@ -0,0 +1,3 @@
|
|||||||
|
security_fixes:
|
||||||
|
- ansible-galaxy - Prevent roles from using symlinks to overwrite
|
||||||
|
files outside of the installation directory (CVE-2023-5115)
|
@ -0,0 +1,45 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
"""Create a role archive which overwrites an arbitrary file."""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import pathlib
|
||||||
|
import tarfile
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
parser = argparse.ArgumentParser(description=__doc__)
|
||||||
|
parser.add_argument('archive', type=pathlib.Path, help='archive to create')
|
||||||
|
parser.add_argument('content', type=pathlib.Path, help='content to write')
|
||||||
|
parser.add_argument('target', type=pathlib.Path, help='file to overwrite')
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
create_archive(args.archive, args.content, args.target)
|
||||||
|
|
||||||
|
|
||||||
|
def create_archive(archive_path: pathlib.Path, content_path: pathlib.Path, target_path: pathlib.Path) -> None:
|
||||||
|
with (
|
||||||
|
tarfile.open(name=archive_path, mode='w') as role_archive,
|
||||||
|
tempfile.TemporaryDirectory() as temp_dir_name,
|
||||||
|
):
|
||||||
|
temp_dir_path = pathlib.Path(temp_dir_name)
|
||||||
|
|
||||||
|
meta_main_path = temp_dir_path / 'meta' / 'main.yml'
|
||||||
|
meta_main_path.parent.mkdir()
|
||||||
|
meta_main_path.write_text('')
|
||||||
|
|
||||||
|
symlink_path = temp_dir_path / 'symlink'
|
||||||
|
symlink_path.symlink_to(target_path)
|
||||||
|
|
||||||
|
role_archive.add(meta_main_path)
|
||||||
|
role_archive.add(symlink_path)
|
||||||
|
|
||||||
|
content_tarinfo = role_archive.gettarinfo(content_path, str(symlink_path))
|
||||||
|
|
||||||
|
with content_path.open('rb') as content_file:
|
||||||
|
role_archive.addfile(content_tarinfo, content_file)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
@ -0,0 +1,44 @@
|
|||||||
|
- name: create test directories
|
||||||
|
file:
|
||||||
|
path: '{{ remote_tmp_dir }}/dir-traversal/{{ item }}'
|
||||||
|
state: directory
|
||||||
|
loop:
|
||||||
|
- source
|
||||||
|
- target
|
||||||
|
- roles
|
||||||
|
|
||||||
|
- name: create test content
|
||||||
|
copy:
|
||||||
|
dest: '{{ remote_tmp_dir }}/dir-traversal/source/content.txt'
|
||||||
|
content: |
|
||||||
|
some content to write
|
||||||
|
|
||||||
|
- name: build dangerous dir traversal role
|
||||||
|
script:
|
||||||
|
chdir: '{{ remote_tmp_dir }}/dir-traversal/source'
|
||||||
|
cmd: create-role-archive.py dangerous.tar content.txt {{ remote_tmp_dir }}/dir-traversal/target/target-file-to-overwrite.txt
|
||||||
|
executable: '{{ ansible_playbook_python }}'
|
||||||
|
|
||||||
|
- name: install dangerous role
|
||||||
|
command:
|
||||||
|
cmd: ansible-galaxy role install --roles-path '{{ remote_tmp_dir }}/dir-traversal/roles' dangerous.tar
|
||||||
|
chdir: '{{ remote_tmp_dir }}/dir-traversal/source'
|
||||||
|
ignore_errors: true
|
||||||
|
register: galaxy_install_dangerous
|
||||||
|
|
||||||
|
- name: check for overwritten file
|
||||||
|
stat:
|
||||||
|
path: '{{ remote_tmp_dir }}/dir-traversal/target/target-file-to-overwrite.txt'
|
||||||
|
register: dangerous_overwrite_stat
|
||||||
|
|
||||||
|
- name: get overwritten content
|
||||||
|
slurp:
|
||||||
|
path: '{{ remote_tmp_dir }}/dir-traversal/target/target-file-to-overwrite.txt'
|
||||||
|
register: dangerous_overwrite_content
|
||||||
|
when: dangerous_overwrite_stat.stat.exists
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- dangerous_overwrite_content.content|default('')|b64decode == ''
|
||||||
|
- not dangerous_overwrite_stat.stat.exists
|
||||||
|
- galaxy_install_dangerous is failed
|
Loading…
Reference in New Issue