Intercepting Communication


Intro to Cybersecurity.

Learn various techniques to intercept and manipulate network communication, from connecting to remote hosts to performing man-in-the-middle attacks.


Lectures and Reading

To get started with this module, run the challenge located in /challenge from within a terminal session. This will create the network environment for you to work with. This will also change your terminal prompt to indicate the host machine you are working on. You can run the command exit to return to your original terminal prompt outside of the challenge network. Trying to connect to the network hosts outside of the challenge environment will not work.


Challenges

From your host at 10.0.0.1, connect to the remote host at 10.0.0.2 on port 31337.

A great way to do this is to use the nc command (pronounced "netcat"), which allows you to open network connections from the command line. For example, to connect to the remote host at 10.0.0.42 on port 4242, you would run:

nc 10.0.0.42 4242

From your host at 10.0.0.1, connect to the remote host at 10.0.0.2 on port 31337, and send the message: Hello, World!.

As before, you'll want to use the netcat command. You'll notice that netcat will hang (e.g. you will not get a shell prompt back), waiting until the connection is closed. You can, as with most hanging processes, kill the process by pressing Ctrl-C.

In this challenge though, you need to send a message to the remote host. If you type that message into the terminal, nothing will immediately happen. That is because your terminal, by default, buffers the input you type until you press Enter! Press Enter after typing your message, and a single packet containing the entire message will be sent to the remote host.

From your host at 10.0.0.1, connect to the remote host at 10.0.0.2 on port 31337, and then shutdown the connection.

Sometimes the other side of a connection wants to wait for you to finish sending all of your data before it finishes sending data back to you. Imagine a protocol where the client might need to send lots of data, over a long duration, before the server can respond with some final result. In this case, it might not make sense to preestablish how much data will be sent in total as part of the protocol, because the client might not know at the beginning how much data it will need to send. How can we handle this situation?

One option would be to have the client send a single packet at the end that just says "END". But network packets can be complicated, with no guarantees from the network that they won't be split apart or merged together. Or what if you want to be able to send "END" as part of the data?

Netcat is a simple tool, that translates data from standard input to network packets and vice versa to standard output. So how do you shutdown the network connection in this way with netcat? You do the equivalent file operation: you close standard input! In an interactive terminal session, you can do this by pressing Ctrl-D.

Unfortunately, netcat may not actually do this by default. Review the man page for netcat (man nc) to see if there is any way to configure netcat to shutdown the network connection after closing standard input (EOF).

From your host at 10.0.0.1, listen on port 31337 for a connection from the remote host at 10.0.0.2.

Once a connection is established, that connection is bidirectional, meaning that both sides can send and receive data. However, to actually establish the connection, one side must listen for incoming connections, and the other side must connect to that listener. This time, unlike before, you are the listener.

Review the man page for netcat (man nc) to see how to listen for incoming connections.

From your host at 10.0.0.1, connect to some unknown remote host on the 10.0.0.0/24 subnet, on port 31337.

Fortunately, there are only 256 possible hosts on this subnet, so you can just try them all!

One simple tool that you can use to help you with this is ping. If you "ping" a host, and it is up, you will get a response; otherwise, ping will timeout and warn you that it cannot reach the host.

For example, try pinging yourself:

ping 10.0.0.1

This will continuously keep pinging until you press Ctrl-C to stop it.

You can also try pinging a host that you know is down:

timeout 10 ping 10.0.0.2

This will run ping for (up to) 10 seconds, but you should see ping messages indicating that the host is unreachable before the timeout.

As with most commands, you can also run man ping to see the manual page for ping.

Consider this an opportunity to practice your shell scripting skills! You can of course ping each of the 256 hosts manually, but maybe a for loop might be even easier!

for i in $(seq 10); do
  echo $i
done

From your host at 10.0.0.1, connect to some unknown remote host on the 10.0.0.0/16 subnet, on port 31337.

Now our network is starting to get bigger! There are 65,536 possible hosts on this subnet, so finding the remote host manually would really be a real pain. Even a basic for loop processing 10 hosts per second would take over an hour to complete!

