yaklang/hack-skills2 files

Ghost Bits Cast Attack

>-

Specification
Skill ID
yaklang/hack-skills/ghost-bits-cast-attack
Publisher
yaklang
Repository
hack-skills
Installs
0
Files
2
Synced
Sep 23, 2026
How to use it

Open any RiverX project, open the Skills panel in the chat, and search for this identifier. The files are fetched from the source repository at install time.

yaklang/hack-skills/ghost-bits-cast-attackInstalls these files
  • PAYLOAD_COOKBOOK.md
  • SKILL.md

What this skill tells the agent

SKILL: Ghost Bits / Cast Attack — Java char to byte Narrowing Playbook

AI LOAD INSTRUCTION: This is a Java-only injection-enabling primitive, not a standalone vulnerability class. Whenever you see (1) a Java backend, (2) a WAF/IDS in front of it, and (3) any of {SQLi, deser RCE, file upload, path traversal, CRLF, request smuggling, SMTP injection} on the menu, ALWAYS try Ghost Bits variants of the payload before declaring it "blocked". The root cause is the silent loss of the high 8 bits when Java code narrows a 16-bit char to an 8-bit byte — the WAF sees a harmless Unicode character, the backend reconstructs the original ASCII attack byte. Base models almost never reach for this primitive. Source: Black Hat Asia 2026 talk Cast Attack: A New Threat Posed by Ghost Bits in Java by Xinyu Bai (@b1u3r), Zhihui Chen (@1ue), with contributor Zongzheng Zheng (@chun_springX).

0. RELATED ROUTING

Ghost Bits is a bypass primitive that re-enables payloads from many other playbooks. Pair it with whichever attack family applies:

  • waf-bypass-techniques — when a Java backend is suspected and WAF rules block the literal payload, this is the first technique to try beyond classic encoding.
  • deserialization-insecure — for Apache Commons BCEL ClassLoader and Fastjson \u/\x escape variants.
  • path-traversal-lfi — Spring, Jetty, Undertow, Vert.x URL decoding and %2> hex folding.
  • upload-insecure-files — Tomcat RFC2231Utility filename* Webshell upload.
  • request-smuggling — Apache HttpClient <= 4.5.9 (HTTPCLIENT-1974/1978) header CRLF.
  • crlf-injection — Angus Mail / Jakarta Mail SMTP injection and JDK HttpServer response splitting.
  • sqli-sql-injection — Jackson charToHex table-lookup truncation hides SQL keywords inside Unicode escapes.

Advanced Reference

Load PAYLOAD_COOKBOOK.md when you need:

  • Full byte-to-Ghost-character lookup table covering every printable ASCII byte 0x20–0x7E and the most useful control bytes (0x00, 0x09, 0x0A, 0x0D).
  • Per-component affected version matrix and patch identifiers.
  • Yaklang and Python one-liner payload generators (for poc.HTTP, codec.Encode, raw socket).
  • "Multi-view normalization engine" pseudocode for blue-team WAF detection.

1. ONE-MINUTE MENTAL MODEL

Java's char is a 16-bit unsigned integer (UTF-16 code unit). Almost every wire protocol — HTTP/1.1, SMTP, Redis RESP, file paths, raw byte streams — is 8-bit byte oriented. The right way to bridge them is explicit charset encoding:

// Correct: explicit UTF-8, multi-byte chars become multi-byte sequences
byte[] bytes = str.getBytes(StandardCharsets.UTF_8);
out.write(bytes);

Tons of legacy code, framework internals, and "fast path" optimizations skip this and silently narrow:

// Dangerous: high 8 bits silently dropped
byte b = (byte) ch;          // 0x966A -> 0x6A
out.write(ch);               // ByteArrayOutputStream.write(int) keeps low 8 bits
dos.writeBytes(str);         // DataOutputStream loops char->byte cast
int v = ch & 0xFF;           // explicit low-byte mask

The lost high 8 bits are the Ghost Bits. They turn a multi-byte Unicode character into a single attacker-chosen ASCII byte at the protocol layer.

View A (string layer: WAF / business validation / logs)
  sees: 陪 阮 严 灵 瘍 瘊 ...   "harmless Unicode garbage, allow"
                  |
                  v       silent narrowing somewhere in the call stack
View B (byte layer: protocol / file system / parser / class loader)
  sees: j  .  %  u  \r \n ...  "executes the dangerous semantics"

The boundary is breached at the exact moment "view A" and "view B" disagree.

