Skip to content

GHSA-WXRW-GVG8-FQJP: Verified Repro With Script Download

GHSA-WXRW-GVG8-FQJP: Sliver has DNS C2 OTP Bypass that Allows Unauthenticated Session Flooding and Denial of Service

GHSA-WXRW-GVG8-FQJP is verified against the affected target. Vulnerability class: DoS. This security reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00088.

REPRO-2026-00088 DoS Feb 13, 2026 .txt
Reproduced in
13m 22s
Tool calls
242
01 · Overview

What Is GHSA-WXRW-GVG8-FQJP?

GHSA-WXRW-GVG8-FQJP describes a DNS C2 OTP bypass in Sliver that allows unauthenticated session flooding and denial of service. Pruva reproduced it (reproduction REPRO-2026-00088).

How to Reproduce GHSA-WXRW-GVG8-FQJP

$ pruva-verify REPRO-2026-00088
or curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00088/artifacts/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh
Run in a VM or disposable container. This exploits a real vulnerability.
06 · Proof of Reproduction

Proof of Reproduction for GHSA-WXRW-GVG8-FQJP

Reproduced by Pruva's autonomous agents — 242 tool calls over 13 min. Full root-cause analysis and the complete transcript are below.

How the agent worked 292 events · 242 tool calls · 13 min
13 minDuration
242Tool calls
46Reasoning steps
292Events
Agent activity over 13 min
Support
7
Repro
238
Variant
43
0:0013:22

Root Cause and Exploit Chain for GHSA-WXRW-GVG8-FQJP

Summary

The Sliver DNS C2 listener in the 1d50db698 (2023) code path accepts unauthenticated DNSMessageType_TOTP bootstrap requests and allocates a new DNSSession for each request even when EnforceOTP is enabled. The TOTP message is treated as a “hello” packet that bypasses any OTP validation, so an unauthenticated remote actor can repeatedly send a tiny protobuf payload and force session allocations without cleanup.

Impact

  • Package/component affected: Sliver server DNS C2 listener (server/c2/dns.go).
  • Affected versions: At least commit 1d50db6982880a1e538afac2ad8c5f268e62c51a (June 2023), prior to removal of TOTP bootstrap in Feb 2026.
  • Risk level and consequences: High availability risk. A remote unauthenticated actor can flood the DNS listener with minimal TOTP bootstrap messages to create unbounded sessions and memory growth, leading to denial of service.

Root Cause

The DNS handler routes all DNSMessageType_TOTP messages directly to handleHello without checking the EnforceOTP flag or validating the OTP value. In handleHello, the server allocates a DNSSession and stores it in sessions with a newly generated DNS session ID, and there is no cleanup for this bootstrap path. The bug is visible in server/c2/dns.go: handleC2 checks for TOTP and calls handleHello, while handleHello simply stores a new session. A later fix removes TOTP from DNS C2 and adds pending message GC (commit 9b001ab88ea17b7247f2622c13003c4dcabe5bf3).

Reproduction Steps

  1. Run repro/reproduction_steps.sh.
  2. The script clones Sliver, checks out commit 1d50db698, adds placeholder assets required for build tags, and runs a minimal DNS listener using StartDNSListener with EnforceOTP=true. It then sends a single base32-encoded DNSMessageType_TOTP query to the listener.
  3. Expected evidence: the DNS response contains a non-zero session ID and the script prints “received session id …” followed by “Vulnerability reproduced…”.

Evidence

  • Log file: logs/dns_totp_poc.log
  • Key excerpt (example): received session id 196.251.221.137
  • Environment details: Go toolchain go1.20.14 (via GOTOOLCHAIN) with build tags server go_sqlite on linux/arm64.

Recommendations / Next Steps

  • Suggested fix: enforce OTP validation in the TOTP bootstrap handler (reject invalid OTPs when EnforceOTP is enabled) and add lifecycle cleanup for sessions created during bootstrap. Preferably remove unauthenticated TOTP bootstrap entirely, as in the Feb 2026 change.
  • Upgrade guidance: move to a version that removes TOTP bootstrap and includes session/pending message GC.
  • Testing recommendations: add unit/integration tests that send TOTP bootstrap messages with EnforceOTP enabled and assert the server rejects them without creating sessions; add load tests ensuring session tables remain bounded.

Additional Notes

  • Idempotency: repro/reproduction_steps.sh was run twice consecutively and succeeded both times.
  • Limitations: the script demonstrates session allocation for a single request; sustained flooding would exacerbate memory use but is not required to confirm the bug.

GHSA-WXRW-GVG8-FQJP Reproduction Transcript

The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.

Full session Replay every step — scrub the timeline or play it back.

Event 1/40
0:001:37
0:00
session startedgpt-4o · GHSA-wxrw-gvg8-fqjp · 5940efdd
0:05
web search
0:08
0:10
web search
0:22
0:22
0:22
extract_facts
no facts extracted
0:23
0:23
supportrepro
0:35
0:35
0:37
0:39
0:49
$git clone https://github.com/BishopFox/sliver.git repo
9.8s
Cloning into 'repo'...
Updating files:  78% (12187/15574)
Updating files:  79% (12304/15574)
Updating files:  80% (12460/15574)
Updating files:  81% (12615/15574)
Updating files:  82% (12771/15574)
Updating files:  83% (12927/15574)
Updating files:  84% (13083/15574)
Updating files:  85% (13238/15574)
Updating files:  86% (13394/15574)
Updating files:  87% (13550/15574)
Updating files:  88% (13706/15574)
Updating files:  89% (13861/15574)
Updating files:  90% (14017/15574)
Updating files:  91% (14173/15574)
Updating files:  92% (14329/15574)
Updating files:  93% (14484/15574)
Updating files:  94% (14640/15574)
Updating files:  95% (14796/15574)
Updating files:  96% (14952/15574)
Updating files:  97% (15107/15574)
Updating files:  98% (15263/15574)
Updating files:  99% (15419/15574)
Updating files: 100% (15574/15574)
Updating files: 100% (15574/15574), done.
0:52
0:52
$rg -n "EnforceOTP|DNSMessageType_TOTP|handleHello" -S repo/server/c2/dns.go repo/implant/sliver/transports/dnsclient/dnsclient.go repo/protobuf/dnspb/dns.proto
0:54
0:54
$which rg || true
/usr/bin/rg
0:56
$rg -n "EnforceOTP|DNSMessageType_TOTP|handleHello" -S repo/server/c2/dns.go repo/implant/sliver/transports/dnsclient/dnsclient.go repo/protobuf/dnspb/dns.proto
0:57
1:00
$rg -n "EnforceOTP|TOTP|handleHello" -S repo/server/c2/dns.go repo/implant/sliver/transports/dnsclient/dnsclient.go repo/protobuf/dnspb/dns.proto
repo/protobuf/dnspb/dns.proto:7:    reserved 1; // formerly TOTP
1:02
1:04
1:04
$rg -n "OTP|TOTP|bootstrap|Enforce" -S repo/server/c2/dns.go
555:		// Enforce a hard cap on pending INIT messages to prevent unbounded allocation.
1:05
1:07
$rg -n "INIT|handleHello|bootstrap|session" -S repo/server/c2/dns.go
{"cwd":"/data/pruva/runs/5940efdd-1d06-482c-88d0-5a6c3442d047","duration_ms":3,"exit_code":0,"stderr":"","stdout":"24:\tprotobuf and contain a 24-bit \"dns session ID\" (16777216 possible values), and a 8 bit\n28:\t\t1. Implant generates a random DNS Session ID and sends an INIT (Age key exchange)\n29:\t\t2. DNS server validates INIT and allocates session state\n30:\t\t3. Requests with valid DNS session IDs enable the server to respond with CRC32 responses\n31:\t\t4. Implant establishes encrypted session\n64:\tsessionIDBitMask = 0x00ffffff // Bitwise mask to get the dns session ID\n69:\t// Upper bound on unauthenticated init reassembly state. INIT is split across multiple\n73:\t// Pending INIT messages are expected to complete quickly. If they don't, expire them.\n77:\t// INIT carries only key-exchange material and should remain small.\n95:\t\tsessions:     &sync.Map{}, // DNS Session ID -> DNSSession\n110:// DNSSession - Holds DNS session information\n229:\tdnsLog.Debugf(\"[dns] dns session id: %d, msg id: %d completed message\", s.ID, msgID)\n276:// pendingInit tracks the reassembly state for an INIT message (which may arrive\n339:\tsessions     *sync.Map\n476:\t// INIT can be ca… [truncated]
1:08
1:10
1:12
$rg -n "TOTP" -S repo
{"cwd":"/data/pruva/runs/5940efdd-1d06-482c-88d0-5a6c3442d047","duration_ms":56,"exit_code":0,"stderr":"","stdout":"repo/client/credentials/credentials.go:556:  18100 | TOTP (HMAC-SHA1)                                           | One-Time Password\nrepo/client/credentials/EXAMPLES.md:298:|18100|TOTP (HMAC-SHA1)|597056:3600|\nrepo/protobuf/dnspb/dns.proto:7:    reserved 1; // formerly TOTP\nrepo/vendor/firebase.google.com/go/v4/auth/user_mgt.go:69:\tTOTPInfo        *TOTPInfo `json:\"totpInfo,omitempty\"`\nrepo/vendor/firebase.google.com/go/v4/auth/user_mgt.go:73:// TOTPInfo describes a user enrolled second TOTP factor.\nrepo/vendor/firebase.google.com/go/v4/auth/user_mgt.go:74:type TOTPInfo struct{}\nrepo/vendor/firebase.google.com/go/v4/auth/user_mgt.go:81:// TOTPMultiFactorInfo describes a user enrolled in TOTP second factor.\nrepo/vendor/firebase.google.com/go/v4/auth/user_mgt.go:82:type TOTPMultiFactorInfo struct{}\nrepo/vendor/firebase.google.com/go/v4/auth/user_mgt.go:96:\tTOTP                *TOTPMultiFactorInfo\nrepo/vendor/firebase.google.com/go/v4/auth/user_mgt.go:199:\t\tauthFactorInfo.TOTPInfo = (*TOTPInfo)(mfaInfo.TOTP)\nrepo/vendor/firebase.google.com/go/v4/auth/user_m… [truncated]
1:14
$rg -n "dns" -S repo/implant/sliver/transports/dnsclient/dnsclient.go
{"cwd":"/data/pruva/runs/5940efdd-1d06-482c-88d0-5a6c3442d047","duration_ms":2,"exit_code":0,"stderr":"","stdout":"1:package dnsclient\n23:\tDNS domains are limited to 254 characters including '.' so that means\n42:// {{if .Config.IncludeDNS}}\n63:\t\"github.com/bishopfox/sliver/protobuf/dnspb\"\n65:\t\"github.com/miekg/dns\"\n71:\tsessionIDBitMask = 0x00ffffff // Bitwise mask to get the dns session ID\n78:\terrInvalidDNSSessionID = errors.New(\"{{if .Config.Debug}}Invalid dns session id{{end}}\")\n80:\tErrTimeout             = errors.New(\"{{if .Config.Debug}}DNS Timeout{{end}}\")\n81:\tErrClosed              = errors.New(\"dns session closed\")\n85:\tErrInvalidMsg          = errors.New(\"invalid dns message\")\n88:// DNSOptions - c2 specific options\n89:type DNSOptions struct {\n100:// ParseDNSOptions - Parse c2 specific options\n101:func ParseDNSOptions(c2URI *url.URL) *DNSOptions {\n128:\treturn &DNSOptions{\n140:// DNSStartSession - Attempt to establish a connection to the DNS server of 'parent'\n141:func DNSStartSession(parent string, opts *DNSOptions) (*SliverDNSClient, error) {\n143:\tlog.Printf(\"DNS client connecting to '%s' (timeout: %s) ...\", parent, opts.QueryTimeout)… [truncated]
1:17
1:17
1:20
$rg -n "TOTP|OTP" -S repo/server/c2/dns.go
1:23
1:23
$git -C repo log -S "TOTP" -- server/c2/dns.go
{"cwd":"/data/pruva/runs/5940efdd-1d06-482c-88d0-5a6c3442d047","duration_ms":30,"exit_code":0,"stderr":"","stdout":"commit 9b001ab88ea17b7247f2622c13003c4dcabe5bf3\nAuthor: moloch-- <875022+moloch--@users.noreply.github.com>\nDate:   Fri Feb 6 11:40:00 2026 -0800\n\n    Remove totp from dns c2, implement pending msg gc\n\ncommit 1d50db6982880a1e538afac2ad8c5f268e62c51a\nAuthor: moloch-- <875022+moloch--@users.noreply.github.com>\nDate:   Mon Jun 19 12:28:04 2023 -0700\n\n    Unit tests pass\n\ncommit 9ab27d174610db90b108a69acca7741eaf8ef7c2\nAuthor: moloch-- <875022+moloch--@users.noreply.github.com>\nDate:   Sun Jun 18 16:17:05 2023 -0700\n\n    initial refactor, broken tests\n\ncommit 72065cd39eb6e549e2f0b4ee019547bf8e631226\nAuthor: moloch-- <875022+moloch--@users.noreply.github.com>\nDate:   Wed Apr 13 11:29:56 2022 -0500\n\n    Removed totp <-> dns session id\n\ncommit 15ca3871dd3f6f63d02b26f267193cac773be1f8\nAuthor: moloch-- <875022+moloch--@users.noreply.github.com>\nDate:   Sun Oct 31 15:50:13 2021 -0500\n\n    wip on dns c2 updates\n\ncommit 7463298de18f813cf563cdb19afab881fd5eacaa\nAuthor: moloch-- <875022+moloch--@users.noreply.github.com>\nDate:   Sat Oct 30 09:45:19 2… [truncated]
1:26
$git -C repo show 15ca3871dd3f6f63d02b26f267193cac773be1f8 --stat
commit 15ca3871dd3f6f63d02b26f267193cac773be1f8
Author: moloch-- <875022+moloch--@users.noreply.github.com>
Date:   Sun Oct 31 15:50:13 2021 -0500

    wip on dns c2 updates

 implant/sliver/encoders/base32.go                |   5 +-
 implant/sliver/transports/dnsclient/dnsclient.go | 116 ++++++---
1:28
$git -C repo show 15ca3871dd3f6f63d02b26f267193cac773be1f8 -- server/c2/dns.go | head -n 120
{"cwd":"/data/pruva/runs/5940efdd-1d06-482c-88d0-5a6c3442d047","duration_ms":2,"exit_code":0,"stderr":"","stdout":"commit 15ca3871dd3f6f63d02b26f267193cac773be1f8\nAuthor: moloch-- <875022+moloch--@users.noreply.github.com>\nDate:   Sun Oct 31 15:50:13 2021 -0500\n\n    wip on dns c2 updates\n\ndiff --git a/server/c2/dns.go b/server/c2/dns.go\nindex 8f7703cf7..6258fb064 100644\n--- a/server/c2/dns.go\n+++ b/server/c2/dns.go\n@@ -18,22 +18,28 @@ package c2\n \talong with this program.  If not, see <https://www.gnu.org/licenses/>.\n \t------------------------------------------------------------------------\n \n-\tDNS command and control implementation:\n-\n-\t1. Implant sends TOTP encoded message to DNS server, server checks validity\n-\t2. DNS server responds with the \"DNS Session ID\" which is just some random value\n-\t3. Requests with valid DNS session IDs enable the server to respond with CRC32 responses\n+\tI've put a little effort to making the server at least not super easily fingerprintable,\n+\tthough I'm guessing it's also still not super hard to do. The server must receive a valid\n+\tTOTP code before we start returning any non-error records. All requests must be formatt… [truncated]
1:30
$git -C repo checkout 1d50db6982880a1e538afac2ad8c5f268e62c51a
0.7s
Note: switching to '1d50db6982880a1e538afac2ad8c5f268e62c51a'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:
1:33
$rg -n "TOTP|totp|OTP" -S repo/server/c2/dns.go repo/protobuf/dnspb/dns.proto repo/implant/sliver/transports/dnsclient/dnsclient.go
repo/server/c2/dns.go:24:	TOTP code before we start returning any non-error records. All requests must be formatted
repo/server/c2/dns.go:26:	8 bit "message ID." The server only responds to non-TOTP queries with valid dns session IDs
repo/server/c2/dns.go:30:		1. Implant sends TOTP encoded message to DNS server, server checks validity
repo/server/c2/dns.go:80:func StartDNSListener(bindIface string, lport uint16, domains []string, canaries bool, enforceOTP bool) *SliverDNSServer {
repo/server/c2/dns.go:88:		EnforceOTP:   enforceOTP,
repo/server/c2/dns.go:313:	EnforceOTP   bool
repo/server/c2/dns.go:381:	// TOTP Handler can be called without dns session ID
repo/server/c2/dns.go:382:	if msg.Type == dnspb.DNSMessageType_TOTP {
1:37

Artifacts and Evidence for GHSA-WXRW-GVG8-FQJP

Scripts, logs, diffs, and output captured during the reproduction.

No artifacts available

08 · How to Fix

How to Fix GHSA-WXRW-GVG8-FQJP

Coming soon

Step-by-step mitigation and hardening guidance for GHSA-WXRW-GVG8-FQJP — configuration checks, workarounds where no patch exists, and how to verify you're protected — is on the way.

10 · FAQ

FAQ: GHSA-WXRW-GVG8-FQJP

How does the Sliver DNS C2 session-flooding attack work?

An unauthenticated remote actor repeatedly sends tiny protobuf DNSMessageType_TOTP bootstrap packets to the DNS C2 listener. Each one is treated as a valid "hello" that bypasses OTP validation and allocates a new DNSSession with no cleanup, letting the attacker force unbounded session allocation and memory growth on the server, leading to denial of service.

Which Sliver versions are affected?

The vulnerable code path is present since at least commit 1d50db6982880a1e538afac2ad8c5f268e62c51a (June 2023) in server/c2/dns.go, prior to a later fix (commit 9b001ab88ea17b7247f2622c13003c4dcabe5bf3) that removes TOTP from DNS C2 and adds pending-message garbage collection.

How severe is this Sliver DNS C2 issue?

The root-cause analysis describes it as a high availability risk: a remote unauthenticated actor can flood the DNS listener to create unbounded sessions and memory growth, leading to denial of service.

How can I reproduce the Sliver DNS C2 OTP bypass?

Download the verified script from this page and run it in an isolated environment against Sliver at commit 1d50db6982880a1e538afac2ad8c5f268e62c51a. It clones Sliver, checks out that commit, and sends repeated unauthenticated DNSMessageType_TOTP bootstrap packets to the DNS C2 listener to demonstrate uncontrolled session allocation.
11 · References

References for GHSA-WXRW-GVG8-FQJP

Authoritative sources for GHSA-WXRW-GVG8-FQJP — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.