Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using SnmpDecodePacket for Encrypted/Authenticated Packets in GoSNMP #463

Open
matan-cylus opened this issue Feb 6, 2024 · 5 comments
Open

Comments

@matan-cylus
Copy link

Hello,

I'm currently exploring the gosnmp library for parsing SNMP packets, specifically versions 2 and 3. My use case involves the SnmpDecodePacket function, which is structured as follows:

func (x *GoSNMP) SnmpDecodePacket(resp []byte) (*SnmpPacket, error)

In the process of integrating this functionality, I encountered a problem regarding the parsing capabilities of SnmpDecodePacket, especially when it comes to handling encrypted or authenticated packets. It appears that the function struggles to effectively parse even the header.

I am using gosnmp version 1.25.0, where this issue does not seem to exist.

Could you please provide guidance or documentation on how to effectively use SnmpDecodePacket with these types of packets (or in general) in the latest version of gosnmp? Any examples or detailed explanations would be greatly appreciated :) .

Thank you for your time and assistance.

@TimRots
Copy link
Member

TimRots commented Feb 7, 2024

I am using gosnmp version 1.25.0, where this issue does not seem to exist.

1.25.0 is unsupported by now and differs a lot from the current code base.

Could you please provide guidance or documentation on how to effectively use SnmpDecodePacket with these types of packets (or in general) in the latest version of gosnmp? Any examples or detailed explanations would be greatly appreciated :) .

From existing tests I whipped up this script to decode a v2 packet:

package main

import (
	"log"

	g "github.com/gosnmp/gosnmp"
)

/*
responseBytes corresponds to the response section of this snmpget

Simple Network Management Protocol
  version: v2c (1)
  community: public
  data: get-response (2)
    get-response
      request-id: 1066889284
      error-status: noError (0)
      error-index: 0
      variable-bindings: 8 items
        1.3.6.1.2.1.1.7.0: 104
        1.3.6.1.2.1.2.2.1.10.1: 271070065
        1.3.6.1.2.1.2.2.1.5.1: 100000000
        1.3.6.1.2.1.1.4.0: 41646d696e6973747261746f72
        1.3.6.1.2.1.43.5.1.1.15.1: Value (Null)
        1.3.6.1.2.1.4.21.1.1.127.0.0.1: 127.0.0.1 (127.0.0.1)
        1.3.6.1.4.1.23.2.5.1.1.1.4.2: 00159937762b
        1.3.6.1.2.1.1.3.0: 318870100
*/

var responseBytes = []byte{
	0x30, 0x81, 0xc2, 0x02, 0x01, 0x01, 0x04, 0x06, 0x70, 0x75, 0x62, 0x6c,
	0x69, 0x63, 0xa2, 0x81, 0xb4, 0x02, 0x04, 0x3f, 0x97, 0x70, 0x44, 0x02,
	0x01, 0x00, 0x02, 0x01, 0x00, 0x30, 0x81, 0xa5, 0x30, 0x0d, 0x06, 0x08,
	0x2b, 0x06, 0x01, 0x02, 0x01, 0x01, 0x07, 0x00, 0x02, 0x01, 0x68, 0x30,
	0x12, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x02, 0x01, 0x02, 0x02, 0x01, 0x0a,
	0x01, 0x41, 0x04, 0x10, 0x28, 0x33, 0x71, 0x30, 0x12, 0x06, 0x0a, 0x2b,
	0x06, 0x01, 0x02, 0x01, 0x02, 0x02, 0x01, 0x05, 0x01, 0x42, 0x04, 0x05,
	0xf5, 0xe1, 0x00, 0x30, 0x19, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x02, 0x01,
	0x01, 0x04, 0x00, 0x04, 0x0d, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x69, 0x73,
	0x74, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x30, 0x0f, 0x06, 0x0b, 0x2b, 0x06,
	0x01, 0x02, 0x01, 0x2b, 0x05, 0x01, 0x01, 0x0f, 0x01, 0x05, 0x00, 0x30,
	0x15, 0x06, 0x0d, 0x2b, 0x06, 0x01, 0x02, 0x01, 0x04, 0x15, 0x01, 0x01,
	0x7f, 0x00, 0x00, 0x01, 0x40, 0x04, 0x7f, 0x00, 0x00, 0x01, 0x30, 0x17,
	0x06, 0x0d, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x17, 0x02, 0x05, 0x01, 0x01,
	0x01, 0x04, 0x02, 0x04, 0x06, 0x00, 0x15, 0x99, 0x37, 0x76, 0x2b, 0x30,
	0x10, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x02, 0x01, 0x01, 0x03, 0x00, 0x43,
	0x04, 0x13, 0x01, 0x92, 0x54,
}

func main() {
	gosnmp := &g.GoSNMP{}
	result, err := gosnmp.SnmpDecodePacket(responseBytes)
	if err != nil {
		log.Fatal(err)
	}
	log.Print(result)
}

which works as expected

&{2c NoAuthNoPriv SnmpV3SecurityModel(0) <nil>   public GetResponse 0 1066889284 0 NoError 0 0 0 [{104 .1.3.6.1.2.1.1.7.0 Integer} {271070065 .1.3.6.1.2.1.2.2.1.10.1 Counter32} {100000000 .1.3.6.1.2.1.2.2.1.5.1 Gauge32} {[65 100 109 105 110 105 115 116 114 97 116 111 114] .1.3.6.1.2.1.1.4.0 OctetString} {<nil> .1.3.6.1.2.1.43.5.1.1.15.1 Null} {127.0.0.1 .1.3.6.1.2.1.4.21.1.1.127.0.0.1 IPAddress} {[0 21 153 55 118 43] .1.3.6.1.4.1.23.2.5.1.1.1.4.2 OctetString} {318870100 .1.3.6.1.2.1.1.3.0 TimeTicks}] {<nil>} {[] false   0 0 0}}

I guess results with v3 packets could differ for how gosnmp will be initialized. Could you share back some example based on above? I'll try to help out

@matan-cylus
Copy link
Author

matan-cylus commented Feb 8, 2024

Thank you for the quick response :)
Im using gosnmp for DPI. meaning when i use the SnmpDecodePacket function to parse the following packets i don't know the expected authentication parameters. can you give me advice on how should i use the function if the cases i show here?

im trying to decode this packet, for example:

"3077020103300f02030489b302020800040101020103042f302d040c800000f805008063d2fe3a000201030203305cdf040561646d696e040c7f663f86fdd8adb6c1bedb7604003030040c800000f805008063d2fe3a000400a21e02030489b30201000201003011300f060a2b060102010202010801020101"

(this is the hex value of the snmp layer)

and this is the error i recieve

error parsing SNMPv3 User Security Model: authentication parameters are not configured to parse incoming authenticated message

same will happen with this packet

"308190020103300f02030091c90202080004010302010304383036040c000039e7000000a1c0a81da0020202f0020302fde8040561646d696e040c11ea7ebd94bf2e64e3500b5b04085611c81435399c400440f0e8472aa3180408ef813db8cf114188ccb806d3e670cdd98e926dff21e98d6ba056223ab768cb402665706ee183e028dd2710452c1530d7553c202fa14950cd"

the common thing i see between them is that the encrypted/authenticated flags are on.

the packets in pcap files:
pcaps.zip

@TimRots
Copy link
Member

TimRots commented Feb 8, 2024

Thanks for sharing those pcaps, that accelerated some tests today.

I feel that the unmarshal method in v3_usm.go is a bit on the strict side. With minimal logic changes and some furniture moving one would be able to dump packet contents for v3:

diff --git a/v3_usm.go b/v3_usm.go
index 9c6574a..cbcbdc2 100644
--- a/v3_usm.go
+++ b/v3_usm.go
@@ -1042,10 +1042,10 @@ func (sp *UsmSecurityParameters) unmarshal(flags SnmpV3MsgFlags, packet []byte,
        if flags&AuthNoPriv > 0 {
                // In case if the authentication protocol is not configured or set to NoAuth, then the packet cannot
                // be processed further
-               if sp.AuthenticationProtocol <= NoAuth {
-                       return 0, errors.New("error parsing SNMPv3 User Security Model: authentication parameters are not configured to parse incoming authenticated message")
+               if sp.AuthenticationProtocol >= NoAuth {
+                       copy(packet[cursor+2:cursor+len(macVarbinds[sp.AuthenticationProtocol])], macVarbinds[sp.AuthenticationProtocol][2:])
                }
-               copy(packet[cursor+2:cursor+len(macVarbinds[sp.AuthenticationProtocol])], macVarbinds[sp.AuthenticationProtocol][2:])
+               //  return 0, errors.New("error parsing SNMPv3 User Security Model: authentication parameters are not configured to parse incoming authenticated message")
        }
        cursor += count

@@ -1057,11 +1057,11 @@ func (sp *UsmSecurityParameters) unmarshal(flags SnmpV3MsgFlags, packet []byte,
        if msgPrivacyParameters, ok := rawMsgPrivacyParameters.(string); ok {
                sp.PrivacyParameters = []byte(msgPrivacyParameters)
                sp.Logger.Printf("Parsed privacyParameters %s", msgPrivacyParameters)
-               if flags&AuthPriv >= AuthPriv {
-                       if sp.PrivacyProtocol <= NoPriv {
-                               return 0, errors.New("error parsing SNMPv3 User Security Model: privacy parameters are not configured to parse incoming encrypted message")
-                       }
-               }
+               //      if flags&AuthPriv >= AuthPriv {
+               //              if sp.PrivacyProtocol <= NoPriv {
+               //                      return 0, errors.New("error parsing SNMPv3 User Security Model: privacy parameters are not configured to parse incoming encrypted message")
+               //              }
+               //      }
        }
% go run main.go
(*gosnmp.SnmpPacket)(0xc0000e6000)({
 Version: (gosnmp.SnmpVersion) 3,
 MsgFlags: (gosnmp.SnmpV3MsgFlags) AuthNoPriv,
 SecurityModel: (gosnmp.SnmpV3SecurityModel) UserSecurityModel,
 SecurityParameters: (*gosnmp.UsmSecurityParameters)(0xc0000a85b0)({
  mu: (sync.Mutex) {
   state: (int32) 0,
   sema: (uint32) 0
  },
  localAESSalt: (uint64) 0,
  localDESSalt: (uint32) 0,
  AuthoritativeEngineID: (string) (len=12) "\x80\x00\x00\xf8\x05\x00\x80c\xd2\xfe:\x00",
  AuthoritativeEngineBoots: (uint32) 3,
  AuthoritativeEngineTime: (uint32) 3169503,
  UserName: (string) (len=5) "admin",
  AuthenticationParameters: (string) (len=12) "\x7ff?\x86\xfdح\xb6\xc1\xbe\xdbv",
  PrivacyParameters: ([]uint8) {
  },
  AuthenticationProtocol: (gosnmp.SnmpV3AuthProtocol) SnmpV3AuthProtocol(0),
  PrivacyProtocol: (gosnmp.SnmpV3PrivProtocol) SnmpV3PrivProtocol(0),
  AuthenticationPassphrase: (string) "",
  PrivacyPassphrase: (string) "",
  SecretKey: ([]uint8) <nil>,
  PrivacyKey: ([]uint8) <nil>,
  Logger: (gosnmp.Logger) {
   logger: (gosnmp.LoggerInterface) <nil>
  }
 }),
 ContextEngineID: (string) (len=12) "\x80\x00\x00\xf8\x05\x00\x80c\xd2\xfe:\x00",
 ContextName: (string) "",
 Community: (string) "",
 PDUType: (gosnmp.PDUType) GetResponse,
 MsgID: (uint32) 297395,
 RequestID: (uint32) 297395,
 MsgMaxSize: (uint32) 2048,
 Error: (gosnmp.SNMPError) NoError,
 ErrorIndex: (uint8) 0,
 NonRepeaters: (uint8) 0,
 MaxRepetitions: (uint32) 0,
 Variables: ([]gosnmp.SnmpPDU) (len=1 cap=5) {
  (gosnmp.SnmpPDU) {
   Value: (int) 1,
   Name: (string) (len=22) ".1.3.6.1.2.1.2.2.1.8.1",
   Type: (gosnmp.Asn1BER) Integer
  }
 },
 Logger: (gosnmp.Logger) {
  logger: (gosnmp.LoggerInterface) <nil>
 },
 SnmpTrap: (gosnmp.SnmpTrap) {
  Variables: ([]gosnmp.SnmpPDU) <nil>,
  IsInform: (bool) false,
  Enterprise: (string) "",
  AgentAddress: (string) "",
  GenericTrap: (int) 0,
  SpecificTrap: (int) 0,
  Timestamp: (uint) 0
 }
})

With the other pcap I run into new issues in v3.go where we error out on

2024/02/08 16:43:03 error parsing SNMPV3 contextEngineID: unknown field type: f0

Long story short. I think gosnmp should be able to do what you need for your use-case. However to achieve that we would need to increase test coverage by quite a bit for all the breakage that will pop up during those renovations, so that we don't sacrifice reliability of the gosnmp library.

@matan-cylus
Copy link
Author

Do you expect this fix to be implemented in the next released version?

@TimRots
Copy link
Member

TimRots commented Mar 2, 2024

Do you expect this fix to be implemented in the next released version?

Sorry I don't think that will be the case.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants