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