@@ -44,6 +44,7 @@ from . import (
permissions,
py_mtime,
rpath,
+ runpath,
scrollkeeper,
shebangdepends,
sodepends,
new file mode 100644
@@ -0,0 +1,71 @@
+# namcap rules - runpath
+#
+# Copyright (C) 2019 Jelle van der Waa <jelle@archlinux.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+from Namcap.util import is_elf
+from Namcap.ruleclass import TarballRule
+
+from elftools.elf.elffile import ELFFile
+from elftools.elf.dynamic import DynamicSection
+
+allowed = ('/usr/lib', '/usr/lib32', '/lib', '$ORIGIN', '${ORIGIN}')
+allowed_toplevels = (s + '/' for s in allowed)
+warn = ['/usr/local/lib']
+
+
+def get_runpaths(fileobj):
+ elffile = ELFFile(fileobj)
+ for section in elffile.iter_sections():
+ if not isinstance(section, DynamicSection):
+ continue
+ for tag in section.iter_tags():
+ if tag.entry.d_tag != 'DT_RUNPATH':
+ continue
+ for path in tag.runpath.split(':'):
+ yield path
+
+
+class package(TarballRule):
+ name = "runpath"
+ description = "Verifies if RUNPATH is secure"
+
+ def analyze(self, pkgingo, tar):
+ for entry in tar:
+ if not entry.isfile():
+ continue
+
+ fileobj = tar.extractfile(entry)
+ if not is_elf(fileobj):
+ continue
+
+ for path in get_runpaths(fileobj):
+ path_ok = path in allowed
+ for allowed_toplevel in allowed_toplevels:
+ if path.startswith(allowed_toplevel):
+ path_ok = True
+
+ if not path_ok:
+ self.errors.append(("insecure-runpath %s %s",
+ (path, entry.name)))
+ break
+
+ if path in warn and entry.name not in insecure_rpaths:
+ self.warnings.append(("insecure-runpath %s %s",
+ (path, entry.name)))
+
+
+# vim: set ts=4 sw=4 noet:
@@ -44,6 +44,7 @@ incorrect-owner %s (%s:%s) :: File (%s) is owned by %s:%s
invalid-filename :: File name %s contains non standard characters
info-dir-file-present %s :: Info directory file (%s) should not be present
insecure-rpath %s %s :: Insecure RPATH '%s' in file ('%s')
+insecure-runpath %s %s :: Insecure RUNPATH '%s' in file ('%s')
libtool-file-present %s :: File (%s) is a libtool file
library-no-package-associated %s :: Referenced library '%s' is an uninstalled dependency
link-level-dependence %s in %s :: Link-level dependence (%s) in file %s
@@ -108,6 +108,9 @@ Checks basic file and and directory permissions. It returns warnings about worl
.B rpath
Gives an error if a binary has RPATH set to something other than /usr/lib
.TP
+.B runpath
+Gives an error if a binary has RUNPATH set to something other than /usr/lib, /usr/lib32
+.TP
.B scrollkeeper
Verifies that there aren't any scrollkeeper directories
.TP
From: Jelle van der Waa <jelle@vdwaa.nl> Include a rule to check for vulnerable RUNPATH ELF binary entries, which allow arbitrary code execution by loading shared libraries from an attacker controller path. --- Namcap/rules/__init__.py | 1 + Namcap/rules/runpath.py | 71 ++++++++++++++++++++++++++++++++++++++++ namcap-tags | 1 + namcap.1 | 3 ++ 4 files changed, 76 insertions(+) create mode 100644 Namcap/rules/runpath.py