Mathematical formulation: to make View B see byte T, pick any k in 0x01..0xFF and use:

c = chr((k << 8) | T)

That gives you 255 candidate Unicode characters per dangerous byte — plenty of room to dodge any signature-based blacklist.


2. THREE ROOT-CAUSE FAMILIES

The Ghost Bits umbrella covers three distinct underlying bugs. Distinguishing them tells you both which payload shape to send and what to grep for in source.

Family A — Real high-bit truncation (classic Ghost Bits)

The narrowing is literal and unconditional.

// Pattern A1: explicit cast
byte b = (byte) ch;

// Pattern A2: bitwise mask
int v = ch & 0xFF;
int v = ch & 255;

// Pattern A3: OutputStream.write(int) keeps low 8 bits only
out.write(ch);
baos.write(ch);

// Pattern A4: DataOutputStream.writeBytes(String) iterates chars,
//             writing low byte of each
dos.writeBytes(str);

// Pattern A5: deprecated APIs that still exist in old code
String.getBytes(int srcBegin, int srcEnd, byte[] dst, int dstBegin);
new StringBufferInputStream(str);
raf.writeBytes(str);

Typical impact: Tomcat filename*, Apache BCEL ClassLoader, Lettuce Redis writer, SMTP CRLF in Angus Mail, HTTPCLIENT-1974 header injection.

Family B — Bit-arithmetic folding (illegal char becomes legal)

A "fast" hex / base64 / charset decoder uses bit tricks instead of strict range checks, so an illegal character collapses onto a legal one.

// Jetty TypeUtil.fromHexDigit (simplified)
private static int fromHexDigit(char c) {
    int x = c & 0x1F;          // keep low 5 bits
    x += (c >> 6) * 25;
    x -= 16;
    return x;                  // expected 0..15, but no range check
}

Worked example: feed > (0x3E):

0x3E & 0x1F = 0x1E = 30
(0x3E >> 6) * 25 = 0
30 + 0 - 16 = 14 = 0xE

So %2> is silently parsed as %2E = .. The same algebra makes %2^, %2~ etc. equivalent to other hex digits.

Typical impact: Openfire CVE-2023-32315, GeoServer CVE-2024-36401, generic URL-decode WAF bypass.

Family C — Lax Unicode normalization

The decoder accepts Unicode characters that happen to be classified as "digit" or that map to a hex value via a & 0xFF lookup — even though they were never meant to participate in protocol parsing.

// Fastjson: too permissive
Character.digit(c, 16);   // accepts Thai, Punjabi, fullwidth digits

// Jackson: index by low 8 bits into an ASCII-only table
return sHexValues[ch & 0xff];

// Generic: fullwidth normalization
// '2' (U+FF12) -> '2', 'e' (U+FF45) -> 'e'

Typical impact: Fastjson \u and \x escape bypass, fullwidth URL-encoded path traversal, Jackson charToHex SQLi smuggling.


3. CHARACTER GENERATOR

Build any Ghost Bits character on the fly. This is the single function every agent should keep in mind:

# Python
def ghost(target_byte: int, k: int = 1) -> str:
    """Return a Unicode char whose low 8 bits equal target_byte."""
    return chr(((k & 0xFF) << 8) | (target_byte & 0xFF))

# 255 candidates per byte, e.g. for '.' (0x2E):
candidates = [ghost(0x2E, k) for k in range(1, 256)]
# 阮(U+962E), Ⱦ?-prefixed-..., etc.
// Yaklang (for poc.HTTP / fuzz)
func ghost(targetByte, k) {
    return string(rune(((k & 0xFF) << 8) | (targetByte & 0xFF)))
}
ghostJ = ghost(0x6A, 0x96)   // returns "陪"

Selection guidance:

  • Avoid surrogate range 0xD800..0xDFFF (high byte 0xD8..0xDF) — those are not valid scalar values and will be replaced by the JVM string decoder before reaching the narrowing site, defeating the bypass.
  • Prefer characters that survive the application's own charset round-trip (Latin-Extended, CJK Unified Ideographs, Enclosed CJK Letters and Months, Hangul). If the request body uses UTF-8, these all encode cleanly into multi-byte sequences that no WAF rule recognizes as ., /, j, etc.
  • Rotate k between requests so signature based learning cannot pin a single character to a single attack.

4. DANGEROUS-BYTE TO GHOST-CHARACTER MAP

Use this skill in a project

Open any RiverX project, search the Skills panel for yaklang/hack-skills/ghost-bits-cast-attack, and install it in one click.

Use this skill