New kid on the block: an NTP server in Rust

Since it is kind of quit here recently, I figured this might be a good time to mention the Pendulum project, more specifically ntpd-rs.

Ntpd-rs is an NTP implementation written in Rust. It’s new and still in development, but the 0.3.2 release seems functional and stable.

Feel free to give it a go if your are adventurous. I’m sure the maintainers would love to hear your feedback.

5 Likes

Rewriting legacy software in Rust is a fad. Though it’s easier to write programs with better memory safety, it guarantees that new programming bugs will be introduced. If one checks out the CVE list for NTP, less than half of the issues are related to memory errors, the only ones that Rust can help about. It’s not insignificant, but the risk of new errors being introduced isn’t insignificant either. I wish the more adventurous luck.

2 Likes

Even one is way too much. Most (if not all) of the memory handling errors are allowing remote code execution.

I think it’s much less than half, maybe 5%. Memory-safety issues impacting NTP servers or clients (the functionality we care about here) seem to be rare. In ntpd I remember only some issues in the Autokey protocol and that was a bad idea for other reasons. If I remember correctly, the other well-known implementations written in C like ntpsec (since they forked from ntp), chrony, openntpd, busybox never had a memory-safety CVE that could be exploited to attack the server or client. Please correct me if I’m wrong.

If they were written in Rust, memory-safety issues would still be security issues. It would just crash instead of allowing the process to read or write to invalid memory. It’s a denial of service, even for little things like reading a single byte past a buffer, which might not have any security impact in C if there is no crash or information leak. Code in Rust still needs to be tested and fuzzed thoroughly to make sure it doesn’t crash on unexpected input.

NTP is one of the simplest protocols used on Internet. A minimal server and client can be written from scratch in a day. I don’t think it’s one of those domains with 60-70% memory-safety issue rates from the studies quoted by Prossimo. Writing a new advanced NTP implementation from scratch just for this reason doesn’t make much sense to me. That’s what told them when they asked me if I was interested in contributing to ntpd-rs.

FWIW, when I wrote rsntp (still running on some servers in the pool), I was interested in improving performance, not security. If there was a demand for an advanced NTP implementation having the security benefits of Rust, I’d rather consider rewriting an existing code base, starting with the small important parts that handle messages from network.

Only buffer overflows (invalid writes to memory) can lead to remote code execution. The impact can be greatly limited by running under a non-privileged user with limited capabilities and disabled system calls (seccomp on Linux). ntpd-rs doesn’t seem to do that yet. I think that means a single bug in Rust or unsafe parts of ntpd-rs or the libraries it depends on could lead to RCE with full root privileges.

2 Likes

Double free and use after free lead to remote code execution as well.

I think only if they cause invalid writing to the memory. Double frees are usually detected by the memory allocator on modern systems. If it’s only reading from the already-freed memory, there couldn’t be an arbitrary code execution unless the process is already capable of some form or execution of its input (not expected on an NTP server or client) and the logic is corrupted, right?

It requires write to the data area that are already freed. Even the second free does it for the attacker. All lead to the corruption of the memory management data structures and that escalates to the total control of the code execution.

I don’t suppose it would be reasonable to separate the time server, management, and client bits into three processes would work. Then stick a packet filtering NAT firewall in front. The server can reply to time requests while potentially having no privileges, ditto for the monitoring/management bit, and the client might only need to be able to manipulate the clock.

If it were possible, someone would’ve done it already and made a splash.

By management you mean remote management over network like the authenticated mode 6 of ntpd? It would still need to communicate with the client and server parts to actually manage them, maybe using a different protocol than the external one. What would be the advantage of having it separate? If the attacker can take control over the management part, they will also control the client and server parts using that protocol.

The mode 6 is the most problematic part of NTP. I think it’s best to disable remote access and just rely on ssh+sudo or similar.

If you want to split the client and server parts into separate processes in order for the server to not have write access to the clock, that is already possible with chrony, rsntp, cfnts (the Cloudflare’s implementation in Rust). They can work as a proxy, mirroring the state of another NTP server which can be configured to be reachable only over loopback and the proxy can run without the CAP_SYS_TIME capability.

1 Like

How do you do that these days? As several (sub-)systems will detect such attempts and close ports.
I had many problems with high-level streaming-real-time data where the TCP/IP stack would stop sending because it though there was an attempt to overflow the server-port.
I have not seen many hacks on Linux systems that are still successful doing this.

It’s far easier to call some stupid employee at a company and tell him he needs to open an attachment and you infect his Windows that way, after you can take over the entire company. Lot easier and seems to make crackers a lot of money these days :rofl:

(BTW, I do not agree with those people, for the record! As they are criminals.)

I’m actually interested in the software since my server seems to be hitting single-core limit of chrony. Setting up multiple chrony servers seems to be more involved than for example, rsntp (which I haven’t tried either).

So I’d like to ask first: Has anyone actually tried it in the pool yet?

Chrony does fork, but you must run it in daemon-mode.

As soon as you use parameters, even debug or print, it will stop forking and run single threading.

d / n / p / q / Q parameters will cause this.

In the code those set: nofork = 1;

Those options have nothing to do with the number of processes that chronyd is running as a server. They just disable the daemon double fork to keep the process attached to the terminal.

If you want to improve the server performance, you have to start chronyd in multiple instances as explained in the FAQ.

I understand it’s not very user friendly to configure that manually. I have this script which can start the instances with all necessary options without any modifications of the config file: Start multiprocess chrony NTP server · GitHub. Maybe you will find it useful. You can modify an existing systemd service to call multichronyd instead of /usr/sbin/chronyd in ExecStart if you set Type=simple.

ntpd-rs doesn’t seem to support multithreaded server operation yet.

2 Likes

Thanks for the explenation what the function does.