-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
SSH.cc
177 lines (149 loc) · 4.75 KB
/
SSH.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
// See the file "COPYING" in the main distribution directory for copyright.
#include "SSH.h"
#include "analyzer/protocol/tcp/TCP_Reassembler.h"
#include "Reporter.h"
#include "types.bif.h"
#include "events.bif.h"
using namespace analyzer::SSH;
SSH_Analyzer::SSH_Analyzer(Connection* c)
: tcp::TCP_ApplicationAnalyzer("SSH", c)
{
interp = new binpac::SSH::SSH_Conn(this);
had_gap = false;
auth_decision_made = false;
skipped_banner = false;
saw_encrypted_client_data = false;
service_accept_size = 0;
userauth_failure_size = 0;
}
SSH_Analyzer::~SSH_Analyzer()
{
delete interp;
}
void SSH_Analyzer::Done()
{
tcp::TCP_ApplicationAnalyzer::Done();
interp->FlowEOF(true);
interp->FlowEOF(false);
}
void SSH_Analyzer::EndpointEOF(bool is_orig)
{
tcp::TCP_ApplicationAnalyzer::EndpointEOF(is_orig);
interp->FlowEOF(is_orig);
}
void SSH_Analyzer::DeliverStream(int len, const u_char* data, bool orig)
{
tcp::TCP_ApplicationAnalyzer::DeliverStream(len, data, orig);
assert(TCP());
if ( TCP()->IsPartial() )
return;
if ( had_gap )
// If only one side had a content gap, we could still try to
// deliver data to the other side if the script layer can handle this.
return;
if ( interp->get_state(orig) == binpac::SSH::ENCRYPTED )
{
ProcessEncryptedSegment(len, orig);
return;
}
interp->clear_encrypted_byte_count_in_current_segment();
try
{
interp->NewData(orig, data, data + len);
}
catch ( const binpac::Exception& e )
{
ProtocolViolation(fmt("Binpac exception: %s", e.c_msg()));
}
auto encrypted_len = interp->get_encrypted_bytes_in_current_segment();
if ( encrypted_len > 0 )
// We must have transitioned into the encrypted state during this
// delivery, but also had some portion of the segment be comprised
// of encrypted data, so process the encrypted segment length.
ProcessEncryptedSegment(encrypted_len, orig);
}
void SSH_Analyzer::Undelivered(uint64_t seq, int len, bool orig)
{
tcp::TCP_ApplicationAnalyzer::Undelivered(seq, len, orig);
had_gap = true;
interp->NewGap(orig, len);
}
void SSH_Analyzer::ProcessEncryptedSegment(int len, bool orig)
{
if ( ssh_encrypted_packet )
BifEvent::generate_ssh_encrypted_packet(interp->bro_analyzer(),
interp->bro_analyzer()->Conn(),
orig, len);
if ( ! auth_decision_made )
ProcessEncrypted(len, orig);
}
void SSH_Analyzer::ProcessEncrypted(int len, bool orig)
{
if ( interp->get_version() != binpac::SSH::SSH2 )
return;
if ( orig )
saw_encrypted_client_data = true;
else
{
// If the client hasn't sent any encrypted data yet, but the
// server is, just ignore it until seeing encrypted client data.
if ( ! saw_encrypted_client_data )
return;
// The first thing we see and want to know is the length of
// SSH_MSG_SERVICE_REQUEST, which has a fixed (decrypted) size
// of 24 bytes (17 for content pad-aligned to 8-byte
// boundaries)
if ( ! service_accept_size )
{
service_accept_size = len;
return;
}
// If our user can authenticate via the "none" method, this
// packet will be a SSH_MSG_USERAUTH_SUCCESS, which has a
// fixed (decrypted) size of 8 bytes (1 for content
// pad-aligned to 8-byte boundaries). relative_len would be
// -16.
if ( ! userauth_failure_size && (len + 16 == service_accept_size) )
{
auth_decision_made = true;
if ( ssh_auth_attempted )
BifEvent::generate_ssh_auth_attempted(interp->bro_analyzer(), interp->bro_analyzer()->Conn(), true);
if ( ssh_auth_successful )
BifEvent::generate_ssh_auth_successful(interp->bro_analyzer(), interp->bro_analyzer()->Conn(), true);
return;
}
// Normally, this packet would be a SSH_MSG_USERAUTH_FAILURE
// message, with a variable length, depending on the
// authentication methods the server supports. If it's too
// big, it might contain a pre-auth MOTD/banner, so we'll just
// skip it.
if ( ! userauth_failure_size )
{
if ( ! skipped_banner && (len - service_accept_size) > 256 )
{
skipped_banner = true;
return;
}
userauth_failure_size = len;
return;
}
// If we've already seen a failure, let's see if this is
// another packet of the same size.
if ( len == userauth_failure_size )
{
if ( ssh_auth_attempted )
BifEvent::generate_ssh_auth_attempted(interp->bro_analyzer(), interp->bro_analyzer()->Conn(), false);
return;
}
// ...or a success packet.
if ( len - service_accept_size == -16 )
{
auth_decision_made = true;
if ( ssh_auth_attempted )
BifEvent::generate_ssh_auth_attempted(interp->bro_analyzer(), interp->bro_analyzer()->Conn(), true);
if ( ssh_auth_successful )
BifEvent::generate_ssh_auth_successful(interp->bro_analyzer(), interp->bro_analyzer()->Conn(), false);
return;
}
}
}