ssh_config.h
ssh_module.cc
ssh_module.h
+ ssh_splitter.cc
+ ssh_splitter.h
)
if (STATIC_INSPECTORS)
#include "stream/stream.h"
#include "ssh_module.h"
+#include "ssh_splitter.h"
using namespace snort;
sshstats.concurrent_sessions--;
}
-static SSHData* SetNewSSHData(Packet* p)
+SSHData* SetNewSSHData(Packet* p)
{
SshFlowData* fd = new SshFlowData;
p->flow->set_flow_data(fd);
return &fd->session;
}
-static SSHData* get_session_data(Flow* flow)
+SSHData* get_session_data(const Flow* flow)
{
SshFlowData* fd = (SshFlowData*)flow->get_flow_data(SshFlowData::inspector_id);
return fd ? &fd->session : nullptr;
// Attempt to get a previously allocated SSH block.
SSHData* sessp = get_session_data(p->flow);
- if (sessp == nullptr)
- {
- /* Check the stream session. If it does not currently
- * have our SSH data-block attached, create one.
- */
- sessp = SetNewSSHData(p);
-
- if ( !sessp )
- // Could not get/create the session data for this packet.
- return;
-
- }
-
// Don't process if we've missed packets
if (sessp->state_flags & SSH_FLG_MISSED_PACKETS)
return;
*/
if ( direction == SSH_DIR_FROM_CLIENT )
{
- sessionp->state_flags |= SSH_FLG_NEWKEYS_SEEN;
+ sessionp->state_flags |= SSH_FLG_CLIENT_NEWKEYS_SEEN;
+ }
+ else
+ {
+ sessionp->state_flags |= SSH_FLG_SERVER_NEWKEYS_SEEN;
}
break;
default:
void show(const SnortConfig*) const override;
void eval(Packet*) override;
+ class StreamSplitter* get_splitter(bool to_server) override
+ { return new SshSplitter(to_server); }
private:
SSH_PROTO_CONF* config;
// packets appear malformed/spoofed.
#include "flow/flow.h"
+#include "protocols/packet.h"
// FIXIT-L move these to ssh.cc
// Session state flags for SSHData::state_flags
#define SSH_FLG_GEX_GRP_SEEN (0x200)
#define SSH_FLG_GEX_INIT_SEEN (0x400)
#define SSH_FLG_GEX_REPLY_SEEN (0x800)
-#define SSH_FLG_NEWKEYS_SEEN (0x1000)
+#define SSH_FLG_CLIENT_NEWKEYS_SEEN (0x1000)
#define SSH_FLG_SESS_ENCRYPTED (0x2000)
#define SSH_FLG_RESPOVERFLOW_ALERTED (0x4000)
#define SSH_FLG_CRC32_ALERTED (0x8000)
#define SSH_FLG_MISSED_PACKETS (0x10000)
#define SSH_FLG_REASSEMBLY_SET (0x20000)
#define SSH_FLG_AUTODETECTED (0x40000)
+#define SSH_FLG_SERVER_NEWKEYS_SEEN (0x8000)
// Some convenient combinations of state flags.
#define SSH_FLG_BOTH_IDSTRING_SEEN \
#define SSH_FLG_V2_DHOLD_DONE \
(SSH_FLG_KEXDH_INIT_SEEN | \
SSH_FLG_KEXDH_REPLY_SEEN | \
- SSH_FLG_NEWKEYS_SEEN )
+ SSH_FLG_CLIENT_NEWKEYS_SEEN )
#define SSH_FLG_V2_DHNEW_DONE \
(SSH_FLG_GEX_REQ_SEEN | \
SSH_FLG_GEX_GRP_SEEN | \
SSH_FLG_GEX_INIT_SEEN | \
SSH_FLG_GEX_REPLY_SEEN | \
- SSH_FLG_NEWKEYS_SEEN )
+ SSH_FLG_CLIENT_NEWKEYS_SEEN )
// SSH version values for SSHData::version
#define SSH_VERSION_UNKNOWN (0x0)
// Length of SSH2 header, in bytes.
#define SSH2_HEADERLEN (5)
+// Length of SSH2 Padding, in bytes.
+#define SSH2_PADDING_LEN (1)
+// Length of SSH2 packet, in bytes.
+#define SSH2_PACKET_LEN (SSH2_HEADERLEN - SSH2_PADDING_LEN)
#define SSH2_PACKET_MAX_SIZE (256 * 1024)
struct SSH2Packet
#define SSH_DIR_FROM_SERVER (0x1)
#define SSH_DIR_FROM_CLIENT (0x2)
+SSHData* get_session_data(const snort::Flow* flow);
+SSHData* SetNewSSHData(snort::Packet* p);
#endif
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2020 Cisco and/or its affiliates. All rights reserved.
+//
+// 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.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//--------------------------------------------------------------------------
+
+// ssh_splitter.cc author Pranav Bhalerao <prbhaler@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ssh.h"
+#include "ssh_splitter.h"
+
+using namespace snort;
+
+SshSplitter::SshSplitter(bool c2s) : StreamSplitter(c2s)
+{
+ client_remain_bytes = 0;
+ server_remain_bytes = 0;
+ state = 0;
+}
+
+StreamSplitter::Status SshSplitter::ssh2_key_exchange_scan(
+ const uint8_t* data, uint32_t len, uint32_t* fp,
+ uint32_t &remain_bytes)
+{
+ if (remain_bytes < len)
+ {
+ uint32_t offset = remain_bytes;
+ while (offset < len)
+ {
+ const SSH2Packet *sshp = (const SSH2Packet*)(data + offset);
+ uint32_t ssh_len = ntohl(sshp->packet_length);
+ if (ssh_len > (len - offset))
+ {
+ remain_bytes = ssh_len - (len - SSH2_PACKET_LEN);
+ return StreamSplitter::SEARCH;
+ }
+
+ switch (data[offset + SSH2_HEADERLEN])
+ {
+ case SSH_MSG_KEXDH_GEX_INIT:
+ case SSH_MSG_KEXDH_GEX_GRP:
+ case SSH_MSG_KEXDH_GEX_REQ:
+ case SSH_MSG_KEXDH_REPLY:
+ case SSH_MSG_KEXDH_INIT:
+ case SSH_MSG_KEXINIT:
+ offset += (ssh_len + SSH2_PACKET_LEN);
+ break;
+ case SSH_MSG_NEWKEYS:
+ offset += (ssh_len + SSH2_PACKET_LEN);
+ // fallthrough
+ default:
+ goto exit_loop;
+
+ }
+ }
+exit_loop:
+ *fp = offset;
+ return StreamSplitter::FLUSH;
+ }
+ else
+ {
+ remain_bytes = remain_bytes - len;
+ if (!remain_bytes)
+ {
+ *fp = len;
+ return StreamSplitter::FLUSH;
+ }
+ else
+ {
+ return StreamSplitter::SEARCH;
+ }
+ }
+}
+
+StreamSplitter::Status SshSplitter::ssh2_scan( SSHData* sessp,
+ const uint8_t* data, uint32_t len, uint32_t flags, uint32_t* fp)
+{
+ if (flags & PKT_FROM_SERVER)
+ {
+ // Do not scan if sever new keys message seen.
+ if (sessp->state_flags & SSH_FLG_SERVER_NEWKEYS_SEEN)
+ {
+ return SEARCH;
+ }
+
+ return ssh2_key_exchange_scan(data, len, fp, server_remain_bytes);
+ }
+ else
+ {
+ return ssh2_key_exchange_scan(data, len, fp, client_remain_bytes);
+ }
+}
+
+StreamSplitter::Status SshSplitter::scan(
+ Packet* p, const uint8_t* data, uint32_t len,
+ uint32_t flags, uint32_t* fp)
+{
+ Flow* flow = p->flow;
+ SSHData* sessp = get_session_data(flow);
+
+ if (nullptr == sessp)
+ {
+ sessp = SetNewSSHData(p);
+ if (nullptr == sessp)
+ return ABORT;
+ }
+
+ if ((sessp->state_flags & SSH_FLG_SERV_IDSTRING_SEEN)
+ && (sessp->state_flags & SSH_FLG_CLIENT_IDSTRING_SEEN))
+ {
+ state = SSH_PAF_KEY_EXCHANGE;
+ }
+
+ if (sessp->state_flags & SSH_FLG_SESS_ENCRYPTED)
+ {
+ state = SSH_PAF_ENCRYPTED;
+ }
+
+ switch(state)
+ {
+ case SSH_PAF_VER_EXCHANGE:
+ {
+ uint32_t n = len;
+ const uint8_t* lf = nullptr, *tmp = data;
+
+ while ((tmp = (const uint8_t*)memchr(tmp, '\n', n)))
+ {
+ lf = tmp++;
+ n = len - (tmp - data);
+ }
+ if (!lf)
+ return SEARCH;
+
+ *fp = lf - data + 1;
+ return FLUSH;
+ }
+ case SSH_PAF_KEY_EXCHANGE:
+ {
+ if (sessp->version == SSH_VERSION_2)
+ {
+ return ssh2_scan(sessp, data, len, flags, fp);
+ }
+ }
+ // fallthrough
+ default:
+ {
+ // there will not be multiple SSH payloads in single TCP PDU.
+ // for SSH1 or Encrypted PDUs flush it at data boundary.
+ *fp = len;
+ return FLUSH;
+ }
+ }
+}
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2020 Cisco and/or its affiliates. All rights reserved.
+//
+// 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.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//--------------------------------------------------------------------------
+
+// ssh_splitter.h author Pranav Bhalerao <prbhaler@cisco.com>
+
+#ifndef SSH_SPLITTER_H
+#define SSH_SPLITTER_H
+
+#include "protocols/packet.h"
+#include "stream/stream_splitter.h"
+
+enum SshPafState
+{
+ SSH_PAF_VER_EXCHANGE,
+ SSH_PAF_KEY_EXCHANGE,
+ SSH_PAF_ENCRYPTED
+};
+
+class SshSplitter : public snort::StreamSplitter
+{
+public:
+ SshSplitter(bool c2s);
+
+ Status scan(snort::Packet*, const uint8_t* data, uint32_t len,
+ uint32_t flags, uint32_t* fp) override;
+
+ bool is_paf() override
+ {
+ return true;
+ }
+
+private:
+ Status ssh2_key_exchange_scan(const uint8_t* data, uint32_t len,
+ uint32_t* fp, uint32_t& remain_bytes);
+ Status ssh2_scan(SSHData* sessp, const uint8_t* data, uint32_t len,
+ uint32_t flags, uint32_t* fp);
+
+ uint32_t state;
+ uint32_t client_remain_bytes = 0;
+ uint32_t server_remain_bytes = 0;
+};
+#endif