Critical security - validation of USM is invalid

Hello to all,

Disclaimer
This is my first post here, and I didn’t see a github to post the issue so I think this is the best place to post.

As a disclaimer I am totally new to SNMPv3 (just reading RFCs and such) and have read the source code, please take the following text with caution. That’s why I’m going to explain everything I understood so you can correct me if I’m talking nonsense.

Context
I think there is a major security leak in the library, because the code does not do any check on the configured USM on the manager side. Let me explain:

  1. The purpose of the USM is to provide: Integrity, authentication and confidentiality.

Each of these modules has a related mechanism (we’ll leave out integrity):

  • Auth: for authentication
  • Priv : for confidentiality

Finally, there are 3 linked security levels:

  • noAuthNoPriv: do not use it, no security provided
  • AuthNoPriv : Make sure the source of the PDU is the right one.
  • AuthPriv: Make sure the source of the PDU is correct and that no one can read the data.

In my testing, when the manager received a snmp TRAP the security level used is the one from the message. This means that as an attacker, I can send the (TRAP) noAuthNoPriv message even if the only USM configured is authPriv. This means that as an attacker, I can steal anyone’s identity if I have the engine ID + username. This means for me that there is no security at all.

Proof
I have run the code from the library SNMP++ 3.4.9 (receive_trap.cpp) without modification and with a USM :

→ noAuthNoPriv

snmptrap -v 3 -e 0x090807060504030201 -u MD5DES -l noAuthNoPriv localhost:10162 ‘’ 1.3.6.1.4.1.8072.2.3.0.1 1.3.6.1.4.1.8072.2.3.2.1 i 123456

The USM configured is :

usm->add_usm_user(“MD5DES”,
SNMP_AUTHPROTOCOL_HMACMD5, SNMP_PRIVPROTOCOL_DES,
“MD5DESUserAuthPassword”, “MD5DESUserPrivPassword”);

And has authPriv key.

Result

reason: -7
msg: SNMP++: Received SNMP Notification (trap or inform)
from: 127.0.0.1/60287
ID:  1.3.6.1.4.1.8072.2.3.0.1
Type:167
Oid: 1.3.6.1.4.1.8072.2.3.2.1
Val: 123456

Status → Accepted, with noAuthNoPriv

Digging the source code

The authentication is performed inside the class usm_v3.cpp

if (((securityLevel > SNMP_SECURITY_LEVEL_NOAUTH_NOPRIV) &&
(user->authProtocol == SNMP_AUTHPROTOCOL_NONE)) ||
((securityLevel == SNMP_SECURITY_LEVEL_AUTH_PRIV) &&
(user->privProtocol == SNMP_PRIVPROTOCOL_NONE))) {
inc_stats_unsupported_sec_levels();
debugprintf(0, “usmProcessMsg: unsupported Securitylevel”);
free_user(user);
return SNMPv3_USM_UNSUPPORTED_SECURITY_LEVEL;
}

Where the security level is extracted from the message !! Not from the USM (user var). This make totally sense with my tests, and as I understand totally defeat the purpose of authentication.

