GhostBits,JavaWAF之殇?

admin 2026-05-22 02:19:57 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 文档探讨Java中的GhostBits漏洞,即char转byte时高位丢失导致WAF检测与后端处理差异的安全问题。通过CVE案例展示该漏洞在Spring框架、Openfire中的实际利用方式,并指出泛用性强的绕过手法对WAF构成严峻挑战。建议开发者警惕类型转换的隐性风险并加强多层校验。 综合评分: 85 文章分类: 漏洞分析,WEB安全,安全开发,应用安全,红队


cover_image

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);
&nbsp; &nbsp;&nbsp;if&nbsp;(b <&nbsp;0&nbsp;|| b >&nbsp;15)
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;throw&nbsp;new&nbsp;NumberFormatException("!hex "&nbsp;+ c);
&nbsp; &nbsp;&nbsp;return&nbsp;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之殇?》

评论:0   参与:  0