[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Re: process-deb-once-with-python review



* Niels Thykier <niels@thykier.net>, 2014-07-08, 07:34:
          target = os.path.normpath(os.path.join(output_dir,
member.name))
          if (not target.startswith(output_dir_stem)
              and target != output_dir):
              raise ValueError("%s escapes unpack-root" % member.name)

os.path.normpath() doesn't access filesystem, so it won't protect you from escaping unpack-root via a symlink.


You mean something like:

symlink usr to /etc
extract usr/password (which overwrites /etc/password)

Yes.

That should be fairly trivial to fix by doing a follow up check with realpath.

realpath() could be racy. If the shipped symlink points into a world-writable directory (e.g. /tmp/foo), and it is possible to predict value of output_dir, local attacker could make such a /tmp/foo symlink that the realpath() test passes, and later switch the /tmp/foo symlink to something else.
So we probably need something better that realpath().

How about safepath() from the attachment?


[I'll reply to the rest of the mail later.]

--
Jakub Wilk
import errno
import os
import stat

class UnsafePath(Exception):
    pass

def safepath(root, path):
    comps = path.split('/')
    comps.reverse()
    if not comps:
        raise UnsafePath
    if comps[-1] in ('.', ''):
        comps.pop()
    tpath = root
    while comps:
        comp = comps.pop()
        if comp in ('.', '..', ''):
            raise UnsafePath
        tpath += '/' + comp
        try:
            st = os.lstat(tpath)
        except OSError as exc:
            if exc.errno == errno.ENOENT:
                continue
        else:
            if comps and stat.S_ISDIR(st.st_mode):
                continue
        raise UnsafePath(path)
    return tpath

Reply to: