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

  1. If hit from JS, treat as 'target did not send a MySQL greeting' and fall back to generic service detection
  2. Retry the fingerprint once — transient deadline races can drop the read
  3. Check the port with nmap -sV to see whether MySQL is actually listening
  4. 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

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


AI-assisted analysis of projectdiscovery/nuclei@265b3a3dec (2026-08-15). Data as JSON: /api/errors/54caddd4da9cc9f6. Report an issue: GitHub.