One easy fix (just a POC this is not a cleaned nor tested code :

switch (securityLevel) {
case SNMP_SECURITY_LEVEL_NOAUTH_NOPRIV:
if (user->privProtocol > SNMP_PRIVPROTOCOL_NONE) {
free_user(user);
inc_stats_unsupported_sec_levels();
return SNMPv3_USM_UNSUPPORTED_SECURITY_LEVEL;
}

      if (user->authProtocol > SNMP_AUTHPROTOCOL_NONE) {
          free_user(user);
          inc_stats_unsupported_sec_levels();
          return SNMPv3_USM_UNSUPPORTED_SECURITY_LEVEL;
      }
      break;

  case SNMP_SECURITY_LEVEL_AUTH_NOPRIV:
      if (user->privProtocol > SNMP_PRIVPROTOCOL_NONE) {
          free_user(user);
          inc_stats_unsupported_sec_levels();
          return SNMPv3_USM_UNSUPPORTED_SECURITY_LEVEL;
      }
      break;
  case SNMP_SECURITY_LEVEL_AUTH_PRIV:
      break;

  default:
      inc_stats_unsupported_sec_levels();
      free_user(user);
      return SNMPv3_USM_UNSUPPORTED_SECURITY_LEVEL;

}

Source

From the RFC 3414 - chapter 2.5.2

When the Message Processing (MP) Subsystem invokes the User-based
> Security module to verify proper security of an incoming message, it
must use the service provided for an incoming message. The abstract
service primitive is:

From the RFC 3414 - chapter 3.2

Processing step 6-7

  1. If the securityLevel specifies that the message is to be
    authenticated, then the message is authenticated according to the
    > user’s authentication protocol. To do so a call is made to the
    authentication module that implements the user’s authentication
    protocol according to the abstract service primitive:

    statusInformation =          -- success or failure
      authenticateIncomingMsg(
      IN   authKey               -- the user's localized authKey
      IN   authParameters        -- as received on the wire
      IN   wholeMsg              -- as received on the wire
      OUT  authenticatedWholeMsg -- checked for authentication
           )
    

RFC 3414 USM for SNMPv3 December 2002

   statusInformation
     indicates if authentication was successful or not.

   authKey
     the user's localized private authKey is the secret key that
     can be used by the authentication algorithm.

   wholeMsg
     the complete serialized message to be authenticated.

   authenticatedWholeMsg
     the same as the input given to the authenticateIncomingMsg
     service, but after authentication has been checked.

   If the authentication module returns failure, then the message
   cannot be trusted, so the usmStatsWrongDigests counter is
   incremented and an error indication (authenticationFailure)
   together with the OID and value of the incremented counter is
   returned to the calling module.

   If the authentication module returns success, then the message is
   authentic and can be trusted so processing continues.

Cisco snmpv3

Hi,
Most likely you mixed up authoritative engine (for traps it is the sender) and authentication (USM) and authorization (VACM).

Before I dig deeper into this, could you please verify your statement that it is not affected by those often seen misunderstandings?

Thank you in advance,
Frank

This is where I get stuck and maybe I’m misunderstanding, but since the USM is not validated by the receiver (manager). How did we verify that the sender of the TRAP is the correct one (not a hacker)? Here is the signature of the TRAP callback (from the example)

void callback(int reason, Snmp *snmp, Pdu &pdu, SnmpTarget &target, void *cd)

Do I just need to call a VACM / USM method on the target object or the Pdu? If not, how can I verify the authentication of the sender? As before the callback the identity is not verified.

EDIT : I see no field in the PDU or target on witch I can add some VACM call

For the anecdote, I’m working on a critical system, a fake TRAP can kill people (ok, I’m exaggerating a bit :wink: , but do very nasty things).

As I read the RFC, I think that the incoming message validation process must check the authentication, otherwise there is no authentication.

note: When I do the same check with net-snmp, (see the end of my post on stackoverflow: c++ - SNMP++ library ignore USM model and accept every input even without auth - Stack Overflow)

I get a different result, not the same as expected by the RFC, but a message with the authPriv configuration, is refused if the authentication is not correct (the encryption seem’s to be a little laxive but this is less critical)

VACM is part of AGENT++. It is not included in SNMP++. VACM is implemented on application level not by the protocol.

Thus, the security you are looking for, you need to implement on your own means or use AGENT++.

In addition, you need to rethink your architecture.
Think about trap directed polling!
If you need the trap receiver to be authoritative, then do not allow receiving traps at all and use INFORM requests instead.

I think you expect something that is intentionally not existing in the SNMP standard.

Please note:

  1. The trap-lister example in SNMP++ is an example and because SNMP++ is not implementing any Access Control Model (ACM) it does not check authorization. The USM API internally checks authentication and whether a PDU is authentic or not is retuned to the API caller. It might be that the example is not providing this information on the console. It is an example, who cares about that?
  2. Claiming hat there is a critical security issue in a library before having fully understood the RFC standards it is based on is a bit early :smirk:.
  3. It would be therefore great, if you could post here and on stackoverflow what your misunderstanding was so that others with the same issue can learn from it.

Thank you in advance,
Frank

Hi,

I think the the confusion is caused by the fact that the SNMPv3 RFC allows sending out a message on behalf of a USM user with a security level of noAuthNoPriv or authNoPriv, even if the user was configured to allow the security level authPriv.

When looking at Table 2 in the Cisco pdf you should note the different values in the column “RFC 3414-Compliant Error Indication”:

  • unsupportedSecurityLevel: This is returned if the securityLevel of the message is higher than the configured user supports.
  • AUTHORIZATION_ERROR: This is something that is not according to the RFC. It is some Cisco specific implementation that says “If a user is configured to support authentication, the authentication must be used. If not, the message will be dropped”.

Kind regards,
Jochen

Hi,

There are a lot of answers to provide, I will separate them into two different posts. The first on secondary points and background, the second on my understanding.

As I appreciate your time and value it. I will be thorough and link all the sources of information I use to understand this system. And I think I must not be alone in the SNMPv3 jungle.

Too fast to security breach claim from Point 2

  1. Claiming hat there is a critical security issue in a library before having fully understood the RFC standards it is based on is a bit early :smirk:.

I still doesn’t understand everything and think that’s the RFC is really misleading, and the documentation for this protocol is really hard-understanding. And I have implemented tons of protocols and medium security and network knowledge.

To be honest, I was thinking there is a major security breach in the library, and wanna to share as fast as possible if this is the case. But I take a lots of precautions in my first post about my current level on this this protocol.

The security model is at least confusing, but we will discuss this point later

Rethink my architecture from AGENTPP post

In addition, you need to rethink your architecture.
Think about trap directed polling!
If you need the trap receiver to be authoritative, then do not allow receiving traps at all and use INFORM requests instead.

I provide a framework, I didn’t control the architecture and I want to avoid my customer to think they are secure and they are not, I need also to explain them in our documentation this points, so I need to understand.

Document from specify the misleading points

Blockquote

  1. It would be therefore great, if you could post here and on stackoverflow what your misunderstanding was so that others with the same issue can learn from it.

Of course, that’s the idea of this post, I think there’s a great need for a detailed answer on this point.

1 Like

Sources
First at all I mainly use the RFC 3414 and hope this is the best one to begin with security model for SNMPv3.

From Jkatz answer

I think the the confusion is caused by the fact that the SNMPv3 RFC allows sending out a message on behalf of a USM user with a security level of noAuthNoPriv or authNoPriv , even if the user was configured to allow the security level authPriv .

  • unsupportedSecurityLevel: This is returned if the securityLevel of the message is higher than the configured user supports.

Where did you find this information? This is exactly the point where I am stuck. It makes no sense to me, and in the RFC I must misunderstand some words or something because I will explain the process of my understanding

1 : There is a authentication module

There is 3 modules, and one who is performing Data Origin Authentication.

From chapter 1.4 Module Organization

The security protocols defined in this memo are split in three
different modules and each has its specific responsibilities such
that together they realize the goals and security services described
above:

  • The authentication module MUST provide for:
    • Data Integrity,
    • Data Origin Authentication,
  • The timeliness module MUST provide for:
    • Protection against message delay or replay (to an extent greater
      than can occur through normal operation).
  • The privacy module MUST provide for
    • Protection against disclosure of the message payload.

Point 2: The authentication module description

The USM is responsible for the authentication with the given protocol (HMAC,…)

Section 6 describes the HMAC-MD5-96 authentication protocol which is the first authentication protocol that MUST be supported with the User-based Security Model. Section describes the HMAC-SHA-96 authentication protocol which is another authentication protocol that SHOULD be supported with the User-based Security Model. In the future additional or replacement authentication protocols may be defined as new needs arise.

The User-based Security Model prescribes that, if authentication is used, then the complete message is checked for integrity in the authentication module.

For a message to be authenticated, it needs to pass authentication
check by the authentication module and the timeliness check which is
a fixed part of this User-based Security model.

Point 3: The Privacy protocol

Still reference to USM for the cyphering

From RFC 3414 section-1.4.3

The User-based Security Model also prescribes that a message needs to
be authenticated if privacy is in use.

Point 4 : When a incoming message arrived the USM validate the security parameters
I understand the process of validating the incoming message is perform by the authentication module who is this RFC who is USM. Maybe there is here that’s I am false.

From RFC 3414 - section 3.2

  1. If the information about the user indicates that it does not
    support the securityLevel requested by the caller, then the
    usmStatsUnsupportedSecLevels counter is incremented and an error
    indication (unsupportedSecurityLevel) together with the OID and
    value of the incremented counter is returned to the calling
    module.

I hope you can correct me in some point (with source if possible). Thanks for your time

p.s. I am limited to 2 links per post… sorry for the inconvenience

From RFC 3414:

3.1. Generating an Outgoing SNMP Message

  1. If the securityLevel specifies that the message is to be protected
    from disclosure, but the user does not support both an
    authentication and a privacy protocol then the message cannot be
    sent. An error indication (unsupportedSecurityLevel) is returned
    to the calling module.

  2. If the securityLevel specifies that the message is to be
    authenticated, but the user does not support an authentication
    protocol, then the message cannot be sent. An error indication
    (unsupportedSecurityLevel) is returned to the calling module.


These are the checks done on the parameter securityLevel when sending out a message. These checks do not force a securityLevel of authPriv if the user user supports authentication and privacy. If it is allowed to send out a noAuthNoPriv for any configured user then we have to be able to receive a noAuthNoPriv message for every configured user.

So, the process of incoming message is explained by the process of outgoing message ? It’s a least confusing…Even if the text in incoming message treatement say explicitly :

  1. If the securityLevel specifies that the message is to be
    authenticated, then the message is authenticated according to the
    user’s authentication protocol. To do so a call is made to the
    authentication module that implements the user’s authentication
    protocol according to the abstract service primitive

For me your explanation work only if one of the following condition is true :

1. A trap is not a “management operation”

But I think the chapter 3.1 say it is :

This section describes the procedure followed by an SNMP engine
whenever it generates a message containing a management operation
(like a request, a response, a notification, or a report) on behalf
of a user, with a particular securityLevel.

2. The word support in incoming process chapter 3.1 part 5) is an interpretation and means equals or lower than and not specified

  1. If the information about the user indicates that it does not
    support the securityLevel requested by the caller, then the
    usmStatsUnsupportedSecLevels counter is incremented and an error
    indication (unsupportedSecurityLevel) together with the OID and
    value of the incremented counter is returned to the calling
    module.

