|
| 1 | +From 9a8796436b9b0641e13480811902ea2ac57881d3 Mon Sep 17 00:00:00 2001 |
| 2 | +From: Denys Vlasenko < [email protected]> |
| 3 | +Date: Wed, 2 Oct 2024 10:12:05 +0200 |
| 4 | +Subject: archival: disallow path traversals (CVE-2023-39810) |
| 5 | + |
| 6 | +Create new configure option for archival/libarchive based extractions to |
| 7 | +disallow path traversals. |
| 8 | +As this is a paranoid option and might introduce backward |
| 9 | +incompatibility, default it to no. |
| 10 | + |
| 11 | +Fixes: CVE-2023-39810 |
| 12 | + |
| 13 | +Based on the patch by Peter Kaestle < [email protected]> |
| 14 | + |
| 15 | +function old new delta |
| 16 | +data_extract_all 921 945 +24 |
| 17 | +strip_unsafe_prefix 101 102 +1 |
| 18 | +------------------------------------------------------------------------------ |
| 19 | +(add/remove: 0/0 grow/shrink: 2/0 up/down: 25/0) Total: 25 bytes |
| 20 | + |
| 21 | +Signed-off-by: Denys Vlasenko < [email protected]> |
| 22 | +--- |
| 23 | + archival/Config.src | 11 +++++++++++ |
| 24 | + archival/libarchive/data_extract_all.c | 8 ++++++++ |
| 25 | + archival/libarchive/unsafe_prefix.c | 6 +++++- |
| 26 | + scripts/kconfig/lxdialog/check-lxdialog.sh | 2 +- |
| 27 | + testsuite/cpio.tests | 23 +++++++++++++++++++++++ |
| 28 | + 5 files changed, 48 insertions(+), 2 deletions(-) |
| 29 | + |
| 30 | +diff --git a/archival/Config.src b/archival/Config.src |
| 31 | +index 6f4f30c43..cbcd7217c 100644 |
| 32 | +--- a/archival/Config.src |
| 33 | ++++ b/archival/Config.src |
| 34 | +@@ -35,4 +35,15 @@ config FEATURE_LZMA_FAST |
| 35 | + This option reduces decompression time by about 25% at the cost of |
| 36 | + a 1K bigger binary. |
| 37 | + |
| 38 | ++config FEATURE_PATH_TRAVERSAL_PROTECTION |
| 39 | ++ bool "Prevent extraction of filenames with /../ path component" |
| 40 | ++ default n |
| 41 | ++ help |
| 42 | ++ busybox tar and unzip remove "PREFIX/../" (if it exists) |
| 43 | ++ from extracted names. |
| 44 | ++ This option enables this behavior for all other unpacking applets, |
| 45 | ++ such as cpio, ar, rpm. |
| 46 | ++ GNU cpio 2.15 has NO such sanity check. |
| 47 | ++# try other archivers and document their behavior? |
| 48 | ++ |
| 49 | + endmenu |
| 50 | +diff --git a/archival/libarchive/data_extract_all.c b/archival/libarchive/data_extract_all.c |
| 51 | +index 049c2c156..8a69711c1 100644 |
| 52 | +--- a/archival/libarchive/data_extract_all.c |
| 53 | ++++ b/archival/libarchive/data_extract_all.c |
| 54 | +@@ -65,6 +65,14 @@ void FAST_FUNC data_extract_all(archive_handle_t *archive_handle) |
| 55 | + } while (--n != 0); |
| 56 | + } |
| 57 | + #endif |
| 58 | ++#if ENABLE_FEATURE_PATH_TRAVERSAL_PROTECTION |
| 59 | ++ /* Strip leading "/" and up to last "/../" path component */ |
| 60 | ++ dst_name = (char *)strip_unsafe_prefix(dst_name); |
| 61 | ++#endif |
| 62 | ++// ^^^ This may be a problem if some applets do need to extract absolute names. |
| 63 | ++// (Probably will need to invent ARCHIVE_ALLOW_UNSAFE_NAME flag). |
| 64 | ++// You might think that rpm needs it, but in my tests rpm's internal cpio |
| 65 | ++// archive has names like "./usr/bin/FOO", not "/usr/bin/FOO". |
| 66 | + |
| 67 | + if (archive_handle->ah_flags & ARCHIVE_CREATE_LEADING_DIRS) { |
| 68 | + char *slash = strrchr(dst_name, '/'); |
| 69 | +diff --git a/archival/libarchive/unsafe_prefix.c b/archival/libarchive/unsafe_prefix.c |
| 70 | +index 33e487bf9..667081195 100644 |
| 71 | +--- a/archival/libarchive/unsafe_prefix.c |
| 72 | ++++ b/archival/libarchive/unsafe_prefix.c |
| 73 | +@@ -14,7 +14,11 @@ const char* FAST_FUNC strip_unsafe_prefix(const char *str) |
| 74 | + cp++; |
| 75 | + continue; |
| 76 | + } |
| 77 | +- if (is_prefixed_with(cp, "/../"+1)) { |
| 78 | ++ /* We are called lots of times. |
| 79 | ++ * is_prefixed_with(cp, "../") is slower than open-coding it, |
| 80 | ++ * with minimal code growth (~few bytes). |
| 81 | ++ */ |
| 82 | ++ if (cp[0] == '.' && cp[1] == '.' && cp[2] == '/') { |
| 83 | + cp += 3; |
| 84 | + continue; |
| 85 | + } |
| 86 | +diff --git a/scripts/kconfig/lxdialog/check-lxdialog.sh b/scripts/kconfig/lxdialog/check-lxdialog.sh |
| 87 | +index 5075ebf2d..910ca1f7c 100755 |
| 88 | +--- a/scripts/kconfig/lxdialog/check-lxdialog.sh |
| 89 | ++++ b/scripts/kconfig/lxdialog/check-lxdialog.sh |
| 90 | +@@ -47,7 +47,7 @@ trap "rm -f $tmp" 0 1 2 3 15 |
| 91 | + check() { |
| 92 | + $cc -x c - -o $tmp 2>/dev/null <<'EOF' |
| 93 | + #include CURSES_LOC |
| 94 | +-main() {} |
| 95 | ++int main() { return 0; } |
| 96 | + EOF |
| 97 | + if [ $? != 0 ]; then |
| 98 | + echo " *** Unable to find the ncurses libraries or the" 1>&2 |
| 99 | +diff --git a/testsuite/cpio.tests b/testsuite/cpio.tests |
| 100 | +index 85e746589..a4462c53e 100755 |
| 101 | +--- a/testsuite/cpio.tests |
| 102 | ++++ b/testsuite/cpio.tests |
| 103 | +@@ -154,6 +154,29 @@ testing "cpio -R with extract" \ |
| 104 | + " "" "" |
| 105 | + SKIP= |
| 106 | + |
| 107 | ++# Create an archive containing a file with "../dont_write" filename. |
| 108 | ++# See that it will not be allowed to unpack. |
| 109 | ++# NB: GNU cpio 2.15 DOES NOT do such checks. |
| 110 | ++optional FEATURE_PATH_TRAVERSAL_PROTECTION |
| 111 | ++rm -rf cpio.testdir |
| 112 | ++mkdir -p cpio.testdir/prepare/inner |
| 113 | ++echo "file outside of destination was written" > cpio.testdir/prepare/dont_write |
| 114 | ++echo "data" > cpio.testdir/prepare/inner/to_extract |
| 115 | ++mkdir -p cpio.testdir/extract |
| 116 | ++testing "cpio extract file outside of destination" "\ |
| 117 | ++(cd cpio.testdir/prepare/inner && echo -e '../dont_write\nto_extract' | cpio -o -H newc) | (cd cpio.testdir/extract && cpio -vi 2>&1) |
| 118 | ++echo \$? |
| 119 | ++ls cpio.testdir/dont_write 2>&1" \ |
| 120 | ++"\ |
| 121 | ++cpio: removing leading '../' from member names |
| 122 | ++../dont_write |
| 123 | ++to_extract |
| 124 | ++1 blocks |
| 125 | ++0 |
| 126 | ++ls: cpio.testdir/dont_write: No such file or directory |
| 127 | ++" "" "" |
| 128 | ++SKIP= |
| 129 | ++ |
| 130 | + # Clean up |
| 131 | + rm -rf cpio.testdir cpio.testdir2 2>/dev/null |
| 132 | + |
| 133 | +-- |
| 134 | +cgit v1.2.3 |
| 135 | + |
0 commit comments