📄 spp_ssh.c
字号:
_dpd.streamAPI->stop_inspection( packetp->stream_session_ptr, packetp, SSN_DIR_BOTH, -1, 0 ); } } } } else { /* * Have seen a server response, so * this appears to be a valid exchange. * Reset suspicious byte count to zero. */ sessp->num_client_bytes = 0; } } else { /* Have already examined more than the limit * of encrypted packets. Both the Gobbles and * the CRC32 attacks occur during authentication * and therefore cannot be used late in an * encrypted session. For performance purposes, * stop examining this session. */ _dpd.streamAPI->stop_inspection( packetp->stream_session_ptr, packetp, SSN_DIR_BOTH, -1, 0 ); } } PREPROC_PROFILE_END(sshPerfStats);}/* Retrieves the SSH data block registered with the stream * session associated w/ the current packet. If none exists, * allocates it and registers it with the stream API. * * PARAMETERS: * * packetp: Pointer to the packet from which/in which to * retrieve/store the SSH data block. * * RETURNS: Pointer to an SSH data block, upon success. * NULL, upon failure. */SSHData* GetSSHData( SFSnortPacket* packetp ){ SSHData* datap = NULL; /* Sanity check(s) */ if (( !packetp ) || ( !packetp->stream_session_ptr )) { return NULL; } /* Attempt to get a previously allocated SSH block. If none exists, * allocate and register one with the stream layer. */ datap = _dpd.streamAPI->get_application_data( packetp->stream_session_ptr, PP_SSH ); if ( !datap ) { datap = malloc( sizeof( SSHData )); if ( !datap ) return NULL; /* Initialize to known state. */ memset( datap, 0, sizeof( SSHData )); /*Register the new SSH data block in the stream session. */ _dpd.streamAPI->set_application_data( packetp->stream_session_ptr, PP_SSH, datap, FreeSSHData ); } return datap;}/* Registered as a callback with our SSH data blocks when * they are added to the underlying stream session. Called * by the stream preprocessor when a session is about to be * destroyed. * * PARAMETERS: * * idatap: Pointer to the moribund data. * * RETURNS: Nothing. */static voidFreeSSHData( void* idatap ){ if ( idatap ) { free( idatap ); }}/* Validates given port as an SSH server port. * * PARAMETERS: * * port: Port to validate. * * RETURNS: SSH_TRUE, if the port is indeed an SSH server port. * SSH_FALSE, otherwise. */static INLINE intCheckSSHPort( u_int16_t port ){ if ( ssh_config.ports[ PORT_INDEX(port) ] & CONV_PORT( port ) ) { return SSH_TRUE; } return SSH_FALSE;}/* Checks if the string 'str' is 'max' bytes long or longer. * Returns 0 if 'str' is less than or equal to 'max' bytes; * returns 1 otherwise.*/static INLINE int SSHCheckStrlen(char *str, int max) { while(*(str++) && max--) ; if(max > 0) return 0; /* str size is <= max bytes */ return 1;}/* Attempts to process current packet as a protocol version exchange * packet. This function will be called if either the client or server * protocol version message (or both) has not been sent. * * PARAMETERS: * * sessionp: Pointer to SSH data for packet's session. * packetp: Pointer to the packet to inspect. * direction: Which direction the packet is going. * known_port: A pre-configured or default server port is involved. * * RETURNS: SSH_SUCCESS, if successfully processed a proto exch msg * SSH_FAILURE, otherwise. */static intProcessSSHProtocolVersionExchange( SSHData* sessionp, SFSnortPacket* packetp, u_int8_t direction, u_int8_t known_port ){ char* version_stringp = (char*) packetp->payload; u_int8_t version; /* Get the version. */ if ( packetp->payload_size >= 6 && !strncasecmp( version_stringp, "SSH-1.", 6)) { if (( packetp->payload_size > 7 ) && ( version_stringp[6] == '9') && (version_stringp[7] == '9')) { /* SSH 1.99 which is the same as SSH2.0 */ version = SSH_VERSION_2; } else { version = SSH_VERSION_1; } /* CAN-2002-0159 */ /* Verify the version string is not greater than * SSH_MAX_PROTOVERS_STRING. * We've already verified the first 6 bytes, so we'll start * check from &version_string[6] */ if( (ssh_config.EnabledAlerts & SSH_ALERT_SECURECRT ) && /* First make sure the payload itself is sufficiently large */ (packetp->payload_size > SSH_MAX_PROTOVERS_STRING) && /* CheckStrlen will check if the version string up to * SSH_MAX_PROTOVERS_STRING+1 since there's no reason to * continue checking after that point*/ (SSHCheckStrlen(&version_stringp[6], SSH_MAX_PROTOVERS_STRING-6))) { ALERT(SSH_EVENT_SECURECRT, SSH_EVENT_SECURECRT_STR); } } else if ( packetp->payload_size >= 6 && !strncasecmp( version_stringp, "SSH-2.", 6)) { version = SSH_VERSION_2; } else { /* Not SSH on SSH port, CISCO vulnerability */ if ((direction == SSH_DIR_FROM_CLIENT) && ( known_port != 0 ) && ( ssh_config.EnabledAlerts & SSH_ALERT_PROTOMISMATCH )) { ALERT(SSH_EVENT_PROTOMISMATCH, SSH_EVENT_PROTOMISMATCH_STR); } return SSH_FAILURE; } /* Saw a valid protocol exchange message. Mark the session * according to the direction. */ switch( direction ) { case SSH_DIR_FROM_SERVER: sessionp->state_flags |= SSH_FLG_SERV_IDSTRING_SEEN; break; case SSH_DIR_FROM_CLIENT: sessionp->state_flags |= SSH_FLG_CLIENT_IDSTRING_SEEN; break; } sessionp->version = version; return SSH_SUCCESS; }/* Called to process SSH1 key exchange or SSH2 key exchange init * messages. On failure, inspection will be continued, but the packet * will be alerted on, and ignored. * * PARAMETERS: * * sessionp: Pointer to SSH data for packet's session. * packetp: Pointer to the packet to inspect. * direction: Which direction the packet is going. * * RETURN: SSH_SUCCESS, if a valid key exchange message is processed * SSH_FAILURE, otherwise. */static int ProcessSSHKeyInitExchange( SSHData* sessionp, SFSnortPacket* packetp, u_int8_t direction ){ SSH2Packet* ssh2packetp = NULL; if ( sessionp->version == SSH_VERSION_1 ) { u_int32_t length; u_int8_t padding_length; u_int8_t message_type; /* * Validate packet payload. * First 4 bytes should have the SSH packet length, * minus any padding. */ if ( packetp->payload_size < 4 ) { if(ssh_config.EnabledAlerts & SSH_ALERT_PAYSIZE) { ALERT(SSH_EVENT_PAYLOAD_SIZE, SSH_PAYLOAD_SIZE_STR); } return SSH_FAILURE; } /* * SSH1 key exchange is very simple and * consists of only two messages, a server * key and a client key message.` */ length = ntohl( *((u_int32_t*) packetp->payload) ); /* Packet payload should be larger than length, due to padding. */ if ( packetp->payload_size < length ) { if(ssh_config.EnabledAlerts & SSH_ALERT_PAYSIZE) { ALERT(SSH_EVENT_PAYLOAD_SIZE, SSH_PAYLOAD_SIZE_STR); } return SSH_FAILURE; } padding_length = (u_int8_t)(8 - (length % 8)); /* * With the padding calculated, verify payload is sufficiently large * to include the message type. */ if ( packetp->payload_size < padding_length + 4 + 1) { if(ssh_config.EnabledAlerts & SSH_ALERT_PAYSIZE) { ALERT(SSH_EVENT_PAYLOAD_SIZE, SSH_PAYLOAD_SIZE_STR); } return SSH_FAILURE; } message_type = *( (u_int8_t*) (packetp->payload + padding_length + 4)); switch( message_type ) { case SSH_MSG_V1_SMSG_PUBLIC_KEY: if ( direction == SSH_DIR_FROM_SERVER ) { sessionp->state_flags |= SSH_FLG_SERV_PKEY_SEEN; } else if ( ssh_config.EnabledAlerts & SSH_ALERT_WRONGDIR ) { /* Server msg not from server. */ ALERT(SSH_EVENT_WRONGDIR, SSH_EVENT_WRONGDIR_STR); } break; case SSH_MSG_V1_CMSG_SESSION_KEY: if ( direction == SSH_DIR_FROM_CLIENT ) { sessionp->state_flags |= SSH_FLG_CLIENT_SKEY_SEEN; } else if ( ssh_config.EnabledAlerts & SSH_ALERT_WRONGDIR ) { /* Client msg not from client. */ ALERT(SSH_EVENT_WRONGDIR, SSH_EVENT_WRONGDIR_STR); } break; default: /* Invalid msg type */ break; } /* Once the V1 key exchange is done, remainder of * communications are encrypted. */ if ( (sessionp->state_flags & SSH_FLG_V1_KEYEXCH_DONE) == SSH_FLG_V1_KEYEXCH_DONE ) { sessionp->state_flags |= SSH_FLG_SESS_ENCRYPTED; } } else if ( sessionp->version == SSH_VERSION_2 ) { /* We want to overlay the payload on our data packet struct, * so first verify that the payload size is big enough. * This may legitimately occur such as in the case of a * retransmission. */ if ( packetp->payload_size < sizeof(SSH2Packet) ) { return SSH_FAILURE; } /* Overlay the SSH2 binary data packet struct on the packet */ ssh2packetp = (SSH2Packet*) packetp->payload; if (( packetp->payload_size < SSH2_HEADERLEN + 1) || ( packetp->payload_size < ntohl(ssh2packetp->packet_length) )) { /* Invalid packet length. */ return SSH_FAILURE; } switch ( packetp->payload[SSH2_HEADERLEN] ) { case SSH_MSG_KEXINIT: sessionp->state_flags |= (direction == SSH_DIR_FROM_SERVER ? SSH_FLG_SERV_KEXINIT_SEEN : SSH_FLG_CLIENT_KEXINIT_SEEN ); break; default: /* Unrecognized message type. */ break; } } else { if(ssh_config.EnabledAlerts & SSH_ALERT_UNRECOGNIZED) { /* Unrecognized version. */ ALERT(SSH_EVENT_VERSION, SSH_VERSION_STR); } return SSH_FAILURE; } return SSH_SUCCESS;}/* Called to process SSH2 key exchange msgs (key exch init msgs already * processed earlier). On failure, inspection will be continued, but the * packet will be alerted on, and ignored. * * PARAMETERS: * * sessionp: Pointer to SSH data for packet's session. * packetp: Pointer to the packet to inspect. * direction: Which direction the packet is going. * * RETURN: SSH_SUCCESS, if a valid key exchange message is processed * SSH_FAILURE, otherwise. */static intProcessSSHKeyExchange( SSHData* sessionp, SFSnortPacket* packetp, u_int8_t direction ){ SSH2Packet* ssh2packetp = NULL; if ( packetp->payload_size < sizeof(SSH2Packet) ) { /* Invalid packet length. */ return SSH_FAILURE; } ssh2packetp = (SSH2Packet*) packetp->payload; if (( packetp->payload_size < SSH2_HEADERLEN + 1 ) || ( packetp->payload_size < ntohl(ssh2packetp->packet_length) )) { if(ssh_config.EnabledAlerts & SSH_ALERT_PAYSIZE) { /* Invalid packet length. */ ALERT(SSH_EVENT_PAYLOAD_SIZE, SSH_PAYLOAD_SIZE_STR); } return SSH_FAILURE; } switch( packetp->payload[SSH2_HEADERLEN] ) { case SSH_MSG_KEXDH_INIT: if ( direction == SSH_DIR_FROM_CLIENT ) { sessionp->state_flags |= SSH_FLG_KEXDH_INIT_SEEN; } else if ( ssh_config.EnabledAlerts & SSH_ALERT_WRONGDIR ) { /* Client msg from server. */ ALERT(SSH_EVENT_WRONGDIR, SSH_EVENT_WRONGDIR_STR); } break; case SSH_MSG_KEXDH_REPLY: if ( direction == SSH_DIR_FROM_SERVER ) { /* KEXDH_REPLY has the same msg * type as the new style GEX_REPLY */ sessionp->state_flags |= SSH_FLG_KEXDH_REPLY_SEEN | SSH_FLG_GEX_REPLY_SEEN; } else if ( ssh_config.EnabledAlerts & SSH_ALERT_WRONGDIR ) { /* Server msg from client. */ ALERT(SSH_EVENT_WRONGDIR, SSH_EVENT_WRONGDIR_STR); } break; case SSH_MSG_KEXDH_GEX_REQ: if ( direction == SSH_DIR_FROM_CLIENT ) { sessionp->state_flags |= SSH_FLG_GEX_REQ_SEEN; } else if ( ssh_config.EnabledAlerts & SSH_ALERT_WRONGDIR ) { /* Server msg from client. */ ALERT(SSH_EVENT_WRONGDIR, SSH_EVENT_WRONGDIR_STR); } break; case SSH_MSG_KEXDH_GEX_GRP: if ( direction == SSH_DIR_FROM_SERVER ) { sessionp->state_flags |= SSH_FLG_GEX_GRP_SEEN; } else if ( ssh_config.EnabledAlerts & SSH_ALERT_WRONGDIR ) { /* Client msg from server. */ ALERT(SSH_EVENT_WRONGDIR, SSH_EVENT_WRONGDIR_STR); } break; case SSH_MSG_KEXDH_GEX_INIT: if ( direction == SSH_DIR_FROM_CLIENT ) { sessionp->state_flags |= SSH_FLG_GEX_INIT_SEEN; } else if ( ssh_config.EnabledAlerts & SSH_ALERT_WRONGDIR ) { /* Server msg from client. */ ALERT(SSH_EVENT_WRONGDIR, SSH_EVENT_WRONGDIR_STR); } break; case SSH_MSG_NEWKEYS: /* This message is required to complete the * key exchange. Both server and client should * send one, but as per Alex Kirk's note on this, * in some implementations the server does not * actually send this message. So receving a new * keys msg from the client is sufficient. */ if ( direction == SSH_DIR_FROM_CLIENT ) { sessionp->state_flags |= SSH_FLG_NEWKEYS_SEEN; } break; default: /* Unrecognized message type. */ break; } /* If either an old-style or new-style Diffie Helman exchange * has completed, the session will enter encrypted mode. */ if (( (sessionp->state_flags & SSH_FLG_V2_DHOLD_DONE) == SSH_FLG_V2_DHOLD_DONE ) || ( (sessionp->state_flags & SSH_FLG_V2_DHNEW_DONE) == SSH_FLG_V2_DHNEW_DONE )) { sessionp->state_flags |= SSH_FLG_SESS_ENCRYPTED; } return SSH_SUCCESS;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -