Ejemplo n.º 1
0
/* Main runtime entry point for SSH preprocessor.
 * Analyzes SSH packets for anomalies/exploits.
 *
 * PARAMETERS:
 *
 * packetp:    Pointer to current packet to process.
 * contextp:    Pointer to context block, not used.
 *
 * RETURNS:     Nothing.
 */
static void
ProcessSSH( void* ipacketp, void* contextp )
{
	SSHData* sessp = NULL;
	uint8_t source = 0;
	uint8_t dest = 0;
	uint8_t known_port = 0;
	uint8_t direction;
	SFSnortPacket* packetp;
#ifdef TARGET_BASED
    int16_t app_id = SFTARGET_UNKNOWN_PROTOCOL;
#endif
    tSfPolicyId policy_id = _dpd.getRuntimePolicy();
    PROFILE_VARS;

	packetp = (SFSnortPacket*) ipacketp;
    sfPolicyUserPolicySet (ssh_config, policy_id);

	/* Make sure this preprocessor should run. */
	if (( !packetp ) ||
	    ( !packetp->payload ) ||
	    ( !packetp->payload_size ) ||
        ( !IPH_IS_VALID(packetp) ) ||
	    ( !packetp->tcp_header ) ||
        /* check if we're waiting on stream reassembly */
        ( packetp->flags & FLAG_STREAM_INSERT))
	{
 		return;
	}

    PREPROC_PROFILE_START(sshPerfStats);

    ssh_eval_config = sfPolicyUserDataGetCurrent(ssh_config);

	/* Attempt to get a previously allocated SSH block. */
	sessp = _dpd.streamAPI->get_application_data(packetp->stream_session_ptr, PP_SSH);
    if (sessp != NULL)
    {
        ssh_eval_config = sfPolicyUserDataGet(sessp->config, sessp->policy_id);
        known_port = 1;
    }

    if (sessp == NULL)
    {
        /* If not doing autodetection, check the ports to make sure this is
         * running on an SSH port, otherwise no need to examine the traffic.
         */
#ifdef TARGET_BASED
        app_id = _dpd.streamAPI->get_application_protocol_id(packetp->stream_session_ptr);
        if (app_id == SFTARGET_UNKNOWN_PROTOCOL)
        {
            PREPROC_PROFILE_END(sshPerfStats);
            return;
        }

        if (app_id && (app_id != ssh_app_id))
        {
            PREPROC_PROFILE_END(sshPerfStats);
            return;
        }

        if (app_id == ssh_app_id)
        {
            known_port = 1;
        }

        if (!app_id)
        {
#endif
            source = (uint8_t)CheckSSHPort( packetp->src_port );
            dest = (uint8_t)CheckSSHPort( packetp->dst_port );

            if ( !ssh_eval_config->AutodetectEnabled && !source && !dest )
            {
                /* Not one of the ports we care about. */
                PREPROC_PROFILE_END(sshPerfStats);
                return;
            }
#ifdef TARGET_BASED
        }
#endif
        /* Check the stream session. If it does not currently
         * have our SSH data-block attached, create one.
         */
        sessp = SSHGetNewSession(packetp, policy_id);

        if ( !sessp )
        {
            /* Could not get/create the session data for this packet. */
            PREPROC_PROFILE_END(sshPerfStats);
            return;
        }

        /* See if a known server port is involved. */
        if (!known_port)
        {
            known_port = ( source || dest ? 1 : 0 );

            /* If this is a non-SSH port, but autodetect is on, we flag this
               session to reduce false positives later on. */
            if (!known_port && ssh_eval_config->AutodetectEnabled)
            {
                sessp->state_flags |= SSH_FLG_AUTODETECTED;
            }
        }
    }

    /* Don't process if we've missed packets */
    if (sessp->state_flags & SSH_FLG_MISSED_PACKETS)
        return;

    /* If we picked up mid-stream or missed any packets (midstream pick up
     * means we've already missed packets) set missed packets flag and make
     * sure we don't do any more reassembly on this session */
    if ((_dpd.streamAPI->get_session_flags(packetp->stream_session_ptr) & SSNFLAG_MIDSTREAM)
            || _dpd.streamAPI->missed_packets(packetp->stream_session_ptr, SSN_DIR_BOTH))
    {
        /* Don't turn off reassembly if autodetected since another preprocessor
         * may actually be looking at this session as well and the SSH
         * autodetect of this session may be wrong. */
        if (!(sessp->state_flags & SSH_FLG_AUTODETECTED))
        {
            _dpd.streamAPI->set_reassembly(packetp->stream_session_ptr,
                    STREAM_FLPOLICY_IGNORE, SSN_DIR_BOTH,
                    STREAM_FLPOLICY_SET_ABSOLUTE);
        }

        sessp->state_flags |= SSH_FLG_MISSED_PACKETS;

        return;
    }

    /* We're interested in this session. Turn on stream reassembly. */
    if ( !(sessp->state_flags & SSH_FLG_REASSEMBLY_SET ))
    {
        _dpd.streamAPI->set_reassembly(packetp->stream_session_ptr,
                        STREAM_FLPOLICY_FOOTPRINT, SSN_DIR_BOTH, STREAM_FLPOLICY_SET_APPEND);
        sessp->state_flags |= SSH_FLG_REASSEMBLY_SET;
    }

    /* Get the direction of the packet. */
    direction = ( (packetp->flags & FLAG_FROM_SERVER ) ?
            SSH_DIR_FROM_SERVER : SSH_DIR_FROM_CLIENT );

	if ( !(sessp->state_flags & SSH_FLG_SESS_ENCRYPTED ))
	{
		/* If server and client have not performed the protocol
		 * version exchange yet, must look for version strings.
	 	 */
		if ( (sessp->state_flags & SSH_FLG_BOTH_IDSTRING_SEEN)
			!= SSH_FLG_BOTH_IDSTRING_SEEN )
		{
			if ( ProcessSSHProtocolVersionExchange( sessp,
					packetp, direction, known_port ) ==
				SSH_FAILURE )
			{
				/*Error processing protovers exchange msg */
			}

            PREPROC_PROFILE_END(sshPerfStats);
			return;
		}

		/* Expecting to see the key init exchange at this point
		 * (in SSH2) or the actual key exchange if SSH1
		 */
		if ((( sessp->state_flags & SSH_FLG_V1_KEYEXCH_DONE )
			!= SSH_FLG_V1_KEYEXCH_DONE ) &&
		     ((sessp->state_flags & SSH_FLG_V2_KEXINIT_DONE )
			!= SSH_FLG_V2_KEXINIT_DONE ))
		{
		    ProcessSSHKeyInitExchange( sessp, packetp, direction );

            PREPROC_PROFILE_END(sshPerfStats);
			return;
		}

		/* If SSH2, need to process the actual key exchange msgs.
		 * The actual key exchange type was negotiated in the
		 * key exchange init msgs. SSH1 won't arrive here.
		 */
		ProcessSSHKeyExchange( sessp, packetp, direction );
	}
	else
	{
		/* Traffic on this session is currently encrypted.
		 * Two of the major SSH exploits, SSH1 CRC-32 and
 		 * the Challenge-Response Overflow attack occur within
         * the encrypted portion of the SSH session. Therefore,
         * the only way to detect these attacks is by examining
		 * amounts of data exchanged for anomalies.
  		 */
		sessp->num_enc_pkts++;

        if ( sessp->num_enc_pkts <= ssh_eval_config->MaxEncryptedPackets )
        {
            if ( direction == SSH_DIR_FROM_CLIENT )
            {
                sessp->num_client_bytes += packetp->payload_size;

                if ( sessp->num_client_bytes >=
                     ssh_eval_config->MaxClientBytes )
                {
                    /* Probable exploit in progress.*/
                    if (sessp->version == SSH_VERSION_1)
                    {
                        if ( ssh_eval_config->EnabledAlerts & SSH_ALERT_CRC32 )
                        {
                            ALERT(SSH_EVENT_CRC32, SSH_EVENT_CRC32_STR);

                            _dpd.streamAPI->stop_inspection(
                                                            packetp->stream_session_ptr,
                                                            packetp,
                                                            SSN_DIR_BOTH, -1, 0 );
                        }
                    }
                    else
                    {
                        if (ssh_eval_config->EnabledAlerts & SSH_ALERT_RESPOVERFLOW )
                        {
                            ALERT(SSH_EVENT_RESPOVERFLOW, SSH_EVENT_RESPOVERFLOW_STR);

                            _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);
}
Ejemplo n.º 2
0
/* Main runtime entry point for SSH preprocessor. 
 * Analyzes SSH packets for anomalies/exploits. 
 * 
 * PARAMETERS:
 *
 * packetp:    Pointer to current packet to process. 
 * contextp:    Pointer to context block, not used.
 *
 * RETURNS:     Nothing.
 */
static void
ProcessSSH( void* ipacketp, void* contextp )
{
	SSHData* sessp = NULL;
	u_int8_t source = 0;
	u_int8_t dest = 0;
	u_int8_t known_port = 0;
	u_int8_t direction; 
	SFSnortPacket* packetp;
    PROFILE_VARS;

	packetp = (SFSnortPacket*) ipacketp;

	/* Make sure this preprocessor should run. */
	if (( !packetp ) ||
	    ( !packetp->payload ) ||
	    ( !packetp->payload_size ) ||
	    ( !packetp->ip4_header ) ||
	    ( !packetp->tcp_header ) ||
        /* check if we're waiting on stream reassembly */
        ( packetp->flags & FLAG_STREAM_INSERT))
	{
 		return;
	} 

    /* If we picked up mid-stream do not process further */
    if ( _dpd.streamAPI->get_session_flags(
            packetp->stream_session_ptr) & SSNFLAG_MIDSTREAM )
    {
        return;
    }

	/* If not doing autodetection, check the ports to make sure this is 
	 * running on an SSH port, otherwise no need to examine the traffic.
	 */
	source = CheckSSHPort( packetp->src_port );
	dest = CheckSSHPort( packetp->dst_port );

	if ( !ssh_config.AutodetectEnabled && !source && !dest )
	{
		/* Not one of the ports we care about. */
		return;
	}

    PREPROC_PROFILE_START(sshPerfStats);

	/* See if a known server port is involved. */
	known_port = ( source || dest ? 1 : 0 );

	/* Get the direction of the packet. */
	direction = ( (packetp->flags & FLAG_FROM_SERVER ) ? 
			SSH_DIR_FROM_SERVER : SSH_DIR_FROM_CLIENT );

	/* Check the stream session. If it does not currently
	 * have our SSH data-block attached, create one.
	 */
	sessp = GetSSHData( packetp );

	if ( !sessp )
	{
		/* Could not get/create the session data for this packet. */
        PREPROC_PROFILE_END(sshPerfStats);
		return;
	}

	if ( !(sessp->state_flags & SSH_FLG_SESS_ENCRYPTED ))
	{
		/* If server and client have not performed the protocol 
		 * version exchange yet, must look for version strings.
	 	 */
		if ( (sessp->state_flags & SSH_FLG_BOTH_IDSTRING_SEEN)
			!= SSH_FLG_BOTH_IDSTRING_SEEN )
		{
			if ( ProcessSSHProtocolVersionExchange( sessp, 
					packetp, direction, known_port ) ==
				SSH_FAILURE )
			{
				/*Error processing protovers exchange msg */
			}

            PREPROC_PROFILE_END(sshPerfStats);
			return;
		}

		/* Expecting to see the key init exchange at this point 
		 * (in SSH2) or the actual key exchange if SSH1 
		 */
		if ((( sessp->state_flags & SSH_FLG_V1_KEYEXCH_DONE )
			!= SSH_FLG_V1_KEYEXCH_DONE ) &&
		     ((sessp->state_flags & SSH_FLG_V2_KEXINIT_DONE )
			!= SSH_FLG_V2_KEXINIT_DONE ))
		{
		    ProcessSSHKeyInitExchange( sessp, packetp, direction );
			
            PREPROC_PROFILE_END(sshPerfStats);
			return;
		}

		/* If SSH2, need to process the actual key exchange msgs.
		 * The actual key exchange type was negotiated in the
		 * key exchange init msgs. SSH1 won't arrive here.
		 */
		ProcessSSHKeyExchange( sessp, packetp, direction );
	}
	else
	{
		/* Traffic on this session is currently encrypted. 
		 * Two of the major SSH exploits, SSH1 CRC-32 and
 		 * the GOBBLES attack occur within the encrypted 
		 * portion of the SSH session. Therefore, the only
		 * way to detect these attacks is by examining 
		 * amounts of data exchanged for anomalies.
  		 */
		sessp->num_enc_pkts++;

		if ( sessp->num_enc_pkts <= ssh_config.MaxEncryptedPackets )
		{
			if ( direction == SSH_DIR_FROM_CLIENT )
			{
			   sessp->num_client_bytes += packetp->payload_size;

			   if ( sessp->num_client_bytes >= 
				ssh_config.MaxClientBytes ) 
			   {
				/* Probable exploit in progress.*/
				if (sessp->version == SSH_VERSION_1) 
				{
					if ( ssh_config.EnabledAlerts & SSH_ALERT_CRC32 )
					{
        			    ALERT(SSH_EVENT_CRC32, SSH_EVENT_CRC32_STR);

			            _dpd.streamAPI->stop_inspection( 
            				packetp->stream_session_ptr, 
            				packetp, 
            				SSN_DIR_BOTH, -1, 0 ); 
					}
				}
				else
				{
					if ( ssh_config.EnabledAlerts & SSH_ALERT_GOBBLES )
					{
						ALERT(SSH_EVENT_GOBBLES, SSH_EVENT_GOBBLES_STR);

			            _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);
}