?? spp_ssh.c
字號:
/* $Id *//*** Copyright (C) 2005 Sourcefire Inc.****** This program is free software; you can redistribute it and/or modify** it under the terms of the GNU General Public License Version 2 as** published by the Free Software Foundation. You may not use, modify or** distribute this program under any other version of the GNU General** Public License.**** This program is distributed in the hope that it will be useful,** but WITHOUT ANY WARRANTY; without even the implied warranty of** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the** GNU General Public License for more details.**** You should have received a copy of the GNU General Public License** along with this program; if not, write to the Free Software** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.*//* * SSH preprocessor * Author: Chris Sherwin * Contributors: Adam Keeton * * * Alert for Gobbles, CRC32, protocol mismatch (Cisco catalyst vulnerability), * and a SecureCRT vulnerability. Will also alert if the client or server * traffic appears to flow the wrong direction, or if packets appear * malformed/spoofed. * */#ifdef HAVE_CONFIG_H#include "config.h"#endif /* HAVE_CONFIG_H */#include "sf_snort_packet.h"#include "sf_dynamic_preprocessor.h"#include "sf_snort_plugin_api.h"#include "debug.h"#include "preprocids.h"#include "spp_ssh.h"#include <stdio.h>#include <syslog.h>#include <string.h>#ifndef WIN32#include <strings.h>#include <sys/time.h>#endif#include <stdlib.h>#include <ctype.h>#include "profiler.h"#ifdef PERF_PROFILINGPreprocStats sshPerfStats;#endif/* * Generator id. Define here the same as the official registry * in generators.h */#define GENERATOR_SPP_SSH 128/* * Function prototype(s) */SSHData* GetSSHData( SFSnortPacket* );static void SSHInit( char* );static void DisplaySSHConfig( void );static void FreeSSHData( void* );static void ParseSSHArgs( u_char* );static void ProcessSSH( void*, void* );static INLINE int CheckSSHPort( u_int16_t );static int ProcessSSHProtocolVersionExchange( SSHData*, SFSnortPacket*, u_int8_t, u_int8_t );static int ProcessSSHKeyExchange( SSHData*, SFSnortPacket*, u_int8_t );static int ProcessSSHKeyInitExchange( SSHData*, SFSnortPacket*, u_int8_t );/* Ultimately calls SnortEventqAdd *//* Arguments are: gid, sid, rev, classification, priority, message, rule_info */#define ALERT(x,y) { _dpd.alertAdd(GENERATOR_SPP_SSH, x, 1, 0, 3, y, 0 ); }/* Convert port value into an index for the ssh_config.ports array */#define PORT_INDEX(port) port/8/* Convert port value into a value for bitwise operations */#define CONV_PORT(port) 1<<(port%8)/* * SSH preprocessor global configuration structure. */static SSHConfig ssh_config = { 0, /* Autodetection */ SSH_DEFAULT_MAX_ENC_PKTS, /* Max enc pkts */ SSH_DEFAULT_MAX_CLIENT_BYTES, /* Max client bytes */ 0, /* Disable rules */ SSH_ALERT_ALL, /* Enabled alerts */ };extern DynamicPreprocessorData _dpd;/* Called at preprocessor setup time. Links preprocessor keyword * to corresponding preprocessor initialization function. * * PARAMETERS: None.* * RETURNS: Nothing. * */void SetupSSH(void){ /* Link preprocessor keyword to initialization function * in the preprocessor list. */ _dpd.registerPreproc( "ssh", SSHInit );}/* Initializes the SSH preprocessor module and registers * it in the preprocessor list. * * PARAMETERS: * * argp: Pointer to argument string to process for config * data. * * RETURNS: Nothing. */static voidSSHInit( char* argp ){#ifdef SUP_IP6 _dpd.fatalMsg("SSH is not currently supported when IPv6 is enabled.");#endif if(!_dpd.streamAPI) { DynamicPreprocessorFatalMessage("SetupSSH(): The Stream preprocessor must be enabled.\n"); } _dpd.addPreproc( ProcessSSH, PRIORITY_APPLICATION, PP_SSH ); ParseSSHArgs( (u_char *)argp );#ifdef PERF_PROFILING _dpd.addPreprocProfileFunc("ssh", (void *)&sshPerfStats, 0, _dpd.totalPerfStats);#endif}/* Parses and processes the configuration arguments * supplied in the SSH preprocessor rule. * * PARAMETERS: * * argp: Pointer to string containing the config arguments. * * RETURNS: Nothing. */static void ParseSSHArgs( u_char* argp ){ char* cur_tokenp = NULL; char* argcpyp = NULL; int port; /* Set up default port to listen on */ ssh_config.ports[ PORT_INDEX( 22 ) ] |= CONV_PORT(22); /* Sanity check(s) */ if ( !argp ) { DisplaySSHConfig(); return; } argcpyp = strdup( (char*) argp ); if ( !argcpyp ) { DynamicPreprocessorFatalMessage("Could not allocate memory to parse SSH options.\n"); return; } cur_tokenp = strtok( argcpyp, " "); while ( cur_tokenp ) { if ( !strcmp( cur_tokenp, SSH_SERVERPORTS_KEYWORD )) { /* If the user specified ports, remove '22' for now since * it now needs to be set explicitely. */ ssh_config.ports[ PORT_INDEX( 22 ) ] = 0; /* Eat the open brace. */ cur_tokenp = strtok( NULL, " "); if (( !cur_tokenp ) || ( cur_tokenp[0] != '{' )) { DynamicPreprocessorFatalMessage("Bad value specified for %s.\n", SSH_SERVERPORTS_KEYWORD); //free(argcpyp); //return; } cur_tokenp = strtok( NULL, " "); while (( cur_tokenp ) && ( cur_tokenp[0] != '}' )) { if ( !isdigit( (int)cur_tokenp[0] )) { DynamicPreprocessorFatalMessage("Bad port %s.\n", cur_tokenp ); //free(argcpyp); //return; } else { port = atoi( cur_tokenp ); if( port < 0 || port > MAX_PORTS ) { DynamicPreprocessorFatalMessage("Port value illegitimate: %s\n", cur_tokenp); //free(argcpyp); //return; } ssh_config.ports[ PORT_INDEX( port ) ] |= CONV_PORT(port); } cur_tokenp = strtok( NULL, " "); } } else if ( !strcmp( cur_tokenp, SSH_AUTODETECT_KEYWORD )) { ssh_config.AutodetectEnabled++; } else if ( !strcmp( cur_tokenp, SSH_MAX_ENC_PKTS_KEYWORD )) { cur_tokenp = strtok( NULL, " "); if (( !cur_tokenp ) || !isdigit((int)cur_tokenp[0]) ) { _dpd.logMsg("Bad value specified for %s." "Reverting to default value %d. ", SSH_MAX_ENC_PKTS_KEYWORD, SSH_DEFAULT_MAX_ENC_PKTS ); } else { ssh_config.MaxEncryptedPackets = (u_int16_t) atoi( cur_tokenp ); } } else if (!strcmp( cur_tokenp, SSH_MAX_CLIENT_BYTES_KEYWORD )) { cur_tokenp = strtok( NULL, " "); if (( !cur_tokenp ) || !isdigit((int)cur_tokenp[0]) ) { _dpd.logMsg("Bad value specified for %s." "Reverting to default value %d. ", SSH_MAX_CLIENT_BYTES_KEYWORD, SSH_DEFAULT_MAX_CLIENT_BYTES ); } else { ssh_config.MaxClientBytes = (u_int16_t) atoi( cur_tokenp ); } } else if ( !strcmp( cur_tokenp, SSH_DISABLE_GOBBLES_KEYWORD )) { ssh_config.EnabledAlerts &= ~SSH_ALERT_GOBBLES; } else if ( !strcmp( cur_tokenp, SSH_DISABLE_CRC32_KEYWORD )) { ssh_config.EnabledAlerts &= ~SSH_ALERT_CRC32; } else if ( !strcmp( cur_tokenp, SSH_DISABLE_SECURECRT_KEYWORD )) { ssh_config.EnabledAlerts &= ~SSH_ALERT_SECURECRT; } else if ( !strcmp( cur_tokenp, SSH_DISABLE_PROTOMISMATCH_KEYWORD )) { ssh_config.EnabledAlerts &= ~SSH_ALERT_PROTOMISMATCH; } else if ( !strcmp( cur_tokenp, SSH_DISABLE_WRONGDIR_KEYWORD )) { ssh_config.EnabledAlerts &= ~SSH_ALERT_WRONGDIR; } else if ( !strcmp( cur_tokenp, SSH_DISABLE_RULES_KEYWORD )) { ssh_config.DisableRules++; } else if( !strcmp( cur_tokenp, SSH_DISABLE_PAYLOAD_SIZE )) { ssh_config.EnabledAlerts &= ~SSH_ALERT_PAYSIZE; } else if( !strcmp( cur_tokenp, SSH_DISABLE_UNRECOGNIZED_VER )) { ssh_config.EnabledAlerts &= ~SSH_ALERT_UNRECOGNIZED; } else { DynamicPreprocessorFatalMessage("Invalid argument: %s\n", cur_tokenp); return; } cur_tokenp = strtok( NULL, " " ); } DisplaySSHConfig(); free(argcpyp);}/* Display the configuration for the SSH preprocessor. * * PARAMETERS: None. * * RETURNS: Nothing. */static voidDisplaySSHConfig(void){ int index; int newline; _dpd.logMsg("SSH config: \n"); _dpd.logMsg(" Autodetection: %s\n", ssh_config.AutodetectEnabled ? "ENABLED":"DISABLED"); _dpd.logMsg(" GOBBLES Alert: %s\n", ssh_config.EnabledAlerts & SSH_ALERT_GOBBLES ? "ENABLED" : "DISABLED" ); _dpd.logMsg(" SSH1 CRC32 Alert: %s\n", ssh_config.EnabledAlerts & SSH_ALERT_CRC32 ? "ENABLED" : "DISABLED" ); _dpd.logMsg(" Server Version String Overflow Alert: %s\n", ssh_config.EnabledAlerts & SSH_ALERT_SECURECRT ? "ENABLED" : "DISABLED" ); _dpd.logMsg(" Protocol Mismatch Alert: %s\n", ssh_config.EnabledAlerts & SSH_ALERT_PROTOMISMATCH? "ENABLED" : "DISABLED" ); _dpd.logMsg(" Bad Message Direction Alert: %s\n", ssh_config.EnabledAlerts & SSH_ALERT_WRONGDIR ? "ENABLED" : "DISABLED" ); _dpd.logMsg(" Bad Payload Size Alert: %s\n", ssh_config.EnabledAlerts & SSH_ALERT_PAYSIZE ? "ENABLED" : "DISABLED" ); _dpd.logMsg(" Unrecognized Version Alert: %s\n", ssh_config.EnabledAlerts & SSH_ALERT_UNRECOGNIZED ? "ENABLED" : "DISABLED" ); _dpd.logMsg(" Max Encrypted Packets: %d %s \n", ssh_config.MaxEncryptedPackets, ssh_config.MaxEncryptedPackets == SSH_DEFAULT_MAX_ENC_PKTS ? "(Default)" : "" ); if ( ssh_config.EnabledAlerts & (SSH_ALERT_GOBBLES | SSH_ALERT_CRC32)) { _dpd.logMsg(" MaxClientBytes: %d %s \n", ssh_config.MaxClientBytes, ssh_config.MaxClientBytes == SSH_DEFAULT_MAX_CLIENT_BYTES ? "(Default)" : "" ); } /* Traverse list, printing ports, 5 per line */ newline = 1; _dpd.logMsg(" Ports:\n"); for(index = 0; index < MAX_PORTS; index++) { if( ssh_config.ports[ PORT_INDEX(index) ] & CONV_PORT(index) ) { _dpd.logMsg("\t%d", index); if ( !((newline++)% 5) ) { _dpd.logMsg("\n"); } } } _dpd.logMsg("\n");}/* 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 voidProcessSSH( 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 = (u_int8_t)CheckSSHPort( packetp->src_port ); dest = (u_int8_t)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);
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -