shadow1ng/fscan · error
invalid kafka response length: %d
Error message
invalid kafka response length: %d
What it means
kafkaRecv reads a 4-byte big-endian length prefix and rejects any declared message length below 4. A Kafka response frame must contain at least its own 4-byte correlation-id; anything smaller is a protocol violation or not a Kafka response at all. This protects the parser from garbage data.
Source
Thrown at plugins/services/kafka.go:191
binary.BigEndian.PutUint16(buf[6:8], uint16(apiVersion))
binary.BigEndian.PutUint32(buf[8:12], uint32(corrID))
binary.BigEndian.PutUint16(buf[12:14], uint16(len(clientID)))
copy(buf[14:], clientID)
copy(buf[14+len(clientID):], body)
_, err := conn.Write(buf)
return err
}
func kafkaRecv(conn io.Reader) ([]byte, error) {
// 读取 4 字节长度
lenBuf := make([]byte, 4)
if _, err := io.ReadFull(conn, lenBuf); err != nil {
return nil, err
}
msgLen := int(binary.BigEndian.Uint32(lenBuf))
if msgLen < 4 {
return nil, fmt.Errorf("invalid kafka response length: %d", msgLen)
}
if msgLen > maxKafkaResponseSize {
return nil, fmt.Errorf("kafka response too large: %d", msgLen)
}
// 读取消息体
msg := make([]byte, msgLen)
if _, err := io.ReadFull(conn, msg); err != nil {
return nil, err
}
// 跳过 correlation_id (4B),返回 body
return msg[4:], nil
}
func kafkaString(s string) []byte {
b := []byte(s)
buf := make([]byte, 2+len(b))
binary.BigEndian.PutUint16(buf, uint16(len(b)))
copy(buf[2:], b)View on GitHub (pinned to 95cc12e753)
Solutions
- Verify the target port is a Kafka broker (run identifyService or a version probe first)
- Re-sync the stream: if framing desync is suspected, reconnect instead of continuing to read
- Log the raw 4 length bytes when this fires to diagnose non-Kafka responses
- Keep this as a hard validation; do not loosen the <4 check
Example fix
// before
msgLen := int(binary.BigEndian.Uint32(lenBuf))
if msgLen < 4 {
return nil, fmt.Errorf("invalid kafka response length: %d", msgLen)
}
// after
msgLen := int(binary.BigEndian.Uint32(lenBuf))
if msgLen < 4 {
return nil, fmt.Errorf("invalid kafka response length: %d (not a kafka response; raw=% x)", msgLen, lenBuf)
} Defensive patterns
Strategy: type-guard
Validate before calling
// Identify the service before parsing frames
if !identifyService(conn) {
return fmt.Errorf("target does not speak the Kafka protocol")
} Type guard
func isValidKafkaFrameLen(lenBuf []byte) bool {
l := binary.BigEndian.Uint32(lenBuf)
return l >= 4 && l <= maxKafkaResponseSize
} Try / catch
resp, err := kafkaRecv(conn)
if err != nil {
if strings.Contains(err.Error(), "invalid kafka response length") {
log.Println("not a kafka response or desynced stream; reconnect and re-identify service")
return
}
return err
} Prevention
- Run service identification before Kafka frame parsing
- Reconnect after any framing violation; never continue a desynced stream
- Log the raw length bytes when validation fails for diagnosis
- Scan only ports already identified as Kafka
When it happens
Trigger: kafkaRecv (called by doKafkaAuth, identifyService, and unit tests): the connection returns a length prefix whose uint32 value is 0..3 — garbage banner from a non-Kafka service, desynced stream, or corrupt frame.
Common situations: Scanning a port that is not actually Kafka (HTTP server echoing short responses); reading a response mid-stream so the 4 bytes are misaligned with the frame boundary; a proxy mangling the framing.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- kafka response too large: %d
- %s [socks5_unsupported_version]
- socks5_invalid_request
- SASL handshake error: %d
- invalid SCRAM server-first payload
AI-assisted analysis of shadow1ng/fscan@95cc12e753 (2026-09-06).
Data as JSON: /api/errors/a564ff22b98a614c.
Report an issue: GitHub.