Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

fixed security issue with tarfile extraction #36

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion setup_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
from setuptools.command.sdist import sdist as _sdist
from setuptools.command.egg_info import egg_info as _egg_info

from tar_validation import get_safe_members_in_tar_file

try:
from urllib2 import urlopen, URLError
except ImportError:
Expand Down Expand Up @@ -62,7 +64,7 @@ def download_library(command):
content.seek(0)
with tarfile.open(fileobj=content) as tf:
dirname = tf.getnames()[0].partition('/')[0]
tf.extractall()
tf.extractall(members=get_safe_members_in_tar_file(tf))
shutil.move(dirname, libdir)
else:
raise SystemExit( "Unable to download secp256k1 library: HTTP-Status: %d", r.getcode())
Expand Down
47 changes: 47 additions & 0 deletions tar_validation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from os.path import abspath
from os.path import dirname
from os.path import join as joinpath
from os.path import realpath

def _badpath(path, base):
"""Determines if a given file path is under a given base path or not.
:param str path: file path where the file will be extracted to.
:param str base: path to the current working directory.
:return: False, if the path is under the given base, else True.
:rtype: bool
"""
# joinpath will ignore base if path is absolute
return not realpath(abspath(joinpath(base, path))).startswith(base)


def _badlink(info, base):
"""Determine if a given link is under a given base path or not.
:param TarInfo info: file that is going to be extracted.
:param str base: path to the current working directory.
:return: False, if the path is under the given base, else True.
:rtype: bool
"""
# Links are interpreted relative to the directory containing the link
tip = realpath(abspath(joinpath(base, dirname(info.name))))
return _badpath(info.linkname, base=tip)


def get_safe_members_in_tar_file(tarfile):
"""Retrieve members of a tar file that are safe to extract.
:param Tarfile tarfile: the archive that has been opened as a TarFile
object.
:return: list of members in the archive that are safe to extract.
:rtype: list
"""
base = realpath(abspath(('.')))
result = []
for finfo in tarfile.getmembers():
if _badpath(finfo.name, base):
print(finfo.name + ' is blocked: illegal path.')
elif finfo.issym() and _badlink(finfo, base):
print(finfo.name + ' is blocked: Symlink to ' + finfo.linkname)
elif finfo.islnk() and _badlink(finfo, base):
print(finfo.name + ' is blocked: Hard link to ' + finfo.linkname)
else:
result.append(finfo)
return result