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
- 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
- Confirm the port number — this usually means a non-MySQL binary service answered
- Treat the error as 'not MySQL (plaintext)' and continue the scan
- 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
- Check whether the MySQL port enforces TLS before plaintext probing
- Filter obviously binary banners with a raw net.Open+Recv pre-read
- Treat ASCII parse failures as 'not plaintext MySQL'
- Use the code protocol for non-standard or wrapped deployments
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
- mysql handshake filler byte is not zero
- empty mysql greeting
- invalid mysql packet length %d
- mysql packet too short
- mysql error packet too short
AI-assisted analysis of projectdiscovery/nuclei@265b3a3dec (2026-08-15).
Data as JSON: /api/errors/000718a88e51d6a0.
Report an issue: GitHub.