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:
- 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
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.