diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index 8f016aff7079..5305f7fc08f4 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -137,6 +137,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)