Research Week

Every now and then we do a research week at X41, where each team member is free to look into interesting topics. Sometimes this results in advisories such as last year, when we found a DoS vulnerability in the Chilkat ASN.1 decoder or a cross-site scripting vulnerability in Antragsgrün. In other weeks this results in small tools or notes that are not interesting standalone to present, but might still be useful to others. In this blog post we will present some of the work achieved in the last research week.

Open sourcing EncroCam

Author: Luc

If the alarm system triggers at night or you find a broken lock in the morning, do you assume your server rack was compromised and you need to rebuild your infrastructure from a known safe state?

We wanted a security camera to see if our infrastructure got messed with, but we couldn’t find one where the feed is streamed off-site without the vendor being able to watch along. A webcam whose feed is encrypted before uploading? Sounds like a fun little project!

The first version encrypted every frame as an ASCII-armored PGP message. This is inefficient, but less than I thought! The storage space overhead is not actually much beyond Base64-encoding the raw stream. Slightly worse is the encryption time, but the worst issue was PGP decryption. Watching back was an exercise of patience.

There were positive aspects to this approach:

  • Very simple to implement.
  • You can have arbitrary cuts in the file and the PGP decrypter will automatically seek to the next “BEGIN PGP MESSAGE” segment. This corruption recovery mechanism is very robust.
  • We use PGP internally anyway. We can piggyback on existing public key infrastructure.

Still, we wanted to make it better while keeping these benefits. EncroCam version 2’s video file format starts with a long magic sequence, then a block of PGP data (containing the symmetric (fast) video data key), and then any number of video blocks. If the system were to crash midway, on next startup it will write the magic sequence again. The decrypter will find this file restart marker and continue to decrypt all data in the file seamlessly.

There is much more I can go into: authenticating the video stream, preventing an attacker from retrieving that key from storage, or how it can livestream data to any cheap FTP drive vendor (there is no EncroCam server software needed). Please see the project’s README file for all design information and known attacks.

It has been running for several years without issue now. At first, it would hang every few months. Hard to reproduce, but we found it: Python’s FTP library calls into TLS which sometimes blocks forever on read(). Luckily, the FTP client constructor supports a timeout parameter and passes this to the TLS code. It will abort when it hangs but not trigger when there is a large data chunk being uploaded. Perfect!

Now that it had been battle tested, it was high time to release! This is one of the things I worked on during this research week.

For your own privacy-friendly security camera, you will need:

  1. A Raspberry Pi 4 (or similar)
  2. A webcam (e.g. Pi Camera, or just any USB camera)
  3. If you want to avoid wearing out the Pi’s SD Card with video recordings, a hard drive for the local copy
  4. If you want an off-site copy, a storage vendor that allows SFTP access

Install your OS (e.g., Ubuntu 24.04 LTS), clone the repository, run the installer, and optionally configure your FTP credentials. That’s it!

More detailed hardware requirements and installation instructions are available in the project README file.

We would love to hear your feedback, either by email or on GitHub.

NTP Fingerprinting

Author: Eric

The NTP protocol allows to synchronize clocks via network to make sure that the time between the different devices does not drift too much. There are several implementations out there such as Chrony, ntpd, openntpd or ntpsec. Auditing these in depth is too much work for a week, but some code was read.

The various NTP implementations differ in simple details, for example which versions they accept as valid in incoming packets:

if (info->version < NTP_MIN_COMPAT_VERSION || info->version > NTP_MAX_COMPAT_VERSION) {
  DEBUG_LOG("NTP packet has invalid version %d", info->version);
  return 0;
}

Other differences are in handling of extensions, invalid lengths or otherwise corrupted packets. This of course allows to create a fingerprinting tool, which we did and released on GitHub.