projectdiscovery/nuclei · warning

encountered invalid ASCII character

Error message

encountered invalid ASCII character

What it means

Thrown by readNullTerminatedASCIIString when it hits a byte that is neither printable ASCII (0x20-0x7e) nor the NUL terminator while scanning the server version string of a MySQL handshake packet. The MySQL version field must be human-readable ASCII terminated by 0x00, so binary garbage means the payload is not a MySQL greeting. It propagates out of IsMySQL / FingerprintMySQL / Connect / ExecuteQuery.

Source

Thrown at pkg/js/libs/mysql/fingerprint.go:373

}

// readNullTerminatedASCIIString mirrors fingerprintx: printable ASCII only,
// returns the index of the NUL terminator (not the next byte).
func readNullTerminatedASCIIString(buf []byte, start int) (string, int, error) {
	if start < 0 || start >= len(buf) {
		return "", 0, fmt.Errorf("invalid string offset")
	}
	var characters []byte
	for position := start; position < len(buf); position++ {
		c := buf[position]
		if c >= 0x20 && c <= 0x7e {
			characters = append(characters, c)
			continue
		}
		if c == 0x00 {
			return string(characters), position, nil
		}
		return "", 0, fmt.Errorf("encountered invalid ASCII character")
	}
	return "", 0, fmt.Errorf("unterminated mysql string")
}

func readPrintableASCII(buf []byte) string {
	var characters []byte
	for _, c := range buf {
		if c >= 0x20 && c <= 0x7e {
			characters = append(characters, c)
		}
	}
	return string(characters)
}

func bytesTrimRightNull(b []byte) []byte {
	for len(b) > 0 && b[len(b)-1] == 0x00 {
		b = b[:len(b)-1]
	}

View on GitHub (pinned to 265b3a3dec)

Solutions

  1. Check whether the MySQL port requires TLS; if so, fingerprint via a TLS connection (e.g. code protocol with net.OpenTLS or SSL protocol) instead of the plaintext MySQL probe
  2. Confirm the port number — this usually means a non-MySQL binary service answered
  3. Treat the error as 'not MySQL (plaintext)' and continue the scan
  4. Capture the raw response with a raw socket to identify what actually answered
Defensive patterns

Strategy: try-catch

Validate before calling

// reject binary/TLS-looking banners before the mysql probe
const conn = net.Open('tcp', host + ':' + port);
const b = conn.Recv(16);
conn.Close();
// TLS records start 0x16 0x03; MySQL greetings start with a length + 0x0a
if (b.charCodeAt(0) === 0x16) { log('TLS on port, use TLS probe'); }

Type guard

function isPrintableAsciiPrefix(s) {
  for (let i = 5; i < s.length && s.charCodeAt(i) !== 0; i++) {
    const c = s.charCodeAt(i);
    if (c < 0x20 || c > 0x7e) return false;
  }
  return true;
}

Try / catch

try {
  mysql.IsMySQL(host, port);
} catch (e) {
  if (String(e).includes('invalid ASCII character')) { /* binary banner: not plaintext mysql */ }
  else { throw e; }
}

Prevention

When it happens

Trigger: The first bytes of the response happen to look like a packet header, but the region at offset 5+ contains binary data: TLS records (probing a TLS-wrapped MySQL port in plaintext), compressed or encrypted banners, or any binary protocol on the target port.

Common situations: MySQL with require_secure_transport or behind an SSL terminator — the plaintext probe reads TLS ClientAlert/ServerHello bytes; non-MySQL binary services on scanned ports; protocol-detection templates run broadly across a target's port range.

Related errors


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