Ghost Bits(幽灵比特)初学理解与总结

原理

什么是 Ghost Bits?

Ghost Bits(幽灵比特) 是 Java 语言特有的底层安全缺陷:

Java 的 char 是 16 位,byte 是 8 位。

当执行 (byte) ch 强制转换时,高 8 位会被直接丢弃且不报错,只保留低 8 位。

这部分被悄悄丢掉的高位,就叫 Ghost Bits。

char = 16 bit
byte = 8 bit

char:字符(存放看得见的文字)占16位,能存汉字、生僻字、特殊符号

byte:字节(计算机最小的存储单元)占8位,装不下汉字只能存英文、数字的底层编码

(byte)ch

就是把 16 位的 char 塞进 8 位的 byte 。一定会发生高 8 位丢失,只保留:低 8 位。

简单演示

一、

public class main {
    public static void main(String[] args) {
        char ch = '陪';
        System.out.println(Integer.toHexString(ch));
    }
}

966a

image-20260428203551362.png

陪 = U+966A
Hex = 0x966A

二、

public class main {
    public static void main(String[] args) {
        char ch = '陪';
        System.out.println(Integer.toHexString(ch));
        byte b = (byte) ch;
		System.out.println((char) b);
    }
}

j

image-20260428203640135.png

陪 → 0x966A

强转 byte 只保留低 8 位 → 0x6A

0x6A = ASCII j

也就是说,陪->j

汉字 → 悄悄变成危险字符

为什么危险?

因为安全检查看到的是char,而真正执行的是byte,于是检查的是A,执行的是B,WAF、业务过滤全部失效。

这叫Security Semantic Split(安全语义分裂),这才是真正危险的地方。

本地复现

文件上传绕过

场景:系统禁止

.jsp
.php
.exe

允许

.jpg
.png
.txt

开发写了:

if (filename.endsWith(".jsp")) {
    reject();//拦截
}

他觉得很安全,但其实不安全

Demo 1:Ghost Bits 文件名绕过

public class GhostBitsDemo {

    public static void main(String[] args) throws Exception {

        String filename = "1.陪sp";

        // 安全检查阶段
        if (filename.endsWith(".jsp")) {
            System.out.println("拦截:危险文件");
        } else {
            System.out.println("通过:看起来安全");
        }

        // 底层保存阶段(模拟)
        byte[] bytes = new byte[filename.length()];

        for (int i = 0; i < filename.length(); i++) {
            bytes[i] = (byte) filename.charAt(i);//charAt是取出第i个
        }
		//将bytes数组[][][]...按照该 ISO-8859-1 翻译表 重新变回一个字符串!
        String realFilename = new String(bytes, "ISO-8859-1");

        System.out.println("真实落地文件名: " + realFilename);
    }
}

通过:看起来安全
真实落地文件名: 1.jsp

为什么成功?

陪=0x966A
低8位 = 0x6A = j

1.陪sp -> 1.jsp

绕过了,成功上传木马

Demo 2:CRLF 注入(更高级)

瘍 → U+760D → 低 8 位 0x0D → \r
瘊 → U+760A → 低 8 位 0x0A → 

所以

瘍瘊 → \r

这意味着可以注入协议换行

例如:

  • SMTP 注入

  • Header 注入

  • 请求走私

  • XSS

    public class CRLFDemo {

      public static void main(String[] args) throws Exception {
    
          String payload = "user瘍瘊X-Test: hacked";
    
          byte[] bytes = new byte[payload.length()];
    
          for (int i = 0; i < payload.length(); i++) {
              bytes[i] = (byte) payload.charAt(i);
          }
    
          String result = new String(bytes, "ISO-8859-1");
    
          System.out.println(result);
      }
    

    }

    user
    X-Test: hacked

Header 响应头被注入了

image-20260428214801287.png

总结

高危代码

以下这些,只取字符的低 8 位,高位直接丢掉

汉字 → 强转 → 变成英文 / 符号

安全字符 → 悄悄 → 危险字符

(byte) ch
ch & 0xff
writeBytes()
OutputStream.write()
RandomAccessFile.writeBytes()
StringBufferInputStream
URLDecoder.decode()

修复

永远使用:

str.getBytes(StandardCharsets.UTF_8)

不要使用:

(byte)
writeBytes()
check(String)
use(byte)

必须:(同一语义、同一类型、同一编码)这才安全。

Ghost Bits 不是某个漏洞而是一整类漏洞模型,它能变成

  • 文件上传绕过
  • 路径穿越
  • 任意文件读取
  • 认证绕过
  • SMTP 注入
  • 请求走私
  • XSS
  • RCE 绕过

原PPT是在棉花糖公众号要的,链接是
https://mp.weixin.qq.com/s/Utx64ue7Phs44pCHrpJbrQ

由于个人水平有限,本篇对 Ghost Bits 漏洞的理解仍较为浅显,文中若有疏漏或错误之处,还望各位师傅多多指正,不胜感激!

温馨提示:本文内容仅用于合法授权的安全学习与研究交流,严禁用于未授权渗透测试、漏洞利用或任何违法行为。

涉及企业或平台的未公开漏洞信息,请遵循负责任披露原则,勿公开传播可直接复现的敏感细节。

如存在侵权、错误信息或不当内容,请联系站方处理,我们将及时核实并删除。邮箱:admin@baimaojianghu.com。