From e813c0e19e4e4780347524c0fb359c5f445664db Mon Sep 17 00:00:00 2001 From: Mattermost Build Date: Fri, 30 Jan 2026 11:53:31 +0200 Subject: [PATCH] MM-67268: Fix SSRF bypass via IPv4-mapped IPv6 literals (#35097) (#35130) Automatic Merge --- server/public/shared/httpservice/client.go | 5 +++++ server/public/shared/httpservice/client_test.go | 11 +++++++++++ 2 files changed, 16 insertions(+) diff --git a/server/public/shared/httpservice/client.go b/server/public/shared/httpservice/client.go index 39b7ef3d16..d9cde2c61c 100644 --- a/server/public/shared/httpservice/client.go +++ b/server/public/shared/httpservice/client.go @@ -27,6 +27,11 @@ var reservedIPRanges []*net.IPNet // IsReservedIP checks whether the target IP belongs to reserved IP address ranges to avoid SSRF attacks to the internal // network of the Mattermost server func IsReservedIP(ip net.IP) bool { + // Canonicalize IPv4-mapped IPv6 addresses (e.g., ::ffff:127.0.0.1) to their + // native IPv4 form so that IPv4 CIDR ranges match correctly. + if ip4 := ip.To4(); ip4 != nil { + ip = ip4 + } for _, ipRange := range reservedIPRanges { if ipRange.Contains(ip) { return true diff --git a/server/public/shared/httpservice/client_test.go b/server/public/shared/httpservice/client_test.go index 709fc5f5e7..324a70d2d2 100644 --- a/server/public/shared/httpservice/client_test.go +++ b/server/public/shared/httpservice/client_test.go @@ -206,6 +206,17 @@ func TestIsReservedIP(t *testing.T) { {"127.120.6.3", net.IPv4(127, 120, 6, 3), true}, {"8.8.8.8", net.IPv4(8, 8, 8, 8), false}, {"9.9.9.9", net.IPv4(9, 9, 9, 8), false}, + // IPv4-mapped IPv6 addresses should be detected as reserved + {"::ffff:127.0.0.1", net.ParseIP("::ffff:127.0.0.1"), true}, + {"::ffff:192.168.1.1", net.ParseIP("::ffff:192.168.1.1"), true}, + {"::ffff:10.0.0.1", net.ParseIP("::ffff:10.0.0.1"), true}, + {"::ffff:169.254.169.254", net.ParseIP("::ffff:169.254.169.254"), true}, + {"::ffff:8.8.8.8", net.ParseIP("::ffff:8.8.8.8"), false}, + // Pure IPv6 reserved addresses + {"::1", net.ParseIP("::1"), true}, + {"fe80::1", net.ParseIP("fe80::1"), true}, + // Public IPv6 + {"2607:f8b0:4004:800::200e", net.ParseIP("2607:f8b0:4004:800::200e"), false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) {