文章总结: 文档探讨Java中的GhostBits漏洞,即char转byte时高位丢失导致WAF检测与后端处理差异的安全问题。通过CVE案例展示该漏洞在Spring框架、Openfire中的实际利用方式,并指出泛用性强的绕过手法对WAF构成严峻挑战。建议开发者警惕类型转换的隐性风险并加强多层校验。 综合评分: 85 文章分类: 漏洞分析,WEB安全,安全开发,应用安全,红队
Ghost Bits,Java WAF之殇?
原创
LoRexxar LoRexxar
LR的安全自留地
2026年4月29日 18:21 北京
在小说阅读器读本章
去阅读
在前两天的BlacksetHat Asia 2026上,@浅蓝和@1ue分享一个非常有趣的议题,Java中的GhostBits漏洞
- • https://i.blackhat.com/Asia-26/Presentations/Asia-26-Bai-Cast-Attack-Ghost-Bits-4.23.pdf
探究深度非常深,影响范围非常之广,内容非常有意思
什么是Ghost Bits?
Ghost Bits这个概念的来源太久很难深究,甚至在软件领域之前就已经有类似的概念。Ghost Bits主要是指那些在莫名其妙的位置影响到软件运行的位,所以形容像幽灵一样。
在这个议题中,Ghost Bits主要指的是在某些类型转换过程中被不小心丢掉的高位,导致原字符串内容变化。
最经典的场景就是char类型和byte类型的转换,也是Java的经典场景。
char类型是16位(2字节),byte类型是8位(1字节),如果发生char强制转换为byte就会丢弃高8位,只保留低8位。其中的8位就像幽灵一样消失了。
在Java中,有4种非常常见的写法都会有该问题
- •
(byte) ch:显式的byte强制类型转化 - •
ch & 0xFF:位掩码,保留低8位 - •
OutputStream.write(int):写入流时被截断 - •
DataOutputStream.writeBytes():官方JDK方法,在文档中明确写明会丢弃高8位
而在unicode中,会有大量的高位内容,经过处理和转换之后就会被截断变成对应的字符
要注意的是,这个问题本质上在源代码层面表现一致,不能单独算作是一个漏洞,所以在80%的场景下,该问题主要影响的是和源代码不在同一层的软件,其中最经典的就是waf!
具体怎么回事?
基础的原理刚才都理解了,其实就是利用高位无效的机制问题,使得输入的内容在waf和实际源代码处理的时候遇到的是不同的内容。
比如说中文字阮,经过处理之后源代码获得的就是.
字符 '阮' = U+962E = 0x962E
(byte) 0x962E = 0x2E = '.'
那你就可以用这种方式绕过WAF的限制
比如说我输入\u丰丰耳失,waf收到这个输入的时候认为没有任何敏感词,则放行到后端jackson,后端将其转为byte,最终拼接成sql注入语句
最神奇的是,这种逻辑的泛用性极强,首先本身高位被抛弃意味着高位可以塞入任意值,那么对于poc就是多对1的转化关系。
以下两种都可以直接转为对应的../../,这对于waf来讲就是极强的考验,即便只针对byte的转化关系,waf也非常难处理
继续拓展?
刚才提到了,在java本身的代码中,char类型和byte类型的转化是非常常用的写法,其带来的问题往往并不能直白的影响到源代码层面,但对于安全来讲,似乎小概率事件会导致大概率问题?!
CVE-2025-41242 Spring框架因Jetty URI解析不一致导致的路径穿越漏洞
刚才我们讨论的是泛用性非常强的waf场景,那么在Spring框架下,本身会有一个非常大的问题,就是Spring框架中StringUtils.uriDecode和JettyURIUtil.encodePathSafeEncoding的处理方式不一致,导致了底层的路径穿越问题。
- • https://github.com/advisories/GHSA-r936-gwx5-v52f
- • https://github.com/spring-projects/spring-framework/commit/24e66b63
对于Spring框架的StringUtils.uriDecode方法
遇到%时会做专门的处理,并调用ByteArrayOutputStream.write导致了高位bit丢失,出现Ghost Bits漏洞。
阮严灵丰丰甲来会被转为.%u002e
这个输入在Spring层面,不但可以通过isInvalidPath/isInvalidEncodedPath的路径检查,还不会被识别为正常的%u编码,全部放行
传递到Jetty中,URIUtil.encodePathSafeEncoding却会将%u002e做unicode解码转为.,最终构造成为../
image-20260429181104521
Openfire CVE-2023-32315 — 认证绕过
转为Byte丢失高位的方案大家都知道,还有一些更邪门的其他漏洞,其实本质上也是类似的问题。
一个很有趣的例子就是Openfire CVE-2023-32315,这个漏洞本质上是一个基础的路径穿越漏洞
- • CVE-2008-6508,最早的漏洞只需要..就可以实现路径穿越
- • CVE-2023-32315,发现可以用%u002e,也就是UTF-16来替代%2e实现路径穿越,因为AuthCheckFilter并没有校验对应的输入,但Jetty支持%u解码,导致了漏洞的绕过
poc就是这样的
/setup/setup-a/%u002e%u002e/%u002e%u002e/log.jsp
很多WAF都加入了%u002e%u002e作为关键字之一,那么你可以使用这个poc来绕过
/setup/setup-a/%2>%2>/%2>%2>/log.jsp
这里有个比较邪门的点在于,对于大部分框架来说,他们不会把%2>当做url编码去处理,因为>并不是合法的url编码。
但是对于Jetty来说,他会一视同仁,把>传入到convertHexDigit做处理
public static byte convertHexDigit(byte c) {
byte b = (byte)((c & 0x1f) + ((c >> 6) * 0x19) - 0x10);
if (b < 0 || b > 15)
throw new NumberFormatException("!hex " + c);
return b;
}
也就是说即便是符号>依旧会经过这一套算法,最终获得结果是14,对应E
那么这样一来,waf收到的请求是%2>不合法的url编码不做处理,jetty把他处理转为了%2E成功输入.绕过waf。
写在最后
其实类似的场景同样非常多,因为许多大型框架中,除了显式的Java类型转化,还会有隐式的框架中的处理导致同样的问题,在原议题中分享了不同框架下涉及到不同漏洞的很多种问题,他们无一都是开发者无意中触发了Ghost bits问题,设计者并没有提前考虑好类型强制转化的额外影响。
正如演讲结尾所说:“We have only scratched the surface” — 这才刚刚开始。
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:LR的安全自留地 LoRexxar LoRexxar《Ghost Bits,Java WAF之殇?》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论