summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIngela Anderton Andin <ingela@erlang.org>2021-02-18 11:40:16 +0100
committerIngela Anderton Andin <ingela@erlang.org>2021-02-23 09:10:20 +0100
commit39f14943f2aaa2c551d6402c73583fe213d6ed87 (patch)
tree86eb31be8cd8f96ebfba81dc22264e0820bbca77
parent5a3d03ee2c12f4c93cf7375abdb3c04f7423f76d (diff)
downloaderlang-39f14943f2aaa2c551d6402c73583fe213d6ed87.tar.gz
inets,httpd: Assure relative paths can not be used to bypass server root directory
Use of uri_string:percent_decode in commmit 5f83a9d719afac0718bff524df6654c9fc94aad5 needs to be combined with uri_string:normalize to assure valid paths. Solves CVE-2021-27563
-rw-r--r--lib/inets/src/http_server/mod_alias.erl24
-rw-r--r--lib/inets/test/httpd_SUITE.erl45
2 files changed, 61 insertions, 8 deletions
diff --git a/lib/inets/src/http_server/mod_alias.erl b/lib/inets/src/http_server/mod_alias.erl
index 35da39c53c..cb2d2c1ef4 100644
--- a/lib/inets/src/http_server/mod_alias.erl
+++ b/lib/inets/src/http_server/mod_alias.erl
@@ -195,22 +195,30 @@ append_index(RealName, [Index | Rest]) ->
%% path
-path(Data, ConfigDB, RequestURI) ->
- InitPath =
+path(Data, ConfigDB, RequestURI0) ->
case proplists:get_value(real_name, Data) of
undefined ->
- {Prefix, DocumentRoot} = which_document_root(ConfigDB),
- {Path, _AfterPath} =
+ {Prefix, DocumentRoot} = which_document_root(ConfigDB),
+ RequestURI = percent_decode_path(RequestURI0),
+ {Path, _AfterPath} =
httpd_util:split_path(DocumentRoot ++ RequestURI),
Prefix ++ Path;
{Path, _AfterPath} ->
Path
- end,
- case uri_string:percent_decode(InitPath) of
- {error, _} -> InitPath;
- P -> P
end.
+percent_decode_path(InitPath) ->
+ case uri_string:percent_decode(InitPath) of
+ {error, _} ->
+ InitPath;
+ Path0 -> %% Protect against vulnerabilities
+ case uri_string:normalize(Path0) of
+ {error, _, _} ->
+ InitPath;
+ Path ->
+ Path
+ end
+ end.
%%
%% Configuration
%%
diff --git a/lib/inets/test/httpd_SUITE.erl b/lib/inets/test/httpd_SUITE.erl
index e4efeb1b81..027932caa2 100644
--- a/lib/inets/test/httpd_SUITE.erl
+++ b/lib/inets/test/httpd_SUITE.erl
@@ -155,6 +155,7 @@ http_head() ->
http_get() ->
[alias,
get,
+ bad_dot_paths,
%%actions, Add configuration so that this test mod_action
esi,
bad_hex,
@@ -473,6 +474,50 @@ get(Config) when is_list(Config) ->
{header, "Date"},
{header, "Server"},
{version, Version}]).
+
+bad_dot_paths() ->
+ [{doc, "Do not allow ..-paths to acesse files outside of doc root"}].
+bad_dot_paths(Config) when is_list(Config) ->
+ Version = proplists:get_value(http_version, Config),
+ Host = proplists:get_value(host, Config),
+ Type = proplists:get_value(type, Config),
+
+ BadDotPath0 = "/..%2f..%2f...%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2fetc/passwd ",
+ BadDotPath1 = "/..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2fetc/passwd ",
+ BadDotPath2 = "/%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2fetc/passwd ",
+
+ ok = httpd_test_lib:verify_request(proplists:get_value(type, Config), Host,
+ proplists:get_value(port, Config),
+ transport_opts(Type, Config),
+ proplists:get_value(node, Config),
+ http_request("GET " ++ BadDotPath0 , Version, Host),
+ [{statuscode, 404},
+ {header, "Content-Type", "text/html"},
+ {header, "Date"},
+ {header, "Server"},
+ {version, Version}]),
+
+ ok = httpd_test_lib:verify_request(proplists:get_value(type, Config), Host,
+ proplists:get_value(port, Config),
+ transport_opts(Type, Config),
+ proplists:get_value(node, Config),
+ http_request("GET " ++ BadDotPath1, Version, Host),
+ [{statuscode, 404},
+ {header, "Content-Type", "text/html"},
+ {header, "Date"},
+ {header, "Server"},
+ {version, Version}]),
+
+ ok = httpd_test_lib:verify_request(proplists:get_value(type, Config), Host,
+ proplists:get_value(port, Config),
+ transport_opts(Type, Config),
+ proplists:get_value(node, Config),
+ http_request("GET " ++ BadDotPath2, Version, Host),
+ [{statuscode, 404},
+ {header, "Content-Type", "text/html"},
+ {header, "Date"},
+ {header, "Server"},
+ {version, Version}]).
basic_auth_1_1(Config) when is_list(Config) ->
basic_auth([{http_version, "HTTP/1.1"} | Config]).