We can of course get fancier with our shell scripting (parallelizing, etc.), but for now, let's consider a standard tool designed to help with this kind of task: nmap.

nmap is a powerful network scanning tool that can be used to discover hosts and services on a computer network. You can, for example, scan for which hosts are up (and popular services running on those hosts) on 10.0.0.0/30 with the following command:

nmap 10.0.0.0/30

Within 15 seconds or so, you should see that your host at 10.0.0.1 is up, as expected.

When conducting a network scan, it is important to be aware of the potential impact on the network. Under default settings, nmap tries to be at least somewhat polite and not totally overwhelm a network with tons of packets. Nevertheless, it is still possible to cause network congestion or even trigger security alerts by running a network scan, and so it is important to be aware of the potential impact. As such, you shouldn't run a network scan on a network that you don't own or have permission to scan!

In this network, it's okay to be a little more aggressive, a little more "rude" with our scanning. You'll want to review the man page for nmap (man nmap) to see how you can speed up the scanning process: you're specifically interested in how many packets are being sent per second. Disabling some of the default scans, such as DNS resolution, can also speed up the scanning process. When in doubt, use -v to see a bit more information about what nmap is currently doing.

Monitor traffic from a remote host. Your host is already receiving traffic on port 31337.


HINT: You will likely want to use the Wireshark tool for this. This is installed on the dojo, and you can launch it from the terminal of the 10.0.0.1 client! Make sure to launch it from there: launched from elsewhere (such as a different terminal on the workspace), Wireshark will not be running on the correct host! Wireshark might take a long time to start up. If you're waiting for over a minute, something is wrong...

Monitor slow traffic from a remote host. Your host is already receiving traffic on port 31337.

You have learned to sniff traffic, but knowledge is only the beginning of action. Now it's time to apply this to an actual security scenario. Steal the admin's cookie, and GET the flag!


HINT: You can use your full set of HTTP tools, as you studied in Talking Web, to use the cookie once you steal it! But whatever you do, make sure to do it in the 10.0.0.1 terminal, to make sure you're running on the right host! You can run Wireshark or whatever else you need in the background (as you learned in The Linux Luminarium).

Configure your network interface. The remote host at 10.0.0.2 is trying to communicate with the remote host at 10.0.0.3 on port 31337.

Your host at 10.0.0.1 is receiving traffic on port 31337; block that traffic.

Your host at 10.0.0.1 is receiving traffic on port 31337; block that traffic, but only from the remote host at 10.0.0.3, you must allow traffic from the remote host at 10.0.0.2.

From your host at 10.0.0.1, connect to the remote host at 10.0.0.2 on port 31337. This time, you are currently blocking outbound traffic to port 31337.

The client at 10.0.0.3 is communicating with the server at 10.0.0.2 on port 31337. Deny this service.

The client at 10.0.0.3 is communicating with the server at 10.0.0.2 on port 31337. Deny this service.

This time the server forks a new process for each client connection.

The client at 10.0.0.3 is communicating with the server at 10.0.0.2 on port 31337. Deny this service.

This time the server forks a new process for each client connection, and limits each session to 1 second.

Manually send an Ethernet packet. The packet should have Ether type=0xFFFF. The packet should be sent to the remote host at 10.0.0.2.

Manually send an Internet Protocol packet. The packet should have IP proto=0xFF. The packet should be sent to the remote host at 10.0.0.2.

Manually send a Transmission Control Protocol packet. The packet should have TCP sport=31337, dport=31337, seq=31337, ack=31337, flags=APRSF. The packet should be sent to the remote host at 10.0.0.2.

Manually perform a Transmission Control Protocol handshake. The initial packet should have TCP sport=31337, dport=31337, seq=31337. The handshake should occur with the remote host at 10.0.0.2.

You're now an expert in TCP, which, as you know, is a great protocol for talking through one connection at a time. TCP is stable and reliable, but it is fairly complex. All of that complexity, of course, comes at a performance cost: all the handshakes, ACKs, and so on take time.

As a solution to this, the Internet's inventors came up with UDP: the User Datagram Packet. UDP is a MUCH simpler protocol. Unlike TCP, which tracks a ton of things, UDP headers simply contain the source port, destination port, length, and a packet checksum. Super simple!

There are some trade-offs for this simplicity, though. Without TCP's functionality, UDP lacks the concept of a "connection". Each packet is not intrinsically linked to any packet, and if this sort of link is desired, the network application itself has to do it.

This makes writing UDP servers and clients a bit strange. When using UDP sockets, there is no more s.listen and s.accept for socket s: you simply s.recvfrom to get data (returning the bytes received and the address of the sender, as retrieved from the UDP packet) and s.sendto (taking the bytes to send an the address of the sender). A single server loop can, thus, handle multiple client interactions all comingled together, but this also makes it easy to mix things up in an insecure way.

In this challenge, you'll make your first UDP connection. You can use Python or netcat, but we'd recommend the former, as it'll be more useful in future challenges.

Though we didn't explore this for TCP, in addition to selecting the destination port, both TCP and UDP can set their source port. We'll practice that here --- you can set the source port with s.bind on the socket, exactly how a server does it to set their listening port. Read the source code of /challenge/run to see what source port you'll need!


NOTE: You must set the source port before sending data! Otherwise, Linux will pick a random source port (the default behavior, when bind is not called).

There are two dangers to UDP: first, it is often used in places where people are already cutting corners for performence's sake. Second, it forces the programmer to keep track of sessions explicitly. This combination can cause security issues.

In this challenge, one side of the connection can confuse a non-trusted connection for a trusted connection, and print the flag. Can you trigger this confusion?


NOTE: In this level, the flag will just be printed to the console when you trigger the confusion. We'll work on realistically exfiltrating it later.

There is a fairly wide gap between the features that TCP provides and UDP's barebones nature. Sometimes, developers want some of those features, and end up reimplementing just those that they need on top of UDP. This leads to weird situations, such as the ability to trigger outbound traffic to other servers, with a potential application to Denial of Service amplification.

Rather than leaking the flag directly, this challenge allows you to redirect it to another server. Can you catch it on the other side?


HINT: You'll need to either use a UDP server to actually receive the flag (e.g., python or netcat), or just sniff it off the network with Wireshark when it comes to you, even if you don't have a server listening!

Of course, the previous spoofing worked because you know the source port that the client was using, and were thus able to forge the server's response. This was, in fact, at the core of a very famous vulnerability in the Domain Name System that facilitates the translation of host names like https://pwn.college to the appropriate IP addresses. The vulnerability allowed attackers to forge responses from DNS servers and redirect victims to IP addresses of their choice!

The fix for that vulnerability was to randomize the source port that DNS requests go out from. Likewise, this challenge no longer binds the source port to 31338. Can you still force the response?


HINT: The source port is only set once per socket, whether at bind time or at the first sendto. What do you do when there's a fixed number that you don't know?

Let's up the game a bit: this challenge checks that the response came from the right server! Luckily, UDP is a lot easier to forge than TCP. In TCP, forging a server response requires you to know sequence numbers and a whole bunch of other inconvenient-to-guess information. Not so with UDP!

Go ahead and craft the server response with scapy, as you've done with TCP, and let's see that flag fly!

Manually send an Address Resolution Protocol packet. The packet should inform the remote host that the IP address 10.0.0.42 can be found at the Ethernet address 42:42:42:42:42:42. The packet should be sent to the remote host at 10.0.0.2.

Intercept traffic from a remote host. The remote host at 10.0.0.2 is communicating with the remote host at 10.0.0.3 on port 31337.

Man-in-the-middle traffic from a remote host. The remote host at 10.0.0.2 is communicating with the remote host at 10.0.0.3 on port 31337.


30-Day Scoreboard:

This scoreboard reflects solves for challenges in this module after the module launched in this dojo.

Rank Hacker Badges Score