From b5832439b1f37331fb4f87e67fe4f61ca26bf7d4 Mon Sep 17 00:00:00 2001 From: Michiel Rook Date: Sun, 18 Jul 2021 17:21:58 +0200 Subject: [PATCH] Properly fix symbolic link path traversal (CVE-2021-32610) --- Archive/Tar.php | 50 ++++++++++++++++++++++++++++--------------------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/Archive/Tar.php b/Archive/Tar.php index a8c9501..3356ad6 100644 --- a/Archive/Tar.php +++ b/Archive/Tar.php @@ -2124,25 +2124,40 @@ public function _extractList( } } } elseif ($v_header['typeflag'] == "2") { + if (!$p_symlinks) { + $this->_warning('Symbolic links are not allowed. ' + . 'Unable to extract {' + . $v_header['filename'] . '}' + ); + return false; + } + $absolute_link = FALSE; $link_depth = 0; - foreach (explode("/", $v_header['filename']) as $dir) { - if ($dir === "..") { - $link_depth--; - } elseif ($dir !== "" && $dir !== "." ) { - $link_depth++; - } + if (strpos($v_header['link'], "/") === 0 || strpos($v_header['link'], ':') !== FALSE) { + $absolute_link = TRUE; } - foreach (explode("/", $v_header['link']) as $dir){ - if ($link_depth <= 0) { - break; + else { + $s_filename = preg_replace('@^' . preg_quote($p_path) . '@', "", $v_header['filename']); + $s_linkname = str_replace('\\', '/', $v_header['link']); + foreach (explode("/", $s_filename) as $dir) { + if ($dir === "..") { + $link_depth--; + } elseif ($dir !== "" && $dir !== "." ) { + $link_depth++; + } } - if ($dir === "..") { - $link_depth--; - } elseif ($dir !== "" && $dir !== ".") { - $link_depth++; + foreach (explode("/", $s_linkname) as $dir){ + if ($link_depth <= 0) { + break; + } + if ($dir === "..") { + $link_depth--; + } elseif ($dir !== "" && $dir !== ".") { + $link_depth++; + } } } - if (strpos($v_header['link'], "/") === 0 or $link_depth <= 0) { + if ($absolute_link || $link_depth <= 0) { $this->_error( 'Out-of-path file extraction {' . $v_header['filename'] . ' --> ' . @@ -2150,13 +2165,6 @@ public function _extractList( ); return false; } - if (!$p_symlinks) { - $this->_warning('Symbolic links are not allowed. ' - . 'Unable to extract {' - . $v_header['filename'] . '}' - ); - return false; - } if (@file_exists($v_header['filename'])) { @unlink($v_header['filename']); }