From 5409d330020b19ab909838e734e29ab71c36a14f Mon Sep 17 00:00:00 2001 From: Matus Fabian Date: Tue, 28 May 2024 13:39:13 +0200 Subject: http_static: sanitize path before file read Romove dot segments from requested target path before start reading file in file handler to prevent path traversal. Type: fix Change-Id: I3bdd3e9d7fffd33c9c8c608169c1dc73423b7078 Signed-off-by: Matus Fabian --- extras/hs-test/http_test.go | 57 ++++++++++++++++++++++++--------------------- extras/hs-test/utils.go | 16 +++++++++++++ 2 files changed, 47 insertions(+), 26 deletions(-) (limited to 'extras/hs-test') diff --git a/extras/hs-test/http_test.go b/extras/hs-test/http_test.go index 94cb0cad064..ba0fdb31a1a 100644 --- a/extras/hs-test/http_test.go +++ b/extras/hs-test/http_test.go @@ -16,10 +16,12 @@ func init() { registerNoTopoTests(NginxHttp3Test, NginxAsServerTest, NginxPerfCpsTest, NginxPerfRpsTest, NginxPerfWrkTest, HeaderServerTest, HttpStaticMovedTest, HttpStaticNotFoundTest, HttpCliMethodNotAllowedTest, - HttpCliBadRequestTest) + HttpCliBadRequestTest, HttpStaticPathTraversalTest) registerNoTopoSoloTests(HttpStaticPromTest) } +const wwwRootPath = "/tmp/www_root" + func HttpTpsTest(s *NsSuite) { iface := s.getInterfaceByName(clientInterface) client_ip := iface.ip4AddressString() @@ -108,21 +110,31 @@ func HttpStaticPromTest(s *NoTopoSuite) { s.assertNil(err) } +func HttpStaticPathTraversalTest(s *NoTopoSuite) { + vpp := s.getContainerByName("vpp").vppInstance + vpp.container.exec("mkdir -p " + wwwRootPath) + vpp.container.exec("mkdir -p " + "/tmp/secret_folder") + vpp.container.createFile("/tmp/secret_folder/secret_file.txt", "secret") + serverAddress := s.getInterfaceByName(tapInterfaceName).peer.ip4AddressString() + s.log(vpp.vppctl("http static server www-root " + wwwRootPath + " uri tcp://" + serverAddress + "/80 debug")) + + client := newHttpClient() + req, err := http.NewRequest("GET", "http://"+serverAddress+":80/../secret_folder/secret_file.txt", nil) + s.assertNil(err, fmt.Sprint(err)) + resp, err := client.Do(req) + s.assertNil(err, fmt.Sprint(err)) + defer resp.Body.Close() + s.assertEqual(404, resp.StatusCode) +} + func HttpStaticMovedTest(s *NoTopoSuite) { vpp := s.getContainerByName("vpp").vppInstance - vpp.container.exec("mkdir -p /tmp/tmp.aaa") - vpp.container.createFile("/tmp/tmp.aaa/index.html", "

Hello

") + vpp.container.exec("mkdir -p " + wwwRootPath + "/tmp.aaa") + vpp.container.createFile(wwwRootPath+"/tmp.aaa/index.html", "

Hello

") serverAddress := s.getInterfaceByName(tapInterfaceName).peer.ip4AddressString() - s.log(vpp.vppctl("http static server www-root /tmp uri tcp://" + serverAddress + "/80 debug")) - - transport := http.DefaultTransport - transport.(*http.Transport).Proxy = nil - client := &http.Client{ - CheckRedirect: func(req *http.Request, via []*http.Request) error { - return http.ErrUseLastResponse - }, - Transport: transport, - } + s.log(vpp.vppctl("http static server www-root " + wwwRootPath + " uri tcp://" + serverAddress + "/80 debug")) + + client := newHttpClient() req, err := http.NewRequest("GET", "http://"+serverAddress+":80/tmp.aaa", nil) s.assertNil(err, fmt.Sprint(err)) resp, err := client.Do(req) @@ -134,12 +146,11 @@ func HttpStaticMovedTest(s *NoTopoSuite) { func HttpStaticNotFoundTest(s *NoTopoSuite) { vpp := s.getContainerByName("vpp").vppInstance + vpp.container.exec("mkdir -p " + wwwRootPath) serverAddress := s.getInterfaceByName(tapInterfaceName).peer.ip4AddressString() - s.log(vpp.vppctl("http static server www-root /tmp uri tcp://" + serverAddress + "/80 debug")) + s.log(vpp.vppctl("http static server www-root " + wwwRootPath + " uri tcp://" + serverAddress + "/80 debug")) - transport := http.DefaultTransport - transport.(*http.Transport).Proxy = nil - client := &http.Client{Transport: transport} + client := newHttpClient() req, err := http.NewRequest("GET", "http://"+serverAddress+":80/notfound.html", nil) s.assertNil(err, fmt.Sprint(err)) resp, err := client.Do(req) @@ -153,9 +164,7 @@ func HttpCliMethodNotAllowedTest(s *NoTopoSuite) { serverAddress := s.getInterfaceByName(tapInterfaceName).peer.ip4AddressString() vpp.vppctl("http cli server") - transport := http.DefaultTransport - transport.(*http.Transport).Proxy = nil - client := &http.Client{Transport: transport} + client := newHttpClient() req, err := http.NewRequest("POST", "http://"+serverAddress+":80/test", nil) s.assertNil(err, fmt.Sprint(err)) resp, err := client.Do(req) @@ -171,9 +180,7 @@ func HttpCliBadRequestTest(s *NoTopoSuite) { serverAddress := s.getInterfaceByName(tapInterfaceName).peer.ip4AddressString() vpp.vppctl("http cli server") - transport := http.DefaultTransport - transport.(*http.Transport).Proxy = nil - client := &http.Client{Transport: transport} + client := newHttpClient() req, err := http.NewRequest("GET", "http://"+serverAddress+":80", nil) s.assertNil(err, fmt.Sprint(err)) resp, err := client.Do(req) @@ -187,9 +194,7 @@ func HeaderServerTest(s *NoTopoSuite) { serverAddress := s.getInterfaceByName(tapInterfaceName).peer.ip4AddressString() vpp.vppctl("http cli server") - transport := http.DefaultTransport - transport.(*http.Transport).Proxy = nil - client := &http.Client{Transport: transport} + client := newHttpClient() req, err := http.NewRequest("GET", "http://"+serverAddress+":80/show/version", nil) s.assertNil(err, fmt.Sprint(err)) resp, err := client.Do(req) diff --git a/extras/hs-test/utils.go b/extras/hs-test/utils.go index 304dd4c241b..d250dc64519 100644 --- a/extras/hs-test/utils.go +++ b/extras/hs-test/utils.go @@ -3,8 +3,10 @@ package main import ( "fmt" "io" + "net/http" "os" "strings" + "time" ) const networkTopologyDir string = "topo-network/" @@ -78,3 +80,17 @@ func (s *Stanza) saveToFile(fileName string) error { _, err = io.Copy(fo, strings.NewReader(s.content)) return err } + +// newHttpClient creates [http.Client] with disabled proxy and redirects, it also sets timeout to 30seconds. +func newHttpClient() *http.Client { + transport := http.DefaultTransport + transport.(*http.Transport).Proxy = nil + transport.(*http.Transport).DisableKeepAlives = true + client := &http.Client{ + Transport: transport, + Timeout: time.Second * 30, + CheckRedirect: func(req *http.Request, via []*http.Request) error { + return http.ErrUseLastResponse + }} + return client +} -- cgit 1.2.3-korg