GitHub Security Advisory: GHSA-hwqm-qvj9-4jr2
Package: github.com/russellhaering/gosaml2 (GO)
Severity: HIGH (CVSS 0.0)
CWE: CWE-129: Improper Validation of Array Index
Vulnerable versions: <= 0.10.0
Patched in: 0.11.0

Summary: gosaml2 CBC Padding Panic — Unauthenticated Process Crash

Details:
## Summary

The AES-CBC decryption path in `DecryptBytes()` panics on crafted ciphertext whose plaintext is all zero bytes. After decryption, `bytes.TrimRight(data, "\x00")` empties the slice, then `data[len(data)-1]` panics with `index out of range [-1]`. There is no `recover()` in the library. The panic propagates through `ValidateEncodedResponse` and kills the goroutine (or the entire process in non-`net/http` servers). An attacker needs only the SP's public RSA key (published in SAML metadata) to construct the payload — no valid signature is required.

## Affected Version

All versions of `github.com/russellhaering/gosaml2` through latest (`v0.9.0` and HEAD) that support AES-CBC encrypted assertions.

## Vulnerable Code

**`types/encrypted_assertion.go:65-79`** — `DecryptBytes`, AES-CBC branch:

```go
case MethodAES128CBC, MethodAES256CBC, MethodTripleDESCBC:
    if len(data)%k.BlockSize() != 0 {
        return nil, fmt.Errorf("encrypted data is not a multiple of the expected CBC block size %d: actual size %d", k.BlockSize(), len(data))
    }
    nonce, data := data[:k.BlockSize()], data[k.BlockSize():]
    c := cipher.NewCBCDecrypter(k, nonce)
    c.CryptBlocks(data, data)

    // Remove zero bytes
    data = bytes.TrimRight(data, "\x00")      // <-- empties the slice if plaintext is all zeros

    // Calculate index to remove based on padding
    padLength := data[len(data)-1]             // <-- PANIC: index out of range [-1]
    lastGoodIndex := len(data) - int(padLength)
    return data[:lastGoodIndex], nil
```

## Attack Details

| Property | Value |
|---|---|
| **Attack Vector** | Network (unauthenticated HTTP POST to ACS endpoint) |
| **Authentication Required** | None |
| **Attacker Knowledge** | SP's public RSA certificate (published in SAML metadata) |
| **Signature Required** | No — decryption happens before assertion signature validation |
| **Payload Size** | Single HTTP POST (~2 KB) |
| **Repeatability** | Unlimited — attacker can send the payload repeatedly |
| **Affected Configurations** | Any SP with `SPKeyStore` configured (encrypted assertion support) |
| **Trigger Condition** | AES-CBC plaintext that is all `0x00` bytes after decryption |

## Impact

- **Process crash**: In gRPC servers, custom frameworks, CLI tools, and background workers, the unrecovered panic kills the entire OS process immediately.
- **Goroutine crash**: In `net/http` servers, the built-in per-goroutine recovery catches the panic, returning HTTP 500 and logging the full stack trace. The server survives but the request-handling goroutine is terminated abnormally.
- **Denial of service**: The attack is unauthenticated and repeatable. A single crafted HTTP request is sufficient. Automated retries can keep the service down indefinitely.
- **No valid signature needed**: The SAML Response does not need to be signed. On the unsigned-response code path (`decode_response.go:346`), `decryptAssertions()` is called **before** any assertion signature validation.

#