Why not, I am not native English and I can misunderstand some subtlety. But generally I understand support by is configured to accept not accept if provided.

3. The point 6 of chapter 3.2 is linked to point 5

Maybe the security level of point 6 is the one extracted from the message and not the one from the USM configuration.

So :

With one condition apply ? Otherwise how do you link the fact the outgoing process override rules in incoming process

p.s. Thanks you for your time, maybe I hope this can be useful for other people too

USM - as its name suggests - is for authentication and not for authorisation. Thus, if a message is received, that does not contain authentication information, then the receiver will never be able to decide if it is authentic or not. But if the receiver can handle (i.e. support) noAuthNoPriv messages (which is most likely the case, because the receiver has not much to do about it), then that USM will accept it.

However, an Access Control Model which is responsible for authorisation might then decide to not accept any messages with noAuthNoPriv security level. The VACM can be configured like this.

You are still looking at the wrong SNMP architecture element for authorisation mechanisms. Authentication will not do authorisation.

Yes, as this case is easier to understand. If the RFC allows to send out a noAuthNoPriv message for a a USM user with privacy and authentication protocol, then it does not make any sense to drop such messages on the receiving side. The sender of the request would have to drop the received answer.

Yes!!! The user in the USM does not have a ‘securityLevel’. A user only has the information about the authentication and privacy protocol that can be used.

1 Like

Ok, thanks for your time, it’s clearer now. The fact that the security level is not applicable to the non-authoritative side should be written specifically (maybe I missed). Because that is the only thing that can explain the incoming process.

I’ve read tons of documents carefully and never read a single mention of this. There probably is, since you’ve done the work like for the SNMP++ agent, but it’s noisy.

One last request, I didn’t see any information stored in the PDU or anything else in the SNMP++ library, is this information accessible anywhere? I can modify the library to add VACM but the less I have to change, the easier the maintenance task will be.

In the callback function that is called by snmp++, you get the Pdu and the SnmpTarget.
You can get all the information you need from these objects:

 target.get_version();
 if (target.get_type() == SnmpTarget::type_ctarget) {
    CTarget *ctarget =  (CTarget*)⌖
    std::cout << "Read community: " << ctarget->get_readcommunity() << std::endl
              << "Write community: " << ctarget->get_writecommunity() << std::endl;
  } else if (target.get_type() == SnmpTarget::type_utarget) {
    UTarget *utarget =  (UTarget*)&target;
    std::cout << "Security name: " << utarget->get_security_name().get_printable() << std::endl
              << "Security model: " << utarget->get_security_model() << std::endl;
#ifdef _SNMPv3
    std::cout << "Security level: " << pdu.get_security_level() << std::endl;
#endif
  }

From the Snmp object you can get the v3MP object and then the USM object. From the USM you then can get information about the configured users.

Thanks you for you time. This is what I am looking for :wink:

Best regards