projectdiscovery/nuclei · warning
empty mysql greeting
Error message
empty mysql greeting
What it means
fingerprintConn reads one MySQL packet with recvMySQLPacket and then guards against a zero-length result before parsing. recvMySQLPacket already fails on short reads and rejects payload lengths <= 0, so this branch is a defensive invariant: a non-error empty read should be impossible on the wire path. Seeing it means an empty greeting buffer reached the parser — from tests calling parseMySQLGreeting directly or a future code path that bypasses the length gate.
Source
Thrown at pkg/js/libs/mysql/fingerprint.go:137
Capabilities []string `json:"capabilities,omitempty"`
CharacterSet uint8 `json:"characterSet,omitempty"`
StatusFlags uint16 `json:"statusFlags,omitempty"`
Status []string `json:"status,omitempty"`
AuthPluginDataLen int `json:"authPluginDataLen,omitempty"`
Salt string `json:"salt,omitempty"`
AuthPluginName string `json:"authPluginName,omitempty"`
ErrorMessage string `json:"errorMsg,omitempty"`
ErrorCode int `json:"errorCode,omitempty"`
}
// fingerprintConn reads the MySQL greeting once and parses an extended fingerprint.
func fingerprintConn(conn net.Conn, timeout time.Duration) (HandshakeInfo, error) {
raw, err := recvMySQLPacket(conn, timeout)
if err != nil {
return HandshakeInfo{}, err
}
if len(raw) == 0 {
return HandshakeInfo{}, fmt.Errorf("empty mysql greeting")
}
return parseMySQLGreeting(raw)
}
func recvMySQLPacket(conn net.Conn, timeout time.Duration) ([]byte, error) {
if err := conn.SetReadDeadline(time.Now().Add(timeout)); err != nil {
return nil, err
}
header := make([]byte, 4)
if _, err := io.ReadFull(conn, header); err != nil {
return nil, err
}
length := int(uint32(header[0]) | uint32(header[1])<<8 | uint32(header[2])<<16)
if length <= 0 || length > 16*1024*1024 {
return nil, fmt.Errorf("invalid mysql packet length %d", length)
}
payload := make([]byte, length)
if _, err := io.ReadFull(conn, payload); err != nil {View on GitHub (pinned to 265b3a3dec)
Solutions
- If hit from JS, treat as 'target did not send a MySQL greeting' and fall back to generic service detection
- Retry the fingerprint once — transient deadline races can drop the read
- Check the port with nmap -sV to see whether MySQL is actually listening
- If reproducing in Go tests, ensure the fixture packet has a valid 4-byte header with a positive payload length
Defensive patterns
Strategy: try-catch
Validate before calling
if (!mysql.IsMySQL || !mysql.IsMySQL(host, port)) { /* probe first when available */ } Try / catch
try { const info = mysql.FingerprintMySQL(host, port); }
catch (e) { log('no mysql greeting from ' + host + ': ' + e); } Prevention
- Treat an empty greeting as a non-MySQL port
- Retry once to rule out deadline races, then move on
- Restrict mysql fingerprints to likely-database ports (3306, 33060, custom DB ports)
When it happens
Trigger: mssql-style FingerprintMySQL flows (mysql.FingerprintMySQL(host, port) via fingerprintConn) where the read layer yields zero bytes without error, or Go unit tests invoking parseMySQLGreeting with an empty slice.
Common situations: Mostly a library-internal invariant; realistically surfaces in fuzzing or when the conn deadline expires exactly between header and payload in a way that returns no data.
Related errors
- invalid mysql packet length %d
- mysql handshake packet too short
- mysql handshake packet length out of range
- unsupported mysql protocol version
- mysql handshake missing filler byte
AI-assisted analysis of projectdiscovery/nuclei@265b3a3dec (2026-08-15).
Data as JSON: /api/errors/54caddd4da9cc9f6.
Report an issue: GitHub.