A canary to detect network intruders

A canary to detect network intruders

Every now and then, security experts discover new vulnerabilities that affect the security of many computer systems. The month of December 2021 was the occasion of a good example, with the flaw in the log4j library which allows the remote execution of arbitrary code on vulnerable systems. Noting that it seems impossible to definitively guard against this type of problem, we wondered whether the Yoctopuce modules could help detect intrusions before it is too late...


The idea is to make a canary, a small piece of software that can detect the signs of a network intrusion. Just as the canaries used in mines in the 19th century allowed miners to flee away from dangerous gases, the goal of our canary is to detect ongoing intrusions to mitigate damage and protect critical data while it is still possible.

Principle

When an intruder manages to enter a private network, he has to search the network for computers with any kind of entry point that would allow him to strengthen his grip, and to gain access to valuable data. These entry points are the various network services opened by each machine, which potentially conceal exploitable vulnerabilities.

The exploratory phase can be conducted very discreetly, but it is not without risk for the intruder, because you can't get through a door without opening it. The intruder will have to open a connection to each service that he tries to attack. In itself, it is a very innocuous operation: anyone can try to connect to a network service, and it is not forbidden to make a mistake. So the exploration can remain silent.

But the intruder has a handicap on legitimate users: he does not know where the real services are. So if we add bogus services to various machines on the network, or if we add machines that look like servers but are not really servers, the intruder risks trying to connect to a service that no legitimate user would have ever used. And that's where the canary awaits him.

Using canaries to detect network intrusions
Using canaries to detect network intrusions



A keep-it-stupid-simple canary

A canary can therefore be implemented as a software that just opens a certain number of network ports liable to be explored by intruders, and to launch an alarm if someone connects to them.

To open the network ports, you must create as many network sockets, attach them to the desired ports with the bind function and listen to them with the listen function. As soon as an incoming connection is present, it is accepted to retrieve the source IP address, then the alarm is triggered. It's a bit of socket-based plumbing, but it's not that complicated. You will find the few dozen lines of code that perform these operations in this file.

To raise the alarm, the Yocto-MaxiBuzzer is ideal: it can be connected by USB to any machine, and it does not risk going unnoticed given its high sound level. The following few lines are used to launch a siren, which will continue to howl autonomously indefinitely until someone does something about to and pull the plug of the Yoctopuce device:

buz->resetPlaySeq();
buz->addVolMoveToPlaySeq(50, 0);        // Set volume to 50%
buz->addFreqMoveToPlaySeq(500, 0);      // Start at 500 Hz
buz->addFreqMoveToPlaySeq(5000, 1000);  // Ramp up to 5 kHz in 1000 ms
buz->addFreqMoveToPlaySeq(500, 1000);   // Ramp down to 500 Hz in 1000 ms
buz->startPlaySeq();                    // Run in loop


We could have made the network code of this canary even shorter using another programming language such as Python or node.js, but we chose to do it in C/C++ in order to obtain a stand-alone executable, without dependency on an interpreter, and that does not provide any information to the intruder regarding consequences of the intrusion detection. You will find the full source code of this tool, combining C++ and C files, on GitHub. You can compile it on Windows, Linux and macOS, and it includes support for command line arguments to easily install it as a service, in order to start automatically at boot time on Windows or Linux. It can even be compiled and used on a RaspberryPi.

This code is intentionally very basic. You can find more sophisticated software of this type that tries to respond to the intruder, in order to get him to continue his intrusion further on the bogus server. But this is not without risk either, because the added complexity can turn out to be a source of vulnerabilities that the intruder could exploit, to hide the intrusion or even to penetrate the system. This is not anymore a true canary, but more like a honeypot.

Likewise, most of the canary solutions we've found on the web use a cloud-based system to report intrusions, rather than an audible alarm. A cloud-based solution has the advantage of allowing remote notification, but the added complexity again offers an opportunity to block the alarm: if the intruder has entered the network thanks to a vulnerability on the router, there is a significant risk that he would also be able to use the router to prevent the target machine from making any outside connection, effectively preventing it from raising an alarm.

This is why we suggest below another solution to implement remote notificatoins, quite more robust.

Transmitting canary alarms

To facilitate the monitoring of canaries, we would like to connect them to a central controller. But this control center must at all costs remain out of the reach of the intruder. The best way to preserve the integrity of this hub is to make sure that it has no direct network connection: no network cable, no WiFi interface, no bluetooth, nothing. This is the only way to ensure that it will be invisible and unassailable from another machine.

The transmission of alarms from the canaries must therefore use another channel. A simple and robust solution is to use an RS485 serial bus. No need for a special infrastructure, just replace the Yocto-MaxiBuzzer on each canary by a Yocto-RS485-V2, and daisy-chain them using a three-wire cable up to the controller. A dedicated communication channel is thus obtained, which does not expose any service of the controller to a possible intruder.

Army of canaries linked by an RS485 bus
Army of canaries linked by an RS485 bus


This new "soldier" version of the canary works like the previous one, but rather than sounding their own siren, canaries will simply send an alarm message. During initialization, we load the Yocto-RS485 logical name, in order to use it to uniquely identify this canary to the controller:

// Use the Yocto-RS485 logical name as line header for reports
string linehdr = rs485->get_module()->get_logicalName()+";";


In case of intrusion detection, a message is sent to the controller indicating the machine from which the connection was initiated, and the port which was targeted.

