Commit 97a5954b by devttys0

Fixed dirtraversal test; updated extractor to not fork unless necessary (nose…

Fixed dirtraversal test; updated extractor to not fork unless necessary (nose tests don't like sys.exit)
parent 271333b6
...@@ -295,7 +295,7 @@ class Extractor(Module): ...@@ -295,7 +295,7 @@ class Extractor(Module):
# one we just dd'd # one we just dd'd
if file_path != dd_file_path: if file_path != dd_file_path:
# Symlinks can cause security issues if they point outside the extraction directory. # Symlinks can cause security issues if they point outside the extraction directory.
self.symlink_sanitizer(file_path) self.symlink_sanitizer(file_path, extraction_directory)
# If this is a directory and we are supposed to process directories for this extractor, # If this is a directory and we are supposed to process directories for this extractor,
# then add all files under that directory to the # then add all files under that directory to the
...@@ -303,7 +303,7 @@ class Extractor(Module): ...@@ -303,7 +303,7 @@ class Extractor(Module):
if os.path.isdir(file_path): if os.path.isdir(file_path):
for root, dirs, files in os.walk(file_path): for root, dirs, files in os.walk(file_path):
# Symlinks can cause security issues if they point outside the extraction directory. # Symlinks can cause security issues if they point outside the extraction directory.
self.symlink_sanitizer([os.path.join(root, x) for x in dirs+files]) self.symlink_sanitizer([os.path.join(root, x) for x in dirs+files], extraction_directory)
for f in files: for f in files:
full_path = os.path.join(root, f) full_path = os.path.join(root, f)
...@@ -949,29 +949,43 @@ class Extractor(Module): ...@@ -949,29 +949,43 @@ class Extractor(Module):
return (retval, '&&'.join(command_list)) return (retval, '&&'.join(command_list))
def shell_call(self, command): def shell_call(self, command):
child_pid = os.fork() # If not in debug mode, redirect output to /dev/null
if child_pid is 0: if not binwalk.core.common.DEBUG:
# Switch to the run-as user privileges, if one has been set tmp = subprocess.DEVNULL
if self.runas_uid is not None and self.runas_gid is not None: else:
binwalk.core.common.debug("Switching privileges to %s (%d:%d)" % (self.runas_user, self.runas_uid, self.runas_gid)) tmp = None
os.setgid(self.runas_uid)
os.setuid(self.runas_gid) # If a run-as user is not the current user, we'll need to switch privileges to that user account
if self.runas_uid != os.getuid():
# If not in debug mode, redirect output to /dev/null binwalk.core.common.debug("Switching privileges to %s (%d:%d)" % (self.runas_user, self.runas_uid, self.runas_gid))
if not binwalk.core.common.DEBUG:
tmp = subprocess.DEVNULL # Fork a child process
else: child_pid = os.fork()
tmp = None if child_pid is 0:
# Switch to the run-as user privileges, if one has been set
# Execute command if self.runas_uid is not None and self.runas_gid is not None:
os.setgid(self.runas_uid)
os.setuid(self.runas_gid)
else:
# child_pid of None indicates that no os.fork() occured
child_pid = None
# If we're the child, or there was no os.fork(), execute the command
if child_pid in [0, None]:
binwalk.core.common.debug("subprocess.call(%s, stdout=%s, stderr=%s)" % (command, str(tmp), str(tmp))) binwalk.core.common.debug("subprocess.call(%s, stdout=%s, stderr=%s)" % (command, str(tmp), str(tmp)))
rval = subprocess.call(shlex.split(command), stdout=tmp, stderr=tmp) rval = subprocess.call(shlex.split(command), stdout=tmp, stderr=tmp)
# A true child process should exit with the subprocess exit value
if child_pid is 0:
sys.exit(rval) sys.exit(rval)
# If no os.fork() happened, just return the subprocess exit value
elif child_pid is None:
return rval
# Else, os.fork() happened and we're the parent. Wait and return the child's exit value.
else: else:
return os.wait()[1] return os.wait()[1]
def symlink_sanitizer(self, file_list): def symlink_sanitizer(self, file_list, extraction_directory):
# Allows either a single file path, or a list of file paths to be passed in for sanitization. # Allows either a single file path, or a list of file paths to be passed in for sanitization.
if type(file_list) is not list: if type(file_list) is not list:
file_list = [file_list] file_list = [file_list]
...@@ -982,7 +996,7 @@ class Extractor(Module): ...@@ -982,7 +996,7 @@ class Extractor(Module):
linktarget = os.path.realpath(file_name) linktarget = os.path.realpath(file_name)
binwalk.core.common.debug("Analysing symlink: %s -> %s" % (file_name, linktarget)) binwalk.core.common.debug("Analysing symlink: %s -> %s" % (file_name, linktarget))
if not linktarget.startswith(self.directory) and linktarget != os.devnull: if not linktarget.startswith(extraction_directory) and linktarget != os.devnull:
binwalk.core.common.warning("Symlink points outside of the extraction directory: %s -> %s; changing link target to %s for security purposes." % (file_name, linktarget, os.devnull)) binwalk.core.common.warning("Symlink points outside of the extraction directory: %s -> %s; changing link target to %s for security purposes." % (file_name, linktarget, os.devnull))
os.remove(file_name) os.remove(file_name)
os.symlink(os.devnull, file_name) os.symlink(os.devnull, file_name)
import os import os
import binwalk import binwalk
from nose.tools import eq_, ok_ from nose.tools import eq_, ok_, assert_equal, assert_not_equal
def test_dirtraversal(): def test_dirtraversal():
''' '''
Test: Open dirtraversal.tar, scan for signatures. Test: Open dirtraversal.tar, scan for signatures.
Verify that dangerous symlinks have been sanitized. Verify that dangerous symlinks have been sanitized.
''' '''
bad_symlink_file_list = ['foo', 'bar', 'foo2', 'bar2'] bad_symlink_file_list = ['foo', 'bar', 'subdir/foo2', 'subdir/bar2']
good_symlink_file_list = ['README_link', 'README2_link'] good_symlink_file_list = ['subdir/README_link', 'README2_link']
input_vector_file = os.path.join(os.path.dirname(__file__), input_vector_file = os.path.join(os.path.dirname(__file__),
"input-vectors", "input-vectors",
"dirtraversal.tar") "dirtraversal.tar")
output_directory = os.path.join(os.path.dirname(__file__),
"input-vectors",
"_dirtraversal.tar.extracted")
scan_result = binwalk.scan(input_vector_file, scan_result = binwalk.scan(input_vector_file,
signature=True, signature=True,
extract=True, extract=True,
quiet=True) quiet=True)[0]
# Test number of modules used
eq_(len(scan_result), 1)
# Make sure the bad symlinks have been sanitized and the # Make sure the bad symlinks have been sanitized and the
# good symlinks have not been sanitized. # good symlinks have not been sanitized.
for result in scan_result[0].results: for symlink in bad_symlink_file_list:
if result.file.name in bad_symlink_file_list: linktarget = os.path.realpath(os.path.join(output_directory, symlink))
assert_equal(os.path.realpath(result.file.path), os.devnull) assert_equal(linktarget, os.devnull)
elif result.file.name in good_symlink_file_list: for symlink in good_symlink_file_list:
assert_not_equal(os.path.realpath(result.file.path), os.devnull) linktarget = os.path.realpath(os.path.join(output_directory, symlink))
assert_not_equal(linktarget, os.devnull)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment