From b740acac8701faa054e90e4c47b10283d4b587ae Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sun, 10 Sep 2023 06:13:20 +0930 Subject: [PATCH] packetbeat/protos/http: don't panic when host is empty (#36518) (#36526) Previously, extractHostHeader would panic if the host part of header was empty. Avoid this by using standard library functions to do splits and clean up IPv6 addresses. Add tests to confirm old behaviour and test to cover panic case. (cherry picked from commit 3d0cdb04db2c439e4e834b55253eb9dadd32f78e) Co-authored-by: Dan Kortschak <90160302+efd6@users.noreply.github.com> --- CHANGELOG.next.asciidoc | 1 + packetbeat/protos/http/http.go | 28 ++++++++++++++++++--------- packetbeat/protos/http/http_test.go | 30 +++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index 2d2942419aa1..dace202a2388 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -134,6 +134,7 @@ https://github.com/elastic/beats/compare/v8.8.1\...main[Check the HEAD diff] *Packetbeat* +- Fix panic in HTTP protocol parsing when host header has empty host part. {issue}36497[36497] {issue}36518[36518] *Winlogbeat* diff --git a/packetbeat/protos/http/http.go b/packetbeat/protos/http/http.go index 6c291e8e83a4..c6347c2b257b 100644 --- a/packetbeat/protos/http/http.go +++ b/packetbeat/protos/http/http.go @@ -20,6 +20,7 @@ package http import ( "bytes" "encoding/base64" + "errors" "fmt" "net" "net/url" @@ -735,20 +736,29 @@ func parseCookieValue(raw string) string { } func extractHostHeader(header string) (host string, port int) { - if len(header) == 0 || net.ParseIP(header) != nil { + if header == "" || net.ParseIP(header) != nil { return header, port } - // Split :port trailer - if pos := strings.LastIndexByte(header, ':'); pos != -1 { - if num, err := strconv.Atoi(header[pos+1:]); err == nil && num > 0 && num < 65536 { - header, port = header[:pos], num + host, ps, err := net.SplitHostPort(header) + if err != nil { + var addrError *net.AddrError + if errors.As(err, &addrError) && addrError.Err == "missing port in address" { + return trimSquareBracket(header), port } } - // Remove square bracket boxing of IPv6 address. - if last := len(header) - 1; header[0] == '[' && header[last] == ']' && net.ParseIP(header[1:last]) != nil { - header = header[1:last] + pi, err := strconv.ParseInt(ps, 10, 16) + if err != nil || pi == 0 { + return header, port + } + return trimSquareBracket(host), int(pi) +} + +func trimSquareBracket(s string) string { + s, ok := strings.CutPrefix(s, "[") + if !ok { + return s } - return header, port + return strings.TrimSuffix(s, "]") } func (http *httpPlugin) hideHeaders(m *message) { diff --git a/packetbeat/protos/http/http_test.go b/packetbeat/protos/http/http_test.go index 73b549894e6d..c8c7a9c73448 100644 --- a/packetbeat/protos/http/http_test.go +++ b/packetbeat/protos/http/http_test.go @@ -1901,6 +1901,36 @@ func TestHttpParser_Extension(t *testing.T) { } } +func TestExtractHostHeader(t *testing.T) { + tests := []struct { + header string + wantHost string + wantPort int + }{ + {header: "", wantHost: "", wantPort: 0}, + {header: "localhost:0", wantHost: "localhost:0", wantPort: 0}, + {header: "127.0.0.1:0", wantHost: "127.0.0.1:0", wantPort: 0}, + {header: "[::]:0", wantHost: "[::]:0", wantPort: 0}, + {header: "localhost", wantHost: "localhost", wantPort: 0}, + {header: "localhost:9001", wantHost: "localhost", wantPort: 9001}, + {header: "localhost:9000000", wantHost: "localhost:9000000", wantPort: 0}, + {header: "127.0.0.1:9001", wantHost: "127.0.0.1", wantPort: 9001}, + {header: "127.0.0.1", wantHost: "127.0.0.1", wantPort: 0}, + {header: "[::]", wantHost: "::", wantPort: 0}, + {header: ":0", wantHost: ":0", wantPort: 0}, + {header: ":9001", wantHost: "", wantPort: 9001}, + } + for _, test := range tests { + host, port := extractHostHeader(test.header) + if host != test.wantHost { + t.Errorf("unexpected host for %q: got:%q want:%q", test.header, host, test.wantHost) + } + if port != test.wantPort { + t.Errorf("unexpected port for %q: got:%d want:%d", test.header, port, test.wantPort) + } + } +} + func benchmarkHTTPMessage(b *testing.B, data []byte) { http := httpModForTests(nil) parser := newParser(&http.parserConfig)