string line = linehdr + "alarm;" + string(sourceIP)
              + ";" + std::to_string(targetPort);
rs485->writeLine(line);


Note that we have chosen here a textual protocol, so the Yocto-RS485-V2 must be configured for a Line-based ASCII protocol. Also make sure that all Yocto-RS485-V2 use the same baud rate and encoding, e.g. 19200, 8N1.

As a bonus, we have added to this new version of the canary the ability to measure the amount of data read and written to disks per period of 15 seconds. This information is transmitted for informational purposes four times per minute to the controller. Not only does this allow the controller to make sure the canary is still alive, but it can detect abnormal activity in case an intruder starts siphoning or encrypting data from the machine.

The full source code for this Soldier Canary is also available on GitHub. Windows, Linux, and macOS specific code for measuring the amount of data read from and written to disks can be found in the OS-specific files winStartup.cpp, linStartup.c and osxStartup.c. If necessary, take care to check that the disks taken into account by this code include your data disk, not just the system disk :-)

The canary command center

The program that will centralize canary alarms and act accordingly to protect critical data will also use a Yocto-RS485-V2 to listen to messages on the bus. An efficient way to do this is to configure a value change callback on the serialPort function, and then use the readMessages method to read all the messages received since the last check. As the controller will only run on a dedicated machine isolated from the network, there is no requirement to use C/C++. You can use for example C#, which makes it easier to create a nice interface giving a good overview of the state of canaries. The code that receives the alarms can therefore look like this:

List<string> messages = p.readMessages("", 1);

for (int i = 0; i < messages.Count; i++) {
    string[] fields = messages[i].Split(';');
    if (fields.Length >= 2 && fields[1] == "alarm") {
        // If a Yocto-MaxiBuzzer is connected, sound a loud alarm
        YBuzzer buz = YBuzzer.FirstBuzzer();
        if (!(buz is null)) {
            buz.resetPlaySeq();
            buz.addVolMoveToPlaySeq(50, 0);        // Set volume to 50%
            buz.addFreqMoveToPlaySeq(500, 0);      // Start at 500 Hz
            buz.addFreqMoveToPlaySeq(5000, 1000);  // Ramp up to 5 kHz
            buz.addFreqMoveToPlaySeq(500, 1000);   // Ramp down to 500
            buz.startPlaySeq();                    // Run in loop
        }
        // Add more tricks here...
    }
}


Now that the alarms are centralized on a single controller, you can afford the luxury of adding other alert actions. For example, you can connect a YoctoHub-GSM-4G with an USB cable to your canary controller, and use it to send SMS alerts:

        // If a Yocto-Hub-GSM is connected, send an SMS
        YMessageBox mbox = YMessageBox.FirstMessageBox();
        if (!(mbox is null)) {
            // Send an SMS, but not more than once every 15 minutes
            if (YAPI.GetTickCount() - lastSMSsent > 15 * 60 * 1000) {
                // FIXME: Put your mobile phone number below
                mbox.sendTextMessage("+xxxxxxxxxx", messages[i]);
                lastSMSsent = YAPI.GetTickCount();
            }
            // One more trick here, see below...
        }


You can also use the YoctoHub-GSM-4G to contact an external HTTP server on the web to propagate the alarm. This call will be invisible to the intruder, since it goes through an independent 4G line. It does not expose the canary controller either, because the request is sent by the YoctoHub itself and not by the canary controller, which still does not have any true network connection.

            // Generate an HTTP callback over the GSM link
            string funcid = mbox.get_serialNumber() + ".network"
            YNetwork net = YNetwork.FindNetwork(funcid);
            if (net.isOnline() &&
                net.get_readiness() == YNetwork.READINESS_WWW_OK) {
                string args = "time=" + YAPI.GetTickCount() +
                              "&csv=" + messages[i];
                net.set_callbackUrl("http://xxxxxx/xxxxx/xxxxx.php?" + args);
                net.triggerCallback();
            }


There are still many other options possible thanks to Yoctopuce modules. For example, you can ask a user to clear alarms within 3 minutes on the controller, and if the acknowledgment does not come, automatically take drastic protective action by removing power to all network switches using of a Yocto-MaxiPowerRelay. It's quite disruptive, but it will prevent an intruder from taking advantage of the night time to progress in his exploration of your network, and it will allow you to think calmly about the measures to be taken. Plus, even with the network down, your canary controller will continue to receive disk access metrics, which will help you spot machines where malware may still be snooping around your disks.

We have put you on GitHub a sample canary controller code based on these principles. We derived it from Yocto-Visualization to allow the display of disk access statistics in graphical form, and we simply added the management of alarms arriving on the serialPort as described above (see the sensorsManager.cs file). But be careful, do not use this code as is, it is just a quick'n dirty example provided in support of this blog post, and does certainly not have the robustness of a production tool.

Conclusion

We have shown how a basic piece of software and a small USB module under $100 can turn into a powerful intrusion detection tool. Be careful, however, to carry out the commissioning by a qualified person, who can adapt these small sample programs to your environment, in particular adapt the list of ports to be monitored, then install the tool where it is the most appropriate and if necessary complete the tool. For example, our code only intercepts TCP ports, but it might be desirable to add similar processing for UDP ports.

As for the centralized controller, it's up to you to turn it into something that suits your needs. Providing you with a real turnkey solution goes beyond what we can offer you in a blog post...

Add a comment No comment yet Back to blog












Yoctopuce, get your stuff connected.