peass-ng/PEASS-ng · error · IOException

invalid characters encountered in Hex data

Error message

invalid characters encountered in Hex data

What it means

HexEncoder.Decode decodes hex-encoded bytes from an input stream into an output stream. Each decoded pair of characters is looked up in the decoder's table; if either nibble value has its high bit set (>= 0x80), the character was not a valid hex digit, so an IOException is thrown. This prevents silently producing garbage bytes from invalid input.

Source

Thrown at winPEAS/winPEASexe/winPEAS/3rdParty/BouncyCastle/util/encoders/HexEncoder.cs:130

            int i = off;
            while (i < end)
            {
                while (i < end && Ignore((char)data[i]))
                {
                    i++;
                }

                b1 = decodingTable[data[i++]];

                while (i < end && Ignore((char)data[i]))
                {
                    i++;
                }

                b2 = decodingTable[data[i++]];

                if ((b1 | b2) >= 0x80)
                    throw new IOException("invalid characters encountered in Hex data");

                buf[bufOff++] = (byte)((b1 << 4) | b2);

                if (bufOff == buf.Length)
                {
                    outStream.Write(buf, 0, bufOff);
                    bufOff = 0;
                }

                outLen++;
            }

            if (bufOff > 0)
            {
                outStream.Write(buf, 0, bufOff);
            }

            return outLen;

View on GitHub (pinned to 53fb989abc)

Solutions

  1. Sanitize the input: strip whitespace, quotes, 0x prefixes, and other non-hex characters before decoding.
  2. Verify the data really is hex (regex ^[0-9a-fA-F]+$ with even length) before calling Decode.
  3. If input may contain invalid characters, use prevalidation or a lenient pre-clean step rather than relying on the exception.
  4. Check the source of the data — if it was hex-encoded with this library's HexEncoder, re-encode instead of decoding a corrupted copy.

Example fix

// before
Hex.Decode(input); // 'a b1...' throws IOException
// after
var cleaned = new string(input.Where(char.IsAsciiHexDigit).ToArray());
if (cleaned.Length % 2 != 0) throw new ArgumentException("not hex");
Hex.Decode(cleaned);
Defensive patterns

Strategy: validation

Validate before calling

static bool IsHexString(string s) => s != null && s.Length % 2 == 0 && System.Text.RegularExpressions.Regex.IsMatch(s, "^[0-9a-fA-F]+$");

Type guard

static bool IsValidHexPair(char a, char b) => Uri.IsHexDigit(a) && Uri.IsHexDigit(b);

Try / catch

try { Hex.Decode(input); } catch (IOException ex) when (ex.Message.Contains("invalid characters")) { throw new FormatException("Input is not valid hex", ex); }

Prevention

When it happens

Trigger: Calling HexEncoder.Decode (or Hex.Decode) on a stream/string containing characters outside 0-9, a-f, A-F (e.g. whitespace mid-pair, 'g'-'z', punctuation, UTF-8 BOM, or bytes >0x7F feeding the decoding table).

Common situations: Decoding base64 or other non-hex strings by mistake; copying hex with spaces/newlines/quotes; data corrupted in transit; decoding output of a different encoder (e.g. MD5 digest printed with separators).

Understand the failure class

Related errors


AI-assisted analysis of peass-ng/PEASS-ng@53fb989abc (2026-09-02). Data as JSON: /api/errors/27a306610bc83faa. Report an issue: GitHub.