Bug#802702: CVE-2011-5325: busybox: Directory traversal via crafted tar file which contains a symlink pointing outside of the current directory
tags 802702 + patch
forwarded 802702 https://bugs.busybox.net/show_bug.cgi?id=8411#c2
thanks
Patch attached & sent upstream.
Regards,
--
,''`.
: :' : Chris Lamb
`. `'` lamby@debian.org / chris-lamb.co.uk
`-
diff --git a/archival/libarchive/data_extract_all.c b/archival/libarchive/data_extract_all.c
index 45776dc..534c7fc 100644
--- a/archival/libarchive/data_extract_all.c
+++ b/archival/libarchive/data_extract_all.c
@@ -142,7 +142,40 @@ void FAST_FUNC data_extract_all(archive_handle_t *archive_handle)
break;
case S_IFLNK:
/* Symlink */
-//TODO: what if file_header->link_target == NULL (say, corrupted tarball?)
+
+ /* To avoid a directory traversal attack via symlinks, for
+ * certain link targets first write placeholder regular files
+ * containing the intended target and mark to replace them
+ * later.
+ *
+ * For example, consider a .tar created via:
+ *
+ * $ tar cvf bug.tar anything.txt
+ * $ ln -s /tmp symlink
+ * $ tar --append -f bug.tar symlink
+ * $ rm symlink
+ * $ mkdir symlink
+ * $ tar --append -f bug.tar symlink/evil.py
+ *
+ * This will result in an archive that contains:
+ *
+ * $ tar --list -f bug.tar
+ * anything.txt
+ * symlink [-> /tmp]
+ * symlink/evil.py
+ *
+ * Untarring bug.tar would otherwise place evil.py in '/tmp'.
+ */
+ if ((!strncmp(file_header->link_target, "/", 1))
+ || (strstr(file_header->link_target, "../"))) {
+ dst_fd = xopen(file_header->name, O_WRONLY | O_CREAT | O_EXCL);
+ xwrite(dst_fd, file_header->link_target,
+ strlen(file_header->link_target));
+ close(dst_fd);
+ llist_add_to_end(&archive_handle->symlink_placeholders,
+ strdup(file_header->name));
+ break;
+ }
res = symlink(file_header->link_target, file_header->name);
if ((res == -1)
&& !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET)
diff --git a/archival/tar.c b/archival/tar.c
index bd61abd..49fc93f 100644
--- a/archival/tar.c
+++ b/archival/tar.c
@@ -22,24 +22,6 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
-/* TODO: security with -C DESTDIR option can be enhanced.
- * Consider tar file created via:
- * $ tar cvf bug.tar anything.txt
- * $ ln -s /tmp symlink
- * $ tar --append -f bug.tar symlink
- * $ rm symlink
- * $ mkdir symlink
- * $ tar --append -f bug.tar symlink/evil.py
- *
- * This will result in an archive which contains:
- * $ tar --list -f bug.tar
- * anything.txt
- * symlink
- * symlink/evil.py
- *
- * Untarring it puts evil.py in '/tmp' even if the -C DESTDIR is given.
- * This doesn't feel right, and IIRC GNU tar doesn't do that.
- */
//config:config TAR
//config: bool "tar"
@@ -309,6 +291,20 @@ static void chksum_and_xwrite(int fd, struct tar_header_t* hp)
xwrite(fd, hp, sizeof(*hp));
}
+static void replace_symlink_placeholders(llist_t *list) {
+ while (list) {
+ char *linkpath = list->data;
+ char *target = xmalloc_xopen_read_close(linkpath, NULL);
+ if (unlink(linkpath) == -1)
+ bb_error_msg_and_die("can't remove placeholder %s",
+ linkpath);
+ if (symlink(target, linkpath) == -1)
+ bb_error_msg_and_die("can't create link: %s -> %s",
+ linkpath, target);
+ list = list->link;
+ }
+}
+
#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
static void writeLongname(int fd, int type, const char *name, int dir)
{
@@ -1205,6 +1201,8 @@ int tar_main(int argc UNUSED_PARAM, char **argv)
while (get_header_tar(tar_handle) == EXIT_SUCCESS)
bb_got_signal = EXIT_SUCCESS; /* saw at least one header, good */
+ replace_symlink_placeholders(tar_handle->symlink_placeholders);
+
/* Check that every file that should have been extracted was */
while (tar_handle->accept) {
if (!find_list_entry(tar_handle->reject, tar_handle->accept->data)
diff --git a/include/bb_archive.h b/include/bb_archive.h
index b82cfd8..bcaed7f 100644
--- a/include/bb_archive.h
+++ b/include/bb_archive.h
@@ -64,6 +64,9 @@ typedef struct archive_handle_t {
/* Currently processed file's header */
file_header_t *file_header;
+ /* List of symlink placeholders */
+ llist_t *symlink_placeholders;
+
/* Process the header component, e.g. tar -t */
void FAST_FUNC (*action_header)(const file_header_t *);
@@ -182,6 +185,7 @@ char get_header_ar(archive_handle_t *archive_handle) FAST_FUNC;
char get_header_cpio(archive_handle_t *archive_handle) FAST_FUNC;
char get_header_tar(archive_handle_t *archive_handle) FAST_FUNC;
char get_header_tar_gz(archive_handle_t *archive_handle) FAST_FUNC;
+char get_header_tar_xz(archive_handle_t *archive_handle) FAST_FUNC;
char get_header_tar_bz2(archive_handle_t *archive_handle) FAST_FUNC;
char get_header_tar_lzma(archive_handle_t *archive_handle) FAST_FUNC;
Reply to: