-
Notifications
You must be signed in to change notification settings - Fork 49
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Use trusted packages in StreamMessage
StreamMessage now uses the same "white list" mechanism as ObjectMessage to avoid some arbitrary code execution on deserialization. Even though StreamMessage is supposed to handle only primitive types, it is still to possible to send a message that contains an arbitrary serializable instance. The consuming application application may then execute code from this class on deserialization. The fix consists in using the list of trusted packages that can be set at the connection factory level. Fixes #135
- Loading branch information
1 parent
4497022
commit f647e5d
Showing
4 changed files
with
146 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
117 changes: 117 additions & 0 deletions
117
src/test/java/com/rabbitmq/integration/tests/StreamMessageSerializationIT.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
// This Source Code Form is subject to the terms of the Mozilla Public | ||
// License, v. 2.0. If a copy of the MPL was not distributed with this | ||
// file, You can obtain one at https://mozilla.org/MPL/2.0/. | ||
// | ||
// Copyright (c) 2013-2020 VMware, Inc. or its affiliates. All rights reserved. | ||
package com.rabbitmq.integration.tests; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
import static org.junit.jupiter.api.Assertions.assertThrows; | ||
import static org.junit.jupiter.api.Assertions.fail; | ||
|
||
import com.rabbitmq.jms.admin.RMQConnectionFactory; | ||
import com.rabbitmq.jms.client.message.RMQStreamMessage; | ||
import com.rabbitmq.jms.client.message.TestMessages; | ||
import com.rabbitmq.jms.util.RMQJMSException; | ||
import java.awt.Color; | ||
import java.lang.reflect.Method; | ||
import java.util.Arrays; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
import javax.jms.Queue; | ||
import javax.jms.QueueReceiver; | ||
import javax.jms.QueueSender; | ||
import javax.jms.QueueSession; | ||
import javax.jms.Session; | ||
import javax.jms.StreamMessage; | ||
import org.junit.jupiter.api.Test; | ||
|
||
public class StreamMessageSerializationIT extends AbstractITQueue { | ||
|
||
private static final String QUEUE_NAME = "test.queue." + StreamMessageSerializationIT.class.getCanonicalName(); | ||
private static final long TEST_RECEIVE_TIMEOUT = 1000; // one second | ||
private static final java.util.List<String> TRUSTED_PACKAGES = Arrays.asList("java.lang", "com.rabbitmq.jms"); | ||
|
||
@Override | ||
protected void customise(RMQConnectionFactory connectionFactory) { | ||
super.customise(connectionFactory); | ||
connectionFactory.setTrustedPackages(TRUSTED_PACKAGES); | ||
} | ||
|
||
protected void testReceiveStreamMessageWithValue(Object value) throws Exception { | ||
try { | ||
queueConn.start(); | ||
QueueSession queueSession = queueConn.createQueueSession(false, Session.DUPS_OK_ACKNOWLEDGE); | ||
Queue queue = queueSession.createQueue(QUEUE_NAME); | ||
|
||
drainQueue(queueSession, queue); | ||
|
||
QueueSender queueSender = queueSession.createSender(queue); | ||
StreamMessage message = (StreamMessage) MessageTestType.STREAM.gen(queueSession, null); | ||
|
||
// we simulate an attack from the sender by calling writeObject with a non-primitive value | ||
// (StreamMessage supports only primitive types) | ||
// the value is then sent to the destination and the consumer will have to | ||
// deserialize it and can potentially execute malicious code | ||
Method writeObjectMethod = RMQStreamMessage.class | ||
.getDeclaredMethod("writeObject", Object.class, boolean.class); | ||
writeObjectMethod.setAccessible(true); | ||
writeObjectMethod.invoke(message, value, true); | ||
|
||
queueSender.send(message); | ||
} finally { | ||
reconnect(Arrays.asList("java.lang", "com.rabbitmq.jms")); | ||
} | ||
|
||
queueConn.start(); | ||
QueueSession queueSession = queueConn.createQueueSession(false, Session.DUPS_OK_ACKNOWLEDGE); | ||
Queue queue = queueSession.createQueue(QUEUE_NAME); | ||
QueueReceiver queueReceiver = queueSession.createReceiver(queue); | ||
RMQStreamMessage m = (RMQStreamMessage) queueReceiver.receive(TEST_RECEIVE_TIMEOUT); | ||
MessageTestType.STREAM.check(m, null); | ||
assertEquals(m.readObject(), value); | ||
} | ||
|
||
@Test | ||
public void testReceiveStreamMessageWithPrimitiveValue() throws Exception { | ||
testReceiveStreamMessageWithValue(1024L); | ||
testReceiveStreamMessageWithValue("a string"); | ||
} | ||
|
||
@Test | ||
public void testReceiveStreamMessageWithTrustedValue() throws Exception { | ||
testReceiveStreamMessageWithValue(new TestMessages.TestSerializable(8, "An object")); | ||
} | ||
|
||
@Test | ||
public void testReceiveStreamMessageWithUntrustedValue1() throws Exception { | ||
// StreamMessage cannot be used with a Map, unless the sender uses a trick | ||
// this is to simulate an attack from the sender | ||
// Note: java.util is not on the trusted package list | ||
assertThrows(RMQJMSException.class, () -> { | ||
Map<String, String> m = new HashMap<String, String>(); | ||
m.put("key", "value"); | ||
testReceiveStreamMessageWithValue(m); | ||
}); | ||
} | ||
@Test | ||
public void testReceiveStreamMessageWithUntrustedValue2() throws Exception { | ||
// StreamMessage cannot be used with a Map, unless the sender uses a trick | ||
// this is to simulate an attack from the sender | ||
// java.awt is not on the trusted package list | ||
assertThrows(RMQJMSException.class, () -> { | ||
testReceiveStreamMessageWithValue(Color.WHITE); | ||
}); | ||
} | ||
|
||
protected void reconnect(java.util.List<String> trustedPackages) throws Exception { | ||
if (queueConn != null) { | ||
this.queueConn.close(); | ||
((RMQConnectionFactory) connFactory).setTrustedPackages(trustedPackages); | ||
this.queueConn = connFactory.createQueueConnection(); | ||
} else { | ||
fail("Cannot reconnect"); | ||
} | ||
} | ||
} | ||
|