CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Dashboard
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2025-69197
6.50.01%

Infinite Lives: Replaying TOTP in Pterodactyl Panel

Alon Barad
Alon Barad
Software Engineer

Feb 25, 2026·6 min read·2 visits

PoC Available

Executive Summary (TL;DR)

Pterodactyl Panel < 1.12.0 treated TOTP codes as reusable passwords within their 30-second validity window. Attackers could capture a used token and replay it to gain access.

Pterodactyl Panel, the go-to management solution for game server hosting, suffered from a classic logic flaw in its Two-Factor Authentication implementation. While the system correctly validated that a Time-based One-Time Password (TOTP) was mathematically correct for the current time window, it failed to record that the token had been used. This stateless verification meant the 'One-Time' part of TOTP was merely a suggestion. An attacker with valid credentials and a captured token could replay the authentication request multiple times within the validity window (typically 30-60 seconds), effectively bypassing the intended security controls of 2FA.

The Hook: When "One-Time" is a Suggestion

Pterodactyl Panel is the heavyweight champion of open-source game server management. It manages Docker containers, handles billing integrations, and generally keeps the Minecraft and Rust ecosystems running. Naturally, it guards these gates with Two-Factor Authentication (2FA), specifically TOTP (Time-based One-Time Passwords). You know the drill: scan the QR code, get the 6-digit code from Authy or Google Authenticator, and you're in.

But here is the dirty little secret about TOTP libraries: most of them are just fancy calculators. They take a secret key and the current time, do some HMAC-SHA1 voodoo, and spit out a 6-digit number. Crucially, the library often doesn't know if that number was used five seconds ago by the legitimate user. It just knows the number is technically correct for the current time slice.

CVE-2025-69197 is the story of what happens when a developer trusts the calculator without checking the receipt. For versions of Pterodactyl prior to 1.12.0, the system verified the math but ignored the history. This created a race condition where a token was valid for its entire 30-60 second lifespan, regardless of how many times it was submitted. It wasn't a One-Time Password; it was a "Valid-For-A-Minute Password."

The Flaw: Stateless Verification

To understand the bug, you have to understand the laziness inherent in stateless authentication. In a proper TOTP implementation, the server must store the timestamp (or the counter index) of the last successful login. When a new code comes in, the server checks two things:

  1. Is the code mathematically valid for the current time?
  2. Is the current time newer than the last time this user logged in?

Pterodactyl missed step 2.

The LoginCheckpointController utilized the PragmaRX\Google2FA library's verifyKey method. This method is stateless. It simply returns true or false based on the hash generation. Because Pterodactyl didn't update the user's record to say "Hey, we just used the token for time slice X," the server had no memory of the event.

This is akin to a movie theater ticket taker who looks at the date on your ticket but forgets to tear the stub. You can walk in, hand the ticket to your friend, and they can walk in too—as long as you both do it before the movie starts. In crypto terms, this is a replay attack enabled by a lack of replay protection.

The Code: Adding Memory to the Machine

Let's look at the smoking gun. The fix, applied in commit 032bf076d92bb2f929fa69c1bac1b89f26b8badf, explicitly changes the verification logic from a simple check to a stateful comparison.

The Vulnerable Logic: Previously, the controller likely did something resembling this (pseudocode):

// The "I verified the math" approach
$valid = $google2fa->verifyKey($user->totp_secret, $input_code);
if ($valid) {
    Auth::login($user);
}

The Fixed Logic: The patch introduces a new column to the database, totp_authenticated_at. This stores the timestamp of the last successful TOTP exchange. The code switches to verifyKeyNewer, which enforces time progression.

// The "I verified the math AND the timeline" approach
$timestamp = $google2fa->verifyKeyNewer(
    $user->totp_secret,
    $input_code,
    $user->totp_authenticated_at // The memory of the last login
);
 
if ($timestamp !== false) {
    // Update the memory so this token can't be used again
    $user->update(['totp_authenticated_at' => $timestamp]);
    Auth::login($user);
}

By passing $user->totp_authenticated_at as the third argument, the library checks if the token provided belongs to a time window after the stored timestamp. If you try to replay a token from the same 30-second window, the library sees that the window index hasn't increased and rejects it. Simple, elegant, and previously missing.

The Exploit: Streaming the Keys to the Kingdom

How does a hacker weaponize this? You still need the username and password, so this isn't a magic key. However, it completely neuters the protection of 2FA in specific, common scenarios.

Scenario: The Streamer / Shoulder Surfer Imagine a server admin streaming their desktop on Discord or Twitch, or perhaps screen-sharing with a "support" agent (social engineering).

  1. Recon: The attacker already possesses the victim's credentials (dumped database, keylogger, or phishing).
  2. The Event: The admin logs in. The attacker watches the stream. The admin opens their Authy app or the dashboard prompts for the code.
  3. The Intercept: The attacker sees the 6-digit code 123456.
  4. The Race: The admin submits the code and logs in. The attacker, watching in real-time, immediately hits "Login" on their own machine using the credentials and the same 123456 code.

Because of the vulnerability, the server accepts the attacker's request as long as it falls within the verification window (usually configured to allow for some clock drift, often +/- 1 window, meaning up to 60-90 seconds).

The Impact: Admin Access & RCE

So, the attacker is in. Now what? Pterodactyl isn't just a dashboard; it's a command center for Docker containers.

If the compromised account has Administrator privileges, the impact is effectively Remote Code Execution (RCE). An admin can:

  1. Modify Startup Commands: Change the launch parameters of a game server to execute arbitrary shell commands.
  2. File Access: Upload malicious binaries or scripts via the file manager.
  3. Database Exfiltration: Access the connection strings for backend databases.

Even for non-admin users, this allows an attacker to hijack game servers, delete backups, or modify configurations to grief players. The vulnerability transforms a "compromised password" scenario—which 2FA is specifically designed to mitigate—back into a full account takeover.

The Fix: Burning the Bridges

The remediation is straightforward: Update to version 1.12.0 immediately. This version includes the database migration and code changes required to enforce stateful TOTP verification.

For those who enjoy living dangerously and cannot update, you could technically tighten the auth.2fa.window setting in config/pterodactyl.php to 0. This reduces the drift window to strict time matching, making the replay window smaller, but it doesn't fix the underlying logic flaw and might lock out users with slightly desynchronized clocks.

Realistically, just patch the panel. This is logic, not magic. If you don't track state, you don't have security.

Official Patches

PterodactylOfficial Release v1.12.0

Fix Analysis (1)

Technical Appendix

CVSS Score
6.5/ 10
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N
EPSS Probability
0.01%
Top 98% most exploited

Affected Systems

Pterodactyl Panel < 1.12.0

Affected Versions Detail

Product
Affected Versions
Fixed Version
Pterodactyl Panel
Pterodactyl
< 1.12.01.12.0
AttributeDetail
CWE IDCWE-294
Attack VectorNetwork
CVSS6.5 (Medium)
Exploit MaturityPoC / Theoretical
Requires CredsYes
Automated ExploitUnlikely

MITRE ATT&CK Mapping

T1078Valid Accounts
Initial Access
T1190Exploit Public-Facing Application
Initial Access
T1563Remote Service Session Hijacking
Lateral Movement
CWE-294
Authentication Bypass by Capture-replay

Authentication Bypass by Capture-replay

Known Exploits & Detection

Manual AnalysisManual replay of captured token within validity window.

Vulnerability Timeline

Fix Committed by Danish Everitt
2025-12-30
CVE Published / GHSA Advisory Released
2026-01-06
Advisory updated with technical impact
2026-01-08

References & Sources

  • [1]GHSA Advisory
  • [2]Fix Commit

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.