Dpkg: fixing CVE-2014-0471
cover Dpkg: fixing CVE-2014-0471
From: Wenzong Fan <[email protected]>
Directory traversal vulnerability in the unpacking functionality in dpkg before 1.15.9, 1.16.x before 1.16.13, and 1.17.x before 1.17.8 allows remote attackers to write arbitrary files via a crafted source package, related to "C-style filename quoting."
The following changes since commit 8e0c54cd0e82ffe120f84f495101cd29e6fd06bf:
bitbake: bb/utils: fix contains_any() (2014-06-12 17:47:59 +0100)
are available in the git repository at:
git://git.pokylinux.org/poky-contrib wenzong/dpkg
http://git.pokylinux.org/cgit.cgi/poky-contrib/log/?h=wenzong/dpkg
Guillem Jover (2):
Dpkg::Source::Patch: Correctly parse C-style diff filenames Dpkg::Source::Patch: Outright reject C-style filenames in patches
.../dpkg-1.17.4-CVE-2014-0471-CVE-2014-3127.patch | 49 ++++++++++++ .../dpkg/dpkg/dpkg-1.17.4-CVE-2014-0471.patch | 83 ++++++++++++++++++++ meta/recipes-devtools/dpkg/dpkg_1.17.4.bb | 2 + 3 files changed, 134 insertions(+) create mode 100644 meta/recipes-devtools/dpkg/dpkg/dpkg-1.17.4-CVE-2014-0471-CVE-2014-3127.patch create mode 100644 meta/recipes-devtools/dpkg/dpkg/dpkg-1.17.4-CVE-2014-0471.patch
Signature · 1 line
1.7.9.5
PATCH 1/2 Dpkg::Source::Patch: Correctly parse C-style diff filenames
From: Guillem Jover <[email protected]>
based on: commit a82651188476841d190c58693f95827d61959b51
We need to strip the surrounding quotes, and unescape any escape sequence, so that we check the same files that the patch program will be using, otherwise a malicious package could overpass those checks, and perform directory traversal attacks on source package unpacking.
[drop the text for debian/changelog,because it's not suitable
for the veriosn]
--- .../dpkg/dpkg/dpkg-1.17.4-CVE-2014-0471.patch | 83 ++++++++++++++++++++ meta/recipes-devtools/dpkg/dpkg_1.17.4.bb | 1 + 2 files changed, 84 insertions(+) create mode 100644 meta/recipes-devtools/dpkg/dpkg/dpkg-1.17.4-CVE-2014-0471.patch
meta/recipes-devtools/dpkg/dpkg/dpkg-1.17.4-CVE-2014-0471.patch new file +83 -0
@@ -0,0 +1,83 @@ +diff -uarN dpkg-1.17.1-org/scripts/Dpkg/Source/Patch.pm dpkg-1.17.1/scripts/Dpkg/Source/Patch.pm +--- dpkg-1.17.1-org/scripts/Dpkg/Source/Patch.pm 2014-06-05 15:24:07.422446284 +0800 ++++ dpkg-1.17.1/scripts/Dpkg/Source/Patch.pm 2014-06-05 15:41:37.746446314 +0800 +@@ -324,14 +324,53 @@ + return $line; + } + +-# Strip timestamp +-sub _strip_ts { +- my $header = shift; +- +- # Tab is the official separator, it's always used when +- # filename contain spaces. Try it first, otherwise strip on space +- # if there's no tab +- $header =~ s/\s.*// unless ($header =~ s/\t.*//); ++my %ESCAPE = (( ++ 'a' => "\a", ++ 'b' => "\b", ++ 'f' => "\f", ++ 'n' => "\n", ++ 'r' => "\r", ++ 't' => "\t", ++ 'v' => "\cK", ++ '\\' => '\\', ++ '"' => '"', ++), ( ++ map { sprintf('%03o', $_) => chr($_) } (0..255) ++)); ++ ++sub _unescape { ++ my ($diff, $str) = @_; ++ ++ if (exists $ESCAPE{$str}) { ++ return $ESCAPE{$str}; ++ } else { ++ error(_g('diff %s patches file with unknown escape sequence \\%s'), ++ $diff, $str); ++ } ++} ++ ++# Fetch the header filename ignoring the optional timestamp ++sub _fetch_filename { ++ my ($diff, $header) = @_; ++ ++ # Strip any leading spaces. ++ $header =~ s/^\s+//; ++ ++ # Is it a C-style string? ++ if ($header =~ m/^"/) { ++ $header =~ m/^"((?:[^\\"]|\\.)*)"/; ++ error(_g('diff %s patches file with unbalanced quote'), $diff) ++ unless defined $1; ++ ++ $header = $1; ++ $header =~ s/\\([0-3][0-7]{2}|.)/_unescape($diff, $1)/eg; ++ } else { ++ # Tab is the official separator, it's always used when ++ # filename contain spaces. Try it first, otherwise strip on space ++ # if there's no tab ++ $header =~ s/\s.*// unless $header =~ s/\t.*//; ++ } ++ + return $header; + } + +@@ -400,7 +439,7 @@ + unless(s/^--- //) { + error(_g("expected ^--- in line %d of diff `%s'"), $., $diff); + } +- $path{old} = $_ = _strip_ts($_); ++ $path{old} = $_ = _fetch_filename($diff, $_); + $fn{old} = $_ if $_ ne '/dev/null' and s{^[^/]*/+}{$destdir/}; + if (/\.dpkg-orig$/) { + error(_g("diff `%s' patches file with name ending .dpkg-orig"), $diff); +@@ -412,7 +451,7 @@ + unless (s/^\+\+\+ //) { + error(_g("line after --- isn't as expected in diff `%s' (line %d)"), $diff, $.); + } +- $path{new} = $_ = _strip_ts($_); ++ $path{new} = $_ = _fetch_filename($diff, $_); + $fn{new} = $_ if $_ ne '/dev/null' and s{^[^/]*/+}{$destdir/}; + + unless (defined $fn{old} or defined $fn{new}) {
meta/recipes-devtools/dpkg/dpkg_1.17.4.bb +1 -0
@@ -12,6 +12,7 @@ SRC_URI += "file://noman.patch \ file://dpkg-configure.service \ file://glibc2.5-sync_file_range.patch \ file://no-vla-warning.patch \ + file://dpkg-1.17.4-CVE-2014-0471.patch \ " SRC_URI[md5sum] = "cc25086e1e3bd9512a95f14cfe9002e1"
Signature · 1 line
1.7.9.5
PATCH 2/2 Dpkg::Source::Patch: Outright reject C-style filenames in patches
In reply to [email protected]From: Guillem Jover <[email protected]>
based on: commit a12eb58959d0a10584a428f4a3103a49204c410f
Because patch only started recognizing C-style filenames in diffs in version 2.7, it's not safe to assume one behaviour or the other, as the system might or might not have a recent enough version, or a GNU patch program at all. There's also no reason we should be supporting this kind of strange encoded filenames in patches, when we have not done so up to now.
Let's just ban these types of diffs and be done with it.
[drop the text for debian/changelog,because it's not suitable for the veriosn]
--- .../dpkg-1.17.4-CVE-2014-0471-CVE-2014-3127.patch | 49 ++++++++++++++++++++ meta/recipes-devtools/dpkg/dpkg_1.17.4.bb | 1 + 2 files changed, 50 insertions(+) create mode 100644 meta/recipes-devtools/dpkg/dpkg/dpkg-1.17.4-CVE-2014-0471-CVE-2014-3127.patch
meta/recipes-devtools/dpkg/dpkg/dpkg-1.17.4-CVE-2014-0471-CVE-2014-3127.patch new file +49 -0
@@ -0,0 +1,49 @@ +diff -uarN dpkg-1.17.1-org/scripts/Dpkg/Source/Patch.pm dpkg-1.17.1/scripts/Dpkg/Source/Patch.pm +--- dpkg-1.17.1-org/scripts/Dpkg/Source/Patch.pm 2014-06-05 16:32:41.765446564 +0800 ++++ dpkg-1.17.1/scripts/Dpkg/Source/Patch.pm 2014-06-05 16:37:21.461446359 +0800 +@@ -324,31 +324,6 @@ + return $line; + } + +-my %ESCAPE = (( +- 'a' => "\a", +- 'b' => "\b", +- 'f' => "\f", +- 'n' => "\n", +- 'r' => "\r", +- 't' => "\t", +- 'v' => "\cK", +- '\\' => '\\', +- '"' => '"', +-), ( +- map { sprintf('%03o', $_) => chr($_) } (0..255) +-)); +- +-sub _unescape { +- my ($diff, $str) = @_; +- +- if (exists $ESCAPE{$str}) { +- return $ESCAPE{$str}; +- } else { +- error(_g('diff %s patches file with unknown escape sequence \\%s'), +- $diff, $str); +- } +-} +- + # Fetch the header filename ignoring the optional timestamp + sub _fetch_filename { + my ($diff, $header) = @_; +@@ -358,12 +333,7 @@ + + # Is it a C-style string? + if ($header =~ m/^"/) { +- $header =~ m/^"((?:[^\\"]|\\.)*)"/; +- error(_g('diff %s patches file with unbalanced quote'), $diff) +- unless defined $1; +- +- $header = $1; +- $header =~ s/\\([0-3][0-7]{2}|.)/_unescape($diff, $1)/eg; ++ error(_g('diff %s patches file with C-style encoded filename'), $diff); + } else { + # Tab is the official separator, it's always used when + # filename contain spaces. Try it first, otherwise strip on space
meta/recipes-devtools/dpkg/dpkg_1.17.4.bb +1 -0
@@ -13,6 +13,7 @@ SRC_URI += "file://noman.patch \ file://glibc2.5-sync_file_range.patch \ file://no-vla-warning.patch \ file://dpkg-1.17.4-CVE-2014-0471.patch \ + file://dpkg-1.17.4-CVE-2014-0471-CVE-2014-3127.patch \ " SRC_URI[md5sum] = "cc25086e1e3bd9512a95f14cfe9002e1"
Signature · 1 line
1.7.9.5