fix for Zip Slip
@@ -4,7 +4,7 @@ | ||
4 | 4 | <!-- |
5 | 5 | Hamigaki.Archivers Library Document Source |
6 | 6 | |
7 | - Copyright Takeshi Mouri 2006-2008. | |
7 | + Copyright Takeshi Mouri 2006-2018. | |
8 | 8 | Distributed under the Boost Software License, Version 1.0. |
9 | 9 | (See accompanying file LICENSE_1_0.txt or copy at |
10 | 10 | http://www.boost.org/LICENSE_1_0.txt) |
@@ -78,9 +78,25 @@ | ||
78 | 78 | namespace fs = boost::filesystem; |
79 | 79 | namespace io = boost::iostreams; |
80 | 80 | |
81 | +bool is_valid_path(const fs::path& ph) | |
82 | +{ | |
83 | + if (ph.has_root_name() || ph.has_root_directory()) | |
84 | + return false; | |
85 | + for (typename Path::iterator it = ph.begin(); it != ph.end(); ++it) | |
86 | + { | |
87 | + if (*it == "..") | |
88 | + return false; | |
89 | + } | |
90 | + return true; | |
91 | +} | |
92 | + | |
81 | 93 | void extract_file(ar::tar_file_source& tar) |
82 | 94 | { |
83 | 95 | const ar::tar::header& head = tar.header(); |
96 | + if (!is_valid_path(head.path)) | |
97 | + { | |
98 | + std::cerr << "Warning: skip invalid path: " << head.path.string() << std::endl; | |
99 | + } | |
84 | 100 | |
85 | 101 | fs::ofstream file(head.path, std::ios_base::binary); |
86 | 102 | io::copy(tar, file); |
@@ -99,7 +115,7 @@ | ||
99 | 115 | |
100 | 116 | <para>tarファイルのオープンに成功したら、メンバ関数<methodname>next_entry</methodname>()を呼び出し、次のエントリを確認する。この関数は未読のエントリがある場合<code>true</code>を返す。全てのエントリを読み終わり、これ以上のエントリが見つからない場合は<code>false</code>が返され、ループは終了する。</para> |
101 | 117 | |
102 | - <para><methodname>next_entry</methodname>()の呼び出しに成功したら、アーカイブは<conceptname>Source</conceptname>として読み出し可能である。また、メンバ関数<methodname>header</methodname>()を呼び出すことで、ヘッダ情報を取得することもできる。ここでは、ヘッダから取得したパス名と同名のファイルを作成し、エントリから読み出した内容を書き出している。</para> | |
118 | + <para><methodname>next_entry</methodname>()の呼び出しに成功したら、アーカイブは<conceptname>Source</conceptname>として読み出し可能である。また、メンバ関数<methodname>header</methodname>()を呼び出すことで、ヘッダ情報を取得することもできる。ここでは、ヘッダから取得したパス名と同名のファイルを作成し、エントリから読み出した内容を書き出している。特に意図していない限り、絶対パスやカレントディレクトリより上への相対パスを含むアーカイブは悪意のあるものである。ファイルシステムへ出力する前には必ずパス名を検証する必要がある。</para> | |
103 | 119 | </section> |
104 | 120 | |
105 | 121 | <section id="archivers.tutorial.zip"> |
@@ -1,6 +1,6 @@ | ||
1 | 1 | // extract.cpp: multi-format extractor |
2 | 2 | |
3 | -// Copyright Takeshi Mouri 2006-2008. | |
3 | +// Copyright Takeshi Mouri 2006-2018. | |
4 | 4 | // Distributed under the Boost Software License, Version 1.0. |
5 | 5 | // (See accompanying file LICENSE_1_0.txt or copy at |
6 | 6 | // http://www.boost.org/LICENSE_1_0.txt) |
@@ -34,6 +34,21 @@ | ||
34 | 34 | namespace fs = boost::filesystem; |
35 | 35 | namespace io = boost::iostreams; |
36 | 36 | |
37 | +template<class Path> | |
38 | +bool is_valid_path(const Path& ph) | |
39 | +{ | |
40 | +#if !defined(HAMIGAKI_ALLOW_DIRECTORY_TRAVERSAL) | |
41 | + if (ph.has_root_name() || ph.has_root_directory()) | |
42 | + return false; | |
43 | + for (typename Path::iterator it = ph.begin(); it != ph.end(); ++it) | |
44 | + { | |
45 | + if (*it == "..") | |
46 | + return false; | |
47 | + } | |
48 | +#endif | |
49 | + return true; | |
50 | +} | |
51 | + | |
37 | 52 | struct entry |
38 | 53 | { |
39 | 54 | fs_ex::file_type type; |
@@ -367,6 +382,11 @@ | ||
367 | 382 | const entry& e = ext_ptr->current_entry(); |
368 | 383 | |
369 | 384 | std::cout << e.path.string() << '\n'; |
385 | + if (!is_valid_path(e.path)) | |
386 | + { | |
387 | + std::cerr << "Warning: invalid path" << '\n'; | |
388 | + continue; | |
389 | + } | |
370 | 390 | |
371 | 391 | if (!e.hard_link_path.empty()) |
372 | 392 | fs_ex::create_hard_link(e.hard_link_path, e.path); |
@@ -1,6 +1,6 @@ | ||
1 | 1 | // uniso.cpp: a simple ISO image extractor program |
2 | 2 | |
3 | -// Copyright Takeshi Mouri 2007, 2008. | |
3 | +// Copyright Takeshi Mouri 2007, 2018. | |
4 | 4 | // Distributed under the Boost Software License, Version 1.0. |
5 | 5 | // (See accompanying file LICENSE_1_0.txt or copy at |
6 | 6 | // http://www.boost.org/LICENSE_1_0.txt) |
@@ -38,6 +38,21 @@ | ||
38 | 38 | #endif |
39 | 39 | } |
40 | 40 | |
41 | +template<class Path> | |
42 | +bool is_valid_path(const Path& ph) | |
43 | +{ | |
44 | +#if !defined(HAMIGAKI_ALLOW_DIRECTORY_TRAVERSAL) | |
45 | + if (ph.has_root_name() || ph.has_root_directory()) | |
46 | + return false; | |
47 | + for (typename Path::iterator it = ph.begin(); it != ph.end(); ++it) | |
48 | + { | |
49 | + if (*it == "..") | |
50 | + return false; | |
51 | + } | |
52 | +#endif | |
53 | + return true; | |
54 | +} | |
55 | + | |
41 | 56 | int main(int argc, char* argv[]) |
42 | 57 | { |
43 | 58 | try |
@@ -77,6 +92,11 @@ | ||
77 | 92 | const ar::iso::header& head = iso.header(); |
78 | 93 | |
79 | 94 | std::cout << head.path.string() << '\n'; |
95 | + if (!is_valid_path(head.path)) | |
96 | + { | |
97 | + std::cerr << "Warning: invalid path" << '\n'; | |
98 | + continue; | |
99 | + } | |
80 | 100 | |
81 | 101 | if (head.is_symlink()) |
82 | 102 | std::cout << "-> " << head.link_path.string() << '\n'; |
@@ -1,6 +1,6 @@ | ||
1 | 1 | // unlha.cpp: a simple LZH decompressing program |
2 | 2 | |
3 | -// Copyright Takeshi Mouri 2006-2008. | |
3 | +// Copyright Takeshi Mouri 2006-2018. | |
4 | 4 | // Distributed under the Boost Software License, Version 1.0. |
5 | 5 | // (See accompanying file LICENSE_1_0.txt or copy at |
6 | 6 | // http://www.boost.org/LICENSE_1_0.txt) |
@@ -38,6 +38,21 @@ | ||
38 | 38 | #endif |
39 | 39 | } |
40 | 40 | |
41 | +template<class Path> | |
42 | +bool is_valid_path(const Path& ph) | |
43 | +{ | |
44 | +#if !defined(HAMIGAKI_ALLOW_DIRECTORY_TRAVERSAL) | |
45 | + if (ph.has_root_name() || ph.has_root_directory()) | |
46 | + return false; | |
47 | + for (typename Path::iterator it = ph.begin(); it != ph.end(); ++it) | |
48 | + { | |
49 | + if (*it == "..") | |
50 | + return false; | |
51 | + } | |
52 | +#endif | |
53 | + return true; | |
54 | +} | |
55 | + | |
41 | 56 | inline fs_ex::timestamp make_timestamp(boost::uint64_t ft) |
42 | 57 | { |
43 | 58 | return fs_ex::timestamp::from_windows_file_time(ft); |
@@ -62,6 +77,11 @@ | ||
62 | 77 | const ar::lha::header& head = lzh.header(); |
63 | 78 | |
64 | 79 | std::cout << head.path.string() << '\n'; |
80 | + if (!is_valid_path(head.path)) | |
81 | + { | |
82 | + std::cerr << "Warning: invalid path" << '\n'; | |
83 | + continue; | |
84 | + } | |
65 | 85 | |
66 | 86 | if (!head.link_path.empty()) |
67 | 87 | { |
@@ -1,6 +1,6 @@ | ||
1 | 1 | // untar.cpp: a simple ZIP decompressing program |
2 | 2 | |
3 | -// Copyright Takeshi Mouri 2006-2008. | |
3 | +// Copyright Takeshi Mouri 2006-2018. | |
4 | 4 | // Distributed under the Boost Software License, Version 1.0. |
5 | 5 | // (See accompanying file LICENSE_1_0.txt or copy at |
6 | 6 | // http://www.boost.org/LICENSE_1_0.txt) |
@@ -38,6 +38,21 @@ | ||
38 | 38 | #endif |
39 | 39 | } |
40 | 40 | |
41 | +template<class Path> | |
42 | +bool is_valid_path(const Path& ph) | |
43 | +{ | |
44 | +#if !defined(HAMIGAKI_ALLOW_DIRECTORY_TRAVERSAL) | |
45 | + if (ph.has_root_name() || ph.has_root_directory()) | |
46 | + return false; | |
47 | + for (typename Path::iterator it = ph.begin(); it != ph.end(); ++it) | |
48 | + { | |
49 | + if (*it == "..") | |
50 | + return false; | |
51 | + } | |
52 | +#endif | |
53 | + return true; | |
54 | +} | |
55 | + | |
41 | 56 | int main(int argc, char* argv[]) |
42 | 57 | { |
43 | 58 | try |
@@ -57,6 +72,11 @@ | ||
57 | 72 | const ar::tar::header& head = tar.header(); |
58 | 73 | |
59 | 74 | std::cout << head.path.string() << '\n'; |
75 | + if (!is_valid_path(head.path)) | |
76 | + { | |
77 | + std::cerr << "Warning: invalid path" << '\n'; | |
78 | + continue; | |
79 | + } | |
60 | 80 | |
61 | 81 | if (head.type_flag == ar::tar::type_flag::link) |
62 | 82 | { |
@@ -1,6 +1,6 @@ | ||
1 | 1 | // unzip.cpp: a simple ZIP decompressing program |
2 | 2 | |
3 | -// Copyright Takeshi Mouri 2006-2008. | |
3 | +// Copyright Takeshi Mouri 2006-2018. | |
4 | 4 | // Distributed under the Boost Software License, Version 1.0. |
5 | 5 | // (See accompanying file LICENSE_1_0.txt or copy at |
6 | 6 | // http://www.boost.org/LICENSE_1_0.txt) |
@@ -38,6 +38,21 @@ | ||
38 | 38 | #endif |
39 | 39 | } |
40 | 40 | |
41 | +template<class Path> | |
42 | +bool is_valid_path(const Path& ph) | |
43 | +{ | |
44 | +#if !defined(HAMIGAKI_ALLOW_DIRECTORY_TRAVERSAL) | |
45 | + if (ph.has_root_name() || ph.has_root_directory()) | |
46 | + return false; | |
47 | + for (typename Path::iterator it = ph.begin(); it != ph.end(); ++it) | |
48 | + { | |
49 | + if (*it == "..") | |
50 | + return false; | |
51 | + } | |
52 | +#endif | |
53 | + return true; | |
54 | +} | |
55 | + | |
41 | 56 | inline fs_ex::timestamp make_timestamp(std::time_t t) |
42 | 57 | { |
43 | 58 | return fs_ex::timestamp::from_time_t(t); |
@@ -62,6 +77,11 @@ | ||
62 | 77 | const ar::zip::header& head = zip.header(); |
63 | 78 | |
64 | 79 | std::cout << head.path.string() << '\n'; |
80 | + if (!is_valid_path(head.path)) | |
81 | + { | |
82 | + std::cerr << "Warning: invalid path" << '\n'; | |
83 | + continue; | |
84 | + } | |
65 | 85 | |
66 | 86 | if (!head.link_path.empty()) |
67 | 87 | { |
@@ -1,6 +1,6 @@ | ||
1 | 1 | // uniso.cpp: a simple ISO image extractor program (Unicode version) |
2 | 2 | |
3 | -// Copyright Takeshi Mouri 2008. | |
3 | +// Copyright Takeshi Mouri 2008-2018. | |
4 | 4 | // Distributed under the Boost Software License, Version 1.0. |
5 | 5 | // (See accompanying file LICENSE_1_0.txt or copy at |
6 | 6 | // http://www.boost.org/LICENSE_1_0.txt) |
@@ -39,6 +39,21 @@ | ||
39 | 39 | #endif |
40 | 40 | } |
41 | 41 | |
42 | +template<class Path> | |
43 | +bool is_valid_path(const Path& ph) | |
44 | +{ | |
45 | +#if !defined(HAMIGAKI_ALLOW_DIRECTORY_TRAVERSAL) | |
46 | + if (ph.has_root_name() || ph.has_root_directory()) | |
47 | + return false; | |
48 | + for (typename Path::iterator it = ph.begin(); it != ph.end(); ++it) | |
49 | + { | |
50 | + if (*it == L"..") | |
51 | + return false; | |
52 | + } | |
53 | +#endif | |
54 | + return true; | |
55 | +} | |
56 | + | |
42 | 57 | int main(int argc, char* argv[]) |
43 | 58 | { |
44 | 59 | try |
@@ -78,6 +93,11 @@ | ||
78 | 93 | const ar::iso::wheader& head = iso.header(); |
79 | 94 | |
80 | 95 | std::wcout << head.path << '\n'; |
96 | + if (!is_valid_path(head.path)) | |
97 | + { | |
98 | + std::cerr << "Warning: invalid path" << '\n'; | |
99 | + continue; | |
100 | + } | |
81 | 101 | |
82 | 102 | if (head.is_symlink()) |
83 | 103 | std::wcout << "-> " << head.link_path << '\n'; |
@@ -1,6 +1,6 @@ | ||
1 | 1 | // wunlha.cpp: a simple LZH decompressing program (Unicode version) |
2 | 2 | |
3 | -// Copyright Takeshi Mouri 2008. | |
3 | +// Copyright Takeshi Mouri 2008-2018. | |
4 | 4 | // Distributed under the Boost Software License, Version 1.0. |
5 | 5 | // (See accompanying file LICENSE_1_0.txt or copy at |
6 | 6 | // http://www.boost.org/LICENSE_1_0.txt) |
@@ -39,6 +39,21 @@ | ||
39 | 39 | #endif |
40 | 40 | } |
41 | 41 | |
42 | +template<class Path> | |
43 | +bool is_valid_path(const Path& ph) | |
44 | +{ | |
45 | +#if !defined(HAMIGAKI_ALLOW_DIRECTORY_TRAVERSAL) | |
46 | + if (ph.has_root_name() || ph.has_root_directory()) | |
47 | + return false; | |
48 | + for (typename Path::iterator it = ph.begin(); it != ph.end(); ++it) | |
49 | + { | |
50 | + if (*it == L"..") | |
51 | + return false; | |
52 | + } | |
53 | +#endif | |
54 | + return true; | |
55 | +} | |
56 | + | |
42 | 57 | inline fs_ex::timestamp make_timestamp(boost::uint64_t ft) |
43 | 58 | { |
44 | 59 | return fs_ex::timestamp::from_windows_file_time(ft); |
@@ -63,6 +78,11 @@ | ||
63 | 78 | const ar::lha::wheader& head = lzh.header(); |
64 | 79 | |
65 | 80 | std::wcout << head.path << '\n'; |
81 | + if (!is_valid_path(head.path)) | |
82 | + { | |
83 | + std::cerr << "Warning: invalid path" << '\n'; | |
84 | + continue; | |
85 | + } | |
66 | 86 | |
67 | 87 | if (!head.link_path.empty()) |
68 | 88 | { |
@@ -1,6 +1,6 @@ | ||
1 | 1 | // untar.cpp: a simple tar extracting program (Unicode version) |
2 | 2 | |
3 | -// Copyright Takeshi Mouri 2008. | |
3 | +// Copyright Takeshi Mouri 2008-2018. | |
4 | 4 | // Distributed under the Boost Software License, Version 1.0. |
5 | 5 | // (See accompanying file LICENSE_1_0.txt or copy at |
6 | 6 | // http://www.boost.org/LICENSE_1_0.txt) |
@@ -39,6 +39,21 @@ | ||
39 | 39 | #endif |
40 | 40 | } |
41 | 41 | |
42 | +template<class Path> | |
43 | +bool is_valid_path(const Path& ph) | |
44 | +{ | |
45 | +#if !defined(HAMIGAKI_ALLOW_DIRECTORY_TRAVERSAL) | |
46 | + if (ph.has_root_name() || ph.has_root_directory()) | |
47 | + return false; | |
48 | + for (typename Path::iterator it = ph.begin(); it != ph.end(); ++it) | |
49 | + { | |
50 | + if (*it == L"..") | |
51 | + return false; | |
52 | + } | |
53 | +#endif | |
54 | + return true; | |
55 | +} | |
56 | + | |
42 | 57 | int main(int argc, char* argv[]) |
43 | 58 | { |
44 | 59 | try |
@@ -58,6 +73,11 @@ | ||
58 | 73 | const ar::tar::wheader& head = tar.header(); |
59 | 74 | |
60 | 75 | std::wcout << head.path << std::endl; |
76 | + if (!is_valid_path(head.path)) | |
77 | + { | |
78 | + std::cerr << "Warning: invalid path" << '\n'; | |
79 | + continue; | |
80 | + } | |
61 | 81 | |
62 | 82 | if (head.type_flag == ar::tar::type_flag::link) |
63 | 83 | { |
@@ -1,6 +1,6 @@ | ||
1 | 1 | // wunzip.cpp: a simple ZIP decompressing program (Unicode version) |
2 | 2 | |
3 | -// Copyright Takeshi Mouri 2008. | |
3 | +// Copyright Takeshi Mouri 2008-2018. | |
4 | 4 | // Distributed under the Boost Software License, Version 1.0. |
5 | 5 | // (See accompanying file LICENSE_1_0.txt or copy at |
6 | 6 | // http://www.boost.org/LICENSE_1_0.txt) |
@@ -39,6 +39,21 @@ | ||
39 | 39 | #endif |
40 | 40 | } |
41 | 41 | |
42 | +template<class Path> | |
43 | +bool is_valid_path(const Path& ph) | |
44 | +{ | |
45 | +#if !defined(HAMIGAKI_ALLOW_DIRECTORY_TRAVERSAL) | |
46 | + if (ph.has_root_name() || ph.has_root_directory()) | |
47 | + return false; | |
48 | + for (typename Path::iterator it = ph.begin(); it != ph.end(); ++it) | |
49 | + { | |
50 | + if (*it == L"..") | |
51 | + return false; | |
52 | + } | |
53 | +#endif | |
54 | + return true; | |
55 | +} | |
56 | + | |
42 | 57 | inline fs_ex::timestamp make_timestamp(std::time_t t) |
43 | 58 | { |
44 | 59 | return fs_ex::timestamp::from_time_t(t); |
@@ -63,6 +78,11 @@ | ||
63 | 78 | const ar::zip::wheader& head = zip.header(); |
64 | 79 | |
65 | 80 | std::wcout << head.path << '\n'; |
81 | + if (!is_valid_path(head.path)) | |
82 | + { | |
83 | + std::cerr << "Warning: invalid path" << '\n'; | |
84 | + continue; | |
85 | + } | |
66 | 86 | |
67 | 87 | if (!head.link_path.empty()) |
68 | 88 | { |