rtmp

package
v0.804.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 9, 2026 License: AGPL-3.0 Imports: 4 Imported by: 0

Documentation

Overview

Package rtmp decodes RTMP (Real-Time Messaging Protocol) wire frames. Originally developed by Macromedia/Adobe for Flash; still the dominant live-streaming ingest protocol on TCP/1935 (default). Widely used by OBS Studio, Twitch, YouTube Live, Facebook Live, Nginx-RTMP, Wowza, SRS (Simple Realtime Server).

Operationally, RTMP is a **high-value live-streaming ingest target** — stream keys transmitted in cleartext. Stream keys are effectively authentication tokens for live-streaming platforms; capturing one lets an attacker broadcast arbitrary content to the victim's channel.

The wire format leaks:

  • **Handshake version via C0/S0** — first byte is the RTMP version: 0x03 = plaintext RTMP; 0x06 = RTMPE (encrypted RTMP using Diffie-Hellman). The C1/S1 blocks that follow contain a 4-byte timestamp plus 1528 bytes of random data.

  • **Application name and server URL via AMF0 "connect" command (message type 20)** — the first AMF0 command sent by any RTMP client is "connect"; its argument object contains "app" (the application name, e.g. "live"), "tcUrl" (the full RTMP URL, e.g. "rtmp://server/live"), "flashVer" (client version string), "swfUrl", "pageUrl". The tcUrl often contains auth tokens or stream keys embedded as query parameters.

  • **Stream keys via "publish" command** — when a client starts publishing, it sends an AMF0 Command Message (type 20) with command name "publish"; the second string argument is the stream name / stream key, transmitted in cleartext. Stream keys are auth tokens for Twitch / YouTube Live / Facebook Live / Wowza / Nginx-RTMP.

  • **Stream name via "play" command** — consumer clients send "play" with the stream name as the second argument.

  • **RTMPE (version 0x06) uses Diffie-Hellman key exchange** but has known implementation weaknesses. Standard RTMP (version 0x03) is entirely cleartext.

  • **Protocol control message types 1-6** — Set Chunk Size (1), Abort (2), Acknowledgement (3), User Control (4), Window Acknowledgement Size (5), Set Peer Bandwidth (6). These reveal session-layer parameters.

  • **Audio (type 8) and Video (type 9) message streams** — identified by message type; payload not decoded.

Wrap-vs-native judgement

Native. The RTMP specification is publicly available (Adobe
RTMP Specification 1.0, December 2012). The chunk format
is a deterministic binary layout with no crypto at the
parse layer for standard RTMP 0x03. RTMPE (0x06) is
detected but not decrypted. AMF0 command parsing is
limited to extracting the command name string and
best-effort extraction of the "app" and "tcUrl" fields
from the following AMF0 object.

What this package covers

  • **C0+C1 / S0+S1 handshake detection** — leading byte is the RTMP version (0x03 or 0x06), followed by 1536 bytes. Surfaces `is_handshake`, `handshake_version`, `is_encrypted`.

  • **RTMP chunk header walker** — basic_header (1-3 bytes): fmt (2 bits) + cs_id (6 bits / 1-byte extended / 2-byte LE extended). Message header per fmt: fmt 0 (11 bytes: timestamp 3 BE + message_length 3 BE + message_type_id 1 + message_stream_id 4 LE); fmt 1 (7 bytes: timestamp_delta 3

  • message_length 3 + message_type_id 1); fmt 2 (3 bytes: timestamp_delta 3); fmt 3 (0 bytes). Extended timestamp (4 BE) if timestamp/delta == 0xFFFFFF.

  • **17-entry message type name table**: Set Chunk Size (1) / Abort (2) / Acknowledgement (3) / User Control (4) / Window Acknowledgement Size (5) / Set Peer Bandwidth (6) / Audio (8) / Video (9) / Data AMF3 (15) / Shared Object AMF3 (17) / Data AMF0 (18) / Shared Object AMF0 (19) / Command AMF0 (20) / Aggregate (22).

  • **AMF0 Command Message walker (type 20)** — extracts the command name from the first AMF0 string marker (0x02 + 2-byte BE length + data). Key commands: connect / createStream / play / publish / deleteStream / FCPublish / releaseStream / onStatus / _result / _error.

  • **"connect" command argument extraction** — best-effort scan for AMF0 object keys "app" and "tcUrl" following the command name; surfaces `app_name` and `tc_url`.

  • **Classification booleans**: `is_connect`, `is_play`, `is_publish`, `is_audio`, `is_video`, `is_control_message`.

  • **User Control Message event type decoder (message type 4)** — 6-entry event type name table: StreamBegin (0) / StreamEOF (1) / StreamDry (2) / SetBufferLength (3) / StreamIsRecorded (4) / PingRequest (6) / PingResponse (7).

What this package does NOT cover (deliberately out of scope)

  • **RTMPE decryption** — RTMPE (version 0x06) uses a Diffie-Hellman key exchange; the decoder detects it but does NOT decrypt. The DH exchange details are complex and intentionally out of scope.
  • **Full AMF0 / AMF3 parser** — only the command name string and best-effort "app"/"tcUrl" extraction are implemented. Full AMF0 value types (numbers, booleans, objects, arrays, dates) and AMF3 are not parsed.
  • **Multi-chunk message reassembly** — large messages span multiple chunks; the decoder parses the first chunk header only.
  • **RTMPS (TLS-wrapped RTMP, TCP/443)** — handle TLS strip first.
  • **RTMPT (HTTP-tunneled RTMP)** — handle HTTP layer separately.
  • **Audio/Video payload decoding** — H.264/AAC/FLV codec payloads are out of scope.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Result

type Result struct {
	TotalBytes int `json:"total_bytes"`

	// Handshake fields (C0+C1 or S0+S1)
	IsHandshake      bool `json:"is_handshake"`
	HandshakeVersion int  `json:"handshake_version,omitempty"`
	IsEncrypted      bool `json:"is_encrypted"`

	// Chunk header fields
	Fmt             int    `json:"fmt,omitempty"`
	ChunkStreamID   int    `json:"cs_id,omitempty"`
	Timestamp       int    `json:"timestamp,omitempty"`
	MessageLength   int    `json:"message_length,omitempty"`
	MessageTypeID   int    `json:"message_type_id,omitempty"`
	MessageTypeName string `json:"message_type_name,omitempty"`
	MessageStreamID int    `json:"message_stream_id,omitempty"`

	// AMF0 Command Message (type 20)
	CommandName string `json:"command_name,omitempty"`

	// "connect" command extracted fields
	AppName  string `json:"app_name,omitempty"`
	TcURL    string `json:"tc_url,omitempty"`
	FlashVer string `json:"flash_ver,omitempty"`

	// Classification booleans
	IsConnect        bool `json:"is_connect"`
	IsPlay           bool `json:"is_play"`
	IsPublish        bool `json:"is_publish"`
	IsAudio          bool `json:"is_audio"`
	IsVideo          bool `json:"is_video"`
	IsControlMessage bool `json:"is_control_message"`

	// User Control Message event (type 4)
	UserControlEventType int    `json:"user_control_event_type,omitempty"`
	UserControlEventName string `json:"user_control_event_name,omitempty"`
}

Result is the structured decode of an RTMP wire-protocol frame.

func Decode

func Decode(hexStr string) (*Result, error)

Decode parses an RTMP wire-protocol frame from a hex string. It auto-discriminates between handshake blocks (C0+C1 / S0+S1) and post-handshake chunk headers by inspecting the buffer length and leading byte:

  • Exactly 1537 bytes with leading 0x03 or 0x06 → handshake.
  • All other inputs → RTMP chunk header.

The discrimination uses the exact 1537-byte size to avoid the fmt=0 / cs_id=3 ambiguity (chunk basic-header byte 0x03 is also a valid first byte for a chunk with fmt=0 and cs_id=3).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL