Package: release.debian.org
Severity: normal
Tags: stretch
User: release.debian....@packages.debian.org
Usertags: pu

Hi SRMs,

I'd like to fix CVE-2018-1000156 in patch for Stretch, which is an
arbitrary command execution in ed-style patches.
While it might be used for remote compromise, it would need a setup to
accept patches unconditionally. But then an attacker has an easy path
already to insert vulnerable code to source files or JavaScript
injection to HTML pages, etc. Hence it doesn't warrant a DSA on its
own, but would be good to fix in a point release.

Thanks for considering,
Laszlo/GCS
diff -Nru patch-2.7.5/debian/changelog patch-2.7.5/debian/changelog
--- patch-2.7.5/debian/changelog	2015-03-07 06:38:30.000000000 +0000
+++ patch-2.7.5/debian/changelog	2018-04-16 20:48:43.000000000 +0000
@@ -1,3 +1,10 @@
+patch (2.7.5-1+deb9u1) stretch; urgency=medium
+
+  * Fix CVE-2018-1000156: arbitrary command execution in ed-style patches
+    (closes: #894993).
+
+ -- Laszlo Boszormenyi (GCS) <g...@debian.org>  Mon, 16 Apr 2018 20:48:43 +0000
+
 patch (2.7.5-1) unstable; urgency=medium
 
   * New upstream release.
diff -Nru patch-2.7.5/debian/patches/Fix_arbitrary_command_execution_in_ed-style_patches.patch patch-2.7.5/debian/patches/Fix_arbitrary_command_execution_in_ed-style_patches.patch
--- patch-2.7.5/debian/patches/Fix_arbitrary_command_execution_in_ed-style_patches.patch	1970-01-01 00:00:00.000000000 +0000
+++ patch-2.7.5/debian/patches/Fix_arbitrary_command_execution_in_ed-style_patches.patch	2018-04-16 20:48:43.000000000 +0000
@@ -0,0 +1,237 @@
+From 123eaff0d5d1aebe128295959435b9ca5909c26d Mon Sep 17 00:00:00 2001
+From: Andreas Gruenbacher <agr...@gnu.org>
+Date: Fri, 6 Apr 2018 12:14:49 +0200
+Subject: Fix arbitrary command execution in ed-style patches
+ (CVE-2018-1000156)
+
+* src/pch.c (do_ed_script): Write ed script to a temporary file instead
+of piping it to ed: this will cause ed to abort on invalid commands
+instead of rejecting them and carrying on.
+* tests/ed-style: New test case.
+* tests/Makefile.am (TESTS): Add test case.
+---
+ src/pch.c         | 91 ++++++++++++++++++++++++++++++++++++++++---------------
+ tests/Makefile.am |  1 +
+ tests/ed-style    | 41 +++++++++++++++++++++++++
+ 3 files changed, 108 insertions(+), 25 deletions(-)
+ create mode 100644 tests/ed-style
+
+diff --git a/src/pch.c b/src/pch.c
+index 0c5cc26..4fd5a05 100644
+--- a/src/pch.c
++++ b/src/pch.c
+@@ -33,6 +33,7 @@
+ # include <io.h>
+ #endif
+ #include <safe.h>
++#include <sys/wait.h>
+ 
+ #define INITHUNKMAX 125			/* initial dynamic allocation size */
+ 
+@@ -2387,22 +2387,28 @@ do_ed_script (char const *inname, char c
+     static char const editor_program[] = EDITOR_PROGRAM;
+ 
+     file_offset beginning_of_this_line;
+-    FILE *pipefp = 0;
+     size_t chars_read;
++    FILE *tmpfp = 0;
++    char const *tmpname;
++    int tmpfd;
++    pid_t pid;
++
++    if (! dry_run && ! skip_rest_of_patch)
++      {
++	/* Write ed script to a temporary file.  This causes ed to abort on
++	   invalid commands such as when line numbers or ranges exceed the
++	   number of available lines.  When ed reads from a pipe, it rejects
++	   invalid commands and treats the next line as a new command, which
++	   can lead to arbitrary command execution.  */
++
++	tmpfd = make_tempfile (&tmpname, 'e', NULL, O_RDWR | O_BINARY, 0);
++	if (tmpfd == -1)
++	  pfatal ("Can't create temporary file %s", quotearg (tmpname));
++	tmpfp = fdopen (tmpfd, "w+b");
++	if (! tmpfp)
++	  pfatal ("Can't open stream for file %s", quotearg (tmpname));
++      }
+ 
+-    if (! dry_run && ! skip_rest_of_patch) {
+-	int exclusive = *outname_needs_removal ? 0 : O_EXCL;
+-	assert (! inerrno);
+-	*outname_needs_removal = true;
+-	copy_file (inname, outname, 0, exclusive, instat.st_mode, true);
+-	sprintf (buf, "%s %s%s", editor_program,
+-		 verbosity == VERBOSE ? "" : "- ",
+-		 outname);
+-	fflush (stdout);
+-	pipefp = popen(buf, binary_transput ? "wb" : "w");
+-	if (!pipefp)
+-	  pfatal ("Can't open pipe to %s", quotearg (buf));
+-    }
+     for (;;) {
+ 	char ed_command_letter;
+ 	beginning_of_this_line = file_tell (pfp);
+@@ -2413,14 +2418,14 @@ do_ed_script (char const *inname, char const *outname,
+ 	}
+ 	ed_command_letter = get_ed_command_letter (buf);
+ 	if (ed_command_letter) {
+-	    if (pipefp)
+-		if (! fwrite (buf, sizeof *buf, chars_read, pipefp))
++	    if (tmpfp)
++		if (! fwrite (buf, sizeof *buf, chars_read, tmpfp))
+ 		    write_fatal ();
+ 	    if (ed_command_letter != 'd' && ed_command_letter != 's') {
+ 	        p_pass_comments_through = true;
+ 		while ((chars_read = get_line ()) != 0) {
+-		    if (pipefp)
+-			if (! fwrite (buf, sizeof *buf, chars_read, pipefp))
++		    if (tmpfp)
++			if (! fwrite (buf, sizeof *buf, chars_read, tmpfp))
+ 			    write_fatal ();
+ 		    if (chars_read == 2  &&  strEQ (buf, ".\n"))
+ 			break;
+@@ -2433,13 +2438,49 @@ do_ed_script (char const *inname, char const *outname,
+ 	    break;
+ 	}
+     }
+-    if (!pipefp)
++    if (!tmpfp)
+       return;
+-    if (fwrite ("w\nq\n", sizeof (char), (size_t) 4, pipefp) == 0
+-	|| fflush (pipefp) != 0)
++    if (fwrite ("w\nq\n", sizeof (char), (size_t) 4, tmpfp) == 0
++	|| fflush (tmpfp) != 0)
+       write_fatal ();
+-    if (pclose (pipefp) != 0)
+-      fatal ("%s FAILED", editor_program);
++
++    if (lseek (tmpfd, 0, SEEK_SET) == -1)
++      pfatal ("Can't rewind to the beginning of file %s", quotearg (tmpname));
++
++    if (! dry_run && ! skip_rest_of_patch) {
++	int exclusive = *outname_needs_removal ? 0 : O_EXCL;
++	*outname_needs_removal = true;
++	if (inerrno != ENOENT)
++	  {
++	    *outname_needs_removal = true;
++	    copy_file (inname, outname, 0, exclusive, instat.st_mode, true);
++	  }
++	sprintf (buf, "%s %s%s", editor_program,
++		 verbosity == VERBOSE ? "" : "- ",
++		 outname);
++	fflush (stdout);
++
++	pid = fork();
++	if (pid == -1)
++	  pfatal ("Can't fork");
++	else if (pid == 0)
++	  {
++	    dup2 (tmpfd, 0);
++	    execl ("/bin/sh", "sh", "-c", buf, (char *) 0);
++	    _exit (2);
++	  }
++	else
++	  {
++	    int wstatus;
++	    if (waitpid (pid, &wstatus, 0) == -1
++	        || ! WIFEXITED (wstatus)
++		|| WEXITSTATUS (wstatus) != 0)
++	      fatal ("%s FAILED", editor_program);
++	  }
++    }
++
++    fclose (tmpfp);
++    safe_unlink (tmpname);
+ 
+     if (ofp)
+       {
+diff --git a/tests/Makefile.am b/tests/Makefile.am
+index 6b6df63..16f8693 100644
+--- a/tests/Makefile.am
++++ b/tests/Makefile.am
+@@ -55,7 +55,8 @@ TESTS = \
+ 	remove-directories \
+ 	symlinks \
+ 	unmodified-files \
+-	deep-directories
++	deep-directories \
++	ed-style
+ 
+ XFAIL_TESTS = \
+ 	dash-o-append
+diff --git a/tests/ed-style b/tests/ed-style
+new file mode 100644
+index 0000000..d8c0689
+--- /dev/null
++++ b/tests/ed-style
+@@ -0,0 +1,41 @@
++# Copyright (C) 2018 Free Software Foundation, Inc.
++#
++# Copying and distribution of this file, with or without modification,
++# in any medium, are permitted without royalty provided the copyright
++# notice and this notice are preserved.
++
++. $srcdir/test-lib.sh
++
++require cat
++use_local_patch
++use_tmpdir
++
++# ==============================================================
++
++cat > ed1.diff <<EOF
++0a
++foo
++.
++EOF
++
++check 'patch -e foo -i ed1.diff' <<EOF
++EOF
++
++check 'cat foo' <<EOF
++foo
++EOF
++
++cat > ed2.diff <<EOF
++1337a
++r !echo bar
++,p
++EOF
++
++check 'patch -e foo -i ed2.diff 2> /dev/null || echo "Status: $?"' <<EOF
++?
++Status: 2
++EOF
++
++check 'cat foo' <<EOF
++foo
++EOF
+--- a/tests/Makefile.in
++++ b/tests/Makefile.in
+@@ -1252,7 +1252,8 @@ TESTS = \
+ 	remove-directories \
+ 	symlinks \
+ 	unmodified-files \
+-	deep-directories
++	deep-directories \
++	ed-style
+ 
+ XFAIL_TESTS = \
+ 	dash-o-append
+@@ -1722,6 +1723,13 @@ deep-directories.log: deep-directories
+ 	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ 	--log-file $$b.log --trs-file $$b.trs \
+ 	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
++	"$$tst" $(AM_TESTS_FD_REDIRECT)
++ed-style.log: ed-style
++	@p='ed-style'; \
++	b='ed-style'; \
++	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
++	--log-file $$b.log --trs-file $$b.trs \
++	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ 	"$$tst" $(AM_TESTS_FD_REDIRECT)
+ .test.log:
+ 	@p='$<'; \
+-- 
+cgit v1.0-41-gc330
+
diff -Nru patch-2.7.5/debian/patches/Fix_ed-style_test_failure.patch patch-2.7.5/debian/patches/Fix_ed-style_test_failure.patch
--- patch-2.7.5/debian/patches/Fix_ed-style_test_failure.patch	1970-01-01 00:00:00.000000000 +0000
+++ patch-2.7.5/debian/patches/Fix_ed-style_test_failure.patch	2018-04-16 20:48:43.000000000 +0000
@@ -0,0 +1,27 @@
+From 458ac51a05426c1af9aa6bf1342ecf60728c19b4 Mon Sep 17 00:00:00 2001
+From: Bruno Haible <br...@clisp.org>
+Date: Sat, 7 Apr 2018 12:34:03 +0200
+Subject: Fix 'ed-style' test failure.
+
+* tests/ed-style: Remove '?' line from expected output.
+---
+ tests/ed-style | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+diff --git a/tests/ed-style b/tests/ed-style
+index d8c0689..6b6ef9d 100644
+--- a/tests/ed-style
++++ b/tests/ed-style
+@@ -31,8 +31,7 @@ r !echo bar
+ ,p
+ EOF
+ 
+-check 'patch -e foo -i ed2.diff 2> /dev/null || echo "Status: $?"' <<EOF
+-?
++check 'patch -e foo -i ed2.diff > /dev/null 2> /dev/null || echo "Status: $?"' <<EOF
+ Status: 2
+ EOF
+ 
+-- 
+cgit v1.0-41-gc330
+
diff -Nru patch-2.7.5/debian/patches/series patch-2.7.5/debian/patches/series
--- patch-2.7.5/debian/patches/series	2015-03-07 06:30:09.000000000 +0000
+++ patch-2.7.5/debian/patches/series	2018-04-16 20:48:43.000000000 +0000
@@ -2,3 +2,5 @@
 558485-backupmode
 m-merge
 patch-bug-1306412.diff
+Fix_arbitrary_command_execution_in_ed-style_patches.patch
+Fix_ed-style_test_failure.patch

Reply via email to