1
+ /*
2
+ * Licensed to the Apache Software Foundation (ASF) under one or more
3
+ * contributor license agreements. See the NOTICE file distributed with
4
+ * this work for additional information regarding copyright ownership.
5
+ * The ASF licenses this file to You under the Apache License, Version 2.0
6
+ * (the "License"); you may not use this file except in compliance with
7
+ * the License. You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ package org .apache .ignite .internal .processors .rest ;
19
+
20
+ import java .io .BufferedInputStream ;
21
+ import java .io .BufferedOutputStream ;
22
+ import java .io .IOException ;
23
+ import java .io .InputStream ;
24
+ import java .io .ObjectInputStream ;
25
+ import java .io .OutputStream ;
26
+ import java .net .InetAddress ;
27
+ import java .net .Socket ;
28
+ import java .nio .ByteBuffer ;
29
+ import java .util .UUID ;
30
+ import java .util .concurrent .atomic .AtomicBoolean ;
31
+ import org .apache .ignite .configuration .ConnectorConfiguration ;
32
+ import org .apache .ignite .configuration .IgniteConfiguration ;
33
+ import org .apache .ignite .internal .client .marshaller .jdk .GridClientJdkMarshaller ;
34
+ import org .apache .ignite .internal .processors .rest .client .message .GridClientHandshakeRequest ;
35
+ import org .apache .ignite .internal .processors .rest .client .message .GridClientMessage ;
36
+ import org .apache .ignite .internal .util .IgniteUtils ;
37
+ import org .apache .ignite .internal .util .lang .GridAbsPredicate ;
38
+ import org .apache .ignite .internal .util .typedef .internal .U ;
39
+ import org .apache .ignite .testframework .GridTestUtils ;
40
+ import org .apache .ignite .testframework .junits .common .GridCommonAbstractTest ;
41
+
42
+ import static org .apache .ignite .IgniteSystemProperties .IGNITE_MARSHALLER_BLACKLIST ;
43
+ import static org .apache .ignite .IgniteSystemProperties .IGNITE_MARSHALLER_WHITELIST ;
44
+ import static org .apache .ignite .internal .processors .rest .protocols .tcp .GridMemcachedMessage .IGNITE_HANDSHAKE_FLAG ;
45
+ import static org .apache .ignite .internal .processors .rest .protocols .tcp .GridMemcachedMessage .IGNITE_REQ_FLAG ;
46
+
47
+ /**
48
+ * Tests for whitelist and blacklist ot avoiding deserialization vulnerability.
49
+ */
50
+ public class TcpRestUnmarshalVulnerabilityTest extends GridCommonAbstractTest {
51
+ /** Marshaller. */
52
+ private static final GridClientJdkMarshaller MARSH = new GridClientJdkMarshaller ();
53
+
54
+ /** Shared value. */
55
+ private static final AtomicBoolean SHARED = new AtomicBoolean ();
56
+
57
+ /** Port. */
58
+ private static int port ;
59
+
60
+ /** Host. */
61
+ private static String host ;
62
+
63
+ /** {@inheritDoc} */
64
+ @ Override protected IgniteConfiguration getConfiguration (String igniteInstanceName ) throws Exception {
65
+ IgniteConfiguration cfg = super .getConfiguration (igniteInstanceName );
66
+
67
+ ConnectorConfiguration connCfg = new ConnectorConfiguration ();
68
+
69
+ port = connCfg .getPort ();
70
+ host = connCfg .getHost ();
71
+
72
+ cfg .setConnectorConfiguration (connCfg );
73
+
74
+ return cfg ;
75
+ }
76
+
77
+ /** {@inheritDoc} */
78
+ @ Override protected void beforeTest () throws Exception {
79
+ super .beforeTest ();
80
+
81
+ SHARED .set (false );
82
+
83
+ System .clearProperty (IGNITE_MARSHALLER_WHITELIST );
84
+ System .clearProperty (IGNITE_MARSHALLER_BLACKLIST );
85
+
86
+ IgniteUtils .clearClassCache ();
87
+ }
88
+
89
+ /**
90
+ * @throws Exception If failed.
91
+ */
92
+ public void testNoLists () throws Exception {
93
+ testExploit (true );
94
+ }
95
+
96
+ /**
97
+ * @throws Exception If failed.
98
+ */
99
+ public void testWhiteListIncluded () throws Exception {
100
+ String path = U .resolveIgnitePath ("modules/core/src/test/config/class_list_exploit_included.txt" ).getPath ();
101
+
102
+ System .setProperty (IGNITE_MARSHALLER_WHITELIST , path );
103
+
104
+ testExploit (true );
105
+ }
106
+
107
+ /**
108
+ * @throws Exception If failed.
109
+ */
110
+ public void testWhiteListExcluded () throws Exception {
111
+ String path = U .resolveIgnitePath ("modules/core/src/test/config/class_list_exploit_excluded.txt" ).getPath ();
112
+
113
+ System .setProperty (IGNITE_MARSHALLER_WHITELIST , path );
114
+
115
+ testExploit (false );
116
+ }
117
+
118
+ /**
119
+ * @throws Exception If failed.
120
+ */
121
+ public void testBlackListIncluded () throws Exception {
122
+ String path = U .resolveIgnitePath ("modules/core/src/test/config/class_list_exploit_included.txt" ).getPath ();
123
+
124
+ System .setProperty (IGNITE_MARSHALLER_BLACKLIST , path );
125
+
126
+ testExploit (false );
127
+ }
128
+
129
+ /**
130
+ * @throws Exception If failed.
131
+ */
132
+ public void testBlackListExcluded () throws Exception {
133
+ String path = U .resolveIgnitePath ("modules/core/src/test/config/class_list_exploit_excluded.txt" ).getPath ();
134
+
135
+ System .setProperty (IGNITE_MARSHALLER_BLACKLIST , path );
136
+
137
+ testExploit (true );
138
+ }
139
+
140
+ /**
141
+ * @throws Exception If failed.
142
+ */
143
+ public void testBothListIncluded () throws Exception {
144
+ String path = U .resolveIgnitePath ("modules/core/src/test/config/class_list_exploit_included.txt" ).getPath ();
145
+
146
+ System .setProperty (IGNITE_MARSHALLER_WHITELIST , path );
147
+ System .setProperty (IGNITE_MARSHALLER_BLACKLIST , path );
148
+
149
+ testExploit (false );
150
+ }
151
+
152
+ /**
153
+ * @param positive Positive.
154
+ */
155
+ private void testExploit (boolean positive ) throws Exception {
156
+ try {
157
+ startGrid ();
158
+
159
+ attack (marshal (new Exploit ()).array ());
160
+
161
+ boolean res = GridTestUtils .waitForCondition (new GridAbsPredicate () {
162
+ @ Override public boolean apply () {
163
+ return SHARED .get ();
164
+ }
165
+ }, 3000L );
166
+
167
+ if (positive )
168
+ assertTrue (res );
169
+ else
170
+ assertFalse (res );
171
+ }
172
+ finally {
173
+ stopAllGrids ();
174
+ }
175
+ }
176
+
177
+ /**
178
+ * @param obj Object.
179
+ */
180
+ private static ByteBuffer marshal (Object obj ) throws IOException {
181
+ return MARSH .marshal (obj , 0 );
182
+ }
183
+
184
+ /**
185
+ * @param data Data.
186
+ */
187
+ private void attack (byte [] data ) throws IOException {
188
+ InetAddress addr = InetAddress .getByName (host );
189
+
190
+ try (
191
+ Socket sock = new Socket (addr , port );
192
+ OutputStream os = new BufferedOutputStream (sock .getOutputStream ())
193
+ ) {
194
+ // Handshake request.
195
+ os .write (IGNITE_HANDSHAKE_FLAG );
196
+
197
+ GridClientHandshakeRequest req = new GridClientHandshakeRequest ();
198
+ req .marshallerId (GridClientJdkMarshaller .ID );
199
+ os .write (req .rawBytes ());
200
+ os .flush ();
201
+
202
+ // Handshake response
203
+ InputStream is = new BufferedInputStream (sock .getInputStream ());
204
+
205
+ is .read (new byte [146 ]); // Read handshake response.
206
+
207
+ int len = data .length + 40 ;
208
+
209
+ os .write (IGNITE_REQ_FLAG ); // Package type.
210
+ os .write ((byte )(len >> 24 )); // Package length.
211
+ os .write ((byte )(len >> 16 ));
212
+ os .write ((byte )(len >> 8 ));
213
+ os .write ((byte )(len ));
214
+ os .write (new byte [40 ]); // Stream header.
215
+ os .write (data ); // Exploit.
216
+ os .flush ();
217
+ }
218
+ }
219
+
220
+ /** */
221
+ private static class Exploit implements GridClientMessage {
222
+ /**
223
+ * @param is Input stream.
224
+ */
225
+ private void readObject (ObjectInputStream is ) throws ClassNotFoundException , IOException {
226
+ SHARED .set (true );
227
+ }
228
+
229
+ /** {@inheritDoc} */
230
+ @ Override public long requestId () {
231
+ return 0 ;
232
+ }
233
+
234
+ /** {@inheritDoc} */
235
+ @ Override public void requestId (long reqId ) {
236
+ // No-op.
237
+ }
238
+
239
+ /** {@inheritDoc} */
240
+ @ Override public UUID clientId () {
241
+ return null ;
242
+ }
243
+
244
+ /** {@inheritDoc} */
245
+ @ Override public void clientId (UUID id ) {
246
+ // No-op.
247
+ }
248
+
249
+ /** {@inheritDoc} */
250
+ @ Override public UUID destinationId () {
251
+ return null ;
252
+ }
253
+
254
+ /** {@inheritDoc} */
255
+ @ Override public void destinationId (UUID id ) {
256
+ // No-op.
257
+ }
258
+
259
+ /** {@inheritDoc} */
260
+ @ Override public byte [] sessionToken () {
261
+ return new byte [0 ];
262
+ }
263
+
264
+ /** {@inheritDoc} */
265
+ @ Override public void sessionToken (byte [] sesTok ) {
266
+ // No-op.
267
+ }
268
+ }
269
+ }
0 commit comments