Security Release — Pterodactyl Daemon@0.4.6

in security, daemon

Today I am releasing Pterodactyl Daemon v0.4.6 to address a DoS attack vector in the Daemon as well as address a race condition in the code causing some headaches for large hosting providers. This blog post will quickly cover what was discovered, how it affects you, and what was done to address it.

I was made aware of a potential issue in the Daemon earlier this week when a member of our support team and developer at a hosting company using Pterodactyl reported unexplainable CPU lockups on one of their Daemons. They were able to track down the source of the issue to a specific block of code in the logic handling streams from Docker containers.

@@ -217,18 +167,28 @@ class Docker {
 
         this.container.attach({ stream: true, stdin: true, stdout: true, stderr: true }, (err, stream) => {
             if (err) return next(err);
+
             this.stream = stream;
             this.stream.setEncoding('utf8');
 
+            const Throttled = new ThrottledReader(this.stream, {
+                rate: Config.get('docker.stream_throttle_bps', 512),
+                cooldownInterval: 250,
+            });
+
             // Sends data once EOL is reached.
-            Carrier.carry(this.stream, data => {
+            Carrier.carry(Throttled, data => {
                 this.server.output(data);
             });
 
-            this.stream.on('end', () => {
-                this.stream = undefined;
-                this.server.streamClosed();
-            });
+            this.stream
+                .on('end', () => {
+                    this.stream = undefined;
+                    this.server.streamClosed();
+                })
+                .on('error', streamError => {
+                    this.server.log.error(streamError);
+                });

In the git-diff above you can see the changes that were made to the streaming code that is being used to quickly send output back to the client in the web console. What is important to note here is that prior to this release, I had written the code to simply take the stream returned by dockerode and pass it to carrier. What carrier does is wait for an EOL character — \n — before passing the accumulated data off to the Server.output() function, which would then handle sending that data to the client.

In almost all cases this logic works perfectly fine. What was discovered however was that a malicious use could adjust their server output and cause a massive amount of data to flow through that stream. When the Daemon encountered this no throttling occurred and we attempted to quickly plow head first through the data. This behavior would cause the Daemon to quickly increase in CPU usage and cause lockups and poor user expierence. To address this issue I implemented the throttled-reader package which allows the output from the stream to be throttled down to — in this case — 512 bits every 0.25 seconds (2 Kb/s). This throttling prevents carrier from becoming overloaded, and prevents the Daemon from encountering CPU lockups. While the browser console will still be flooded by the output even after the server stops due to a queued response, the Daemon will continue to operate without CPU impacts, and refreshing the browser page will clear the backlog.

I would like to thank Trixter#0001 who quickly reported this issue and his corresponding findings which lead to this fix. I would also like to thank Frank#6617 who reported a seperate logging issue and subsequently helped me to address a race condition when the Daemon was booting. This release was very much unexpected, especially on the heels of the next big release for Pterodactyl: v0.7.0 which is slated for release in the coming weeks. However, due to these issues and the potential impacts on our users I felt it was important to get this release out there to keep things moving smoothly while we all wait for the next stable release.

As always, if you have any questions, comments, or concerns regarding this release or any aspect of this software please don't hesitate to get in contact on our Discord.

Comments