Download as txt, pdf, or txt
Download as txt, pdf, or txt
You are on page 1of 5

Hello, and welcome

back to this course. In the previous video, we started talking about


detecting brute force password guessing or credential stuffing
attacks over the network. In that video, we
talked about one of the easy cases where we have access to the various
commands being used to authenticate to FTP; and so, we are able to track the status
of those
commands and see the message from the server saying that an authentication
attempt had failed. In this video, we're
going to look at a bit more of a realistic case, one where we don't have
that level of data. We'll be looking at SSH. Our problem with SSH is that SSH is
designed to be an
encrypted protocol. SSH, that client and
the server set up a shared encryption key using
asymmetric cryptography; and then with that
shared encryption key, they're able to encrypt everything else that
they say to one another. Since things like
authentication are encrypted, we're not able to take a
look and see what user accounts the user is
trying to log into, what password they provide, and whether or not
they succeeded. In general, that's a good thing. However, when we're trying to
determine if we're a victim of a credential stuffing attack using solely network
traffic, that makes our lives a
little bit more difficult. However, it's still possible to detect a potential
credential stuffing attack, it just takes a little
bit more work with SSH. Once again, we've got
Wireshark open here; and in this case, we have a file with four
login attempts to SSH. The first and last
are successful and the middle two are unsuccessful using an
incorrect password. If we look at the data that we can see
in our network traffic, there's not a lot of use here. For example, I can do
follow TCP stream, to see the contents
of the TCP stream. We see the handshake where
they set up parameters, which is potentially
interesting if you've gotten insecure SSH, client, and server. But if it's secure,
this is all public data anyway. Then after that we
just have gibberish, because we're
trying to interpret encrypted data as something
that actually make sense. If I flick through
the streams here, we see more of the same. I just told you
that the first and the last streams or authentication attempts
in this capture are the two successful ones. As I flick back and forth
through the streams, that might give you a hint
about how we're going to differentiate successful
logins and failed ones. We've got a successful one
here and a failed one here. They're much
different in length. Because if you have
a successful login, you're connected to the system, you're able to interact with it
and actually do something. In this case, when we've
got a successful one here, there's not actually
much done on the system, but just the fact that we have a successful
authentication; and then, we get the banner from the server saying that
the login was successful, is enough to create a
fairly long packet capture. When we go to a
failed one where we tried to log in, it didn't work. Then the client
immediately terminated the connection rather
than trying to enter another password; we have a much shorter capture. This
difference in
length can let us guess whether or not a connection
was successful or not. We've got a vague
feeling here by looking just at the streams, but we can actually
get something a little bit more concrete
using Wireshark. Wireshark has this
statistics area and if we choose conversations, we see that there's
four TCP conversations here, that makes sense. SSH occurs over TCP and we've got
four
connection attempts. Here, we can see the
IP source address, source port, destination
address, destination port. We see high number
of ports here for our client and a consistent
port 22 for the server. We see the number
of packets sent. Here, we can already
see some differences. We've got 25 packets sent
for a failed attempt, and closer to 40 for
a successful one. We see that the number of
bytes sent is much larger for a successful attempt than
for a failed one, etc. What we're going
to focus on here is the bytes from the
server to the client, because notice that if we go into the client server bytes,
there's not much variation. Got about a little
under 3,000 for a successful attack
and a little under 2,400 for an unsuccessful one. We'd have to tune our threshold
very tight to differentiate those and so if there was any
variation for some reason, then we'd get a false
positive or a false negative. However, we look at the
server in this case, we see over 8,000 bytes
sent from the server to the client and closer to
2,000 for client to server. A bigger gap gives us a little
bit more room for error. That's what we're
going to be focusing on in our Python analysis. I'm going to minimize this and
we'll go back to our code. This is the same file that we looked at in the previous
video, here's our FTP analysis. Down here we talk about
making the decision to go to FTP or SSH and then we
didn't talk through this, but this section here is just designed to print
the results that we saw in the previous video and that we'll see
in this one as well. But our focus here is going
to be on the SSH traffic. Assuming that we have a packet that's going to the
server, and assuming that that
server is set up, normally, we know that its destination
port is going to be unknown. That's the high
number client port, but this source
port from server to client is going to be port 22, if we've got a server
to client transmission. That's the one we care
about because we just looked in Wireshark and saw that the server to client byte
counts are widely
different for a successful, non-successful authentication in this particular case
and we'll talk a bit more about why in a moment but the client to server
one's not that different. Here in our SSH
analysis function, we should only be looking at
traffic from the server to the client and so we'll pull out a few different
pieces of information. We know that the server IP
address will be the source IP, the Client IP will be
the destination IP, and so we're going to use that same keying
as we did for FTP, saying going from the client to the server so from CIP to SIP.
In practice, it's
going to be from the destination IP to source IP. We also know that the
only port that really provides us any information
here is the destination port, because the server port
is always going to be 22, the destination port is
going to vary and be a high number port and so we're looking at the length of the
traffic that
we're sending. Easiest way to get that
is to take a look inside our packet and pull out the
length field of an IP packet. That'll tell us
the amount of data stored in that IP
packet and if we add 14 we'll get the total amount including the headers that
aren't included in
that length value, so L here is the length of the packet server
sending to the client. Now we have an if statement
and the reason why is, we want to be able to say whether or not a
connection is closed and failed and we could use
an easy way to do that. With SSH, if things go properly, you should have a nice
tear down using a fin, thin AC, etc. If the connections
died for some reason, there should be a packet
from the server to the client saying fin or having an f flag in its TCP packets and
so we're
going to test for that. If we have that f we're tearing down and
we're going to take the records that
we're building and move it to the
failed SSH category. Otherwise, we're going to add
the length that we've just observed to a dictionary that we're building similar
to what we did with the FTP. Let's do that second
case first here. Every packet that's not
tearing down the connection, we're first going to see
if we've already got this connection in our
dictionary of SSH connection. At this point, we're looking
at a pair of IP addresses, source, and destination. If we don't already have
that in our dictionary, we're going to add it and assign an empty dictionary to it.
Then we need to
determine whether or not this particular port
is in that dictionary. That port again is going to be the client port because
that's the high number
port that varies, allowing us to differentiate different SSH connections
from one another. Because theoretically,
you could have multiple different SSH sessions
running at the same time from the same computer
and so they'd both be going to the same port but
have different source ports. If the port already exists
in that dictionary, we're going to take its current length value that's stored and
add in the
length of our new packet. Otherwise, we're going
to test to see if there is a SYN flag
set in our packet, because that means
that we're setting up a new SSH connection. This matters because I
just said a few moments ago that we're testing for the FIN flag from the
server to the client. That's not actually
the last packet that the server sends. In fact, there's a chance that the server
will send a FIN flag, the client will send a FIN ACK, and then the server
will send an ACK, acknowledging that final FIN ACK and tearing down the connection.
In this case, we've got
the potential that we have a packet coming from a connection we've already
decided to ignore. To ensure that we
don't have that case, we're going to look for a SYN flag set in
the servers' packet, because that means
we're at the start of a connection and not the end. If this is a brand
new connection, we'll add it to
our dictionary and specify that it's current length is the length of this packet.
Regardless of if
we're successful or unsuccessful with
authentication, this will happen several
times as the client and server negotiate all the
terms of the connection, and then the client sends in
its username and password. Then if we've got a
successful connection, there's even the
possibility of the client's going to do something
with that connection. Perform some commands
on the server. But eventually, we'll get to the point
where that connection gets torn down using
FINs and facts. If we've got a FIN
packet from the server, then we know that
we need to take this particular connection
record out of our SSH cons, which tracks existing
live connections and puts it in our
failed SSH one, if it was a failed
connection attempts. We're going to pop the record
for a particular IP pair and the client port and the
result will be stored in b, so that b should
be the length of the packets that
had been sent from the server to the client
in this connection. We're going to add in the
length of our current packet, and then we're going to test
it against a threshold. In this case, I'm using
a threshold of 5,000, but this might have to vary based off of your
implementation. I'm using 5,000 here because
that's what's used in Bro/Zeke for this
exact same purpose. However, depending on how
your server is configured, banners, etc, you might need
it to go lower or higher. As we saw here, 5,000 is a
nice safe value in this case. The reason why in this case is the server I'm
connecting to is default Vanilla Ubuntu server and so it's banner message
is quite long. That gives us that nice long
8,000 bytes being sent from the server because of
all that information it provides to the user
about like last login, information on the system, etc. If your servers have been
modified to have a different
banner that's shorter, you might need a
different threshold. But if the bytes from the server to the client is
less than that threshold, we've got a good chance that we're looking at a
failed login attempt. If so, we're going to check our failed SSH dictionary
to see if our key, so that IP pair
is already there. If so, we're going to increment the number of failed
login attempts. Otherwise, we'll
create a new entry in the dictionary for that
failed login attempt. Important to notice here, a major difference between
this and our FTP version, and that's that we're
not tracking usernames. The reason why is we can't. That authentication
occurs within the encrypted connection
and so we can't see what username was
being sent to the server. The best we can do is say, okay, there's a failed login
from this user or from this IP address
to this IP address. That at least helps provide us some information about
whether we might have an attempted brute force or credential stuffing attack. If we
can identify the
target of that attack, we can look at that
particular server and its records to see which user was the
target of that attack. Let's give this version of
the code a run as well. In this case, note
I've commented out the FTP related stuff and we'll look at the
SSH packet capture now. If I do Python,
BruteForceNetworkDetection.py, hit Enter and we
see that we have two failed login attempts from this IP address
to this IP address. You can see that we're
collecting here on the client's local network to
the 192.168 local address. However, if we were
doing this defensively, we might see something
like a login attempt from a public-facing IP to
a private one instead. Based off of this with
only two failures, as we discussed before, two failures might
not indicate that we've got an attempted attack. However, if I was seeing
200 or 2,000 or whatever, that might be an indication
that something's going wrong. It depends on how often that
system should be accessed and you can also look at should this IP address have
access to that system. If not, any number of
failed login attempts or even successful ones is indication that something's wrong
and more
investigation is needed. This closes up our discussion of brute force detection
over the network. Previous video, we looked
at an easy case with FTP. Now we looked at one based
a little bit more on heuristics and guessing
for SSH. Thank you.

You might also like