Preventing TURN redirects to loopback addresses.

This can be used for a certain security exploit, and doesn't have any
other practical applications we know of.

BUG=chromium:649118

Review-Url: https://codereview.webrtc.org/2440043004
Cr-Commit-Position: refs/heads/master@{#14751}
This commit is contained in:
deadbeef 2016-10-24 13:15:59 -07:00 committed by Commit bot
parent 838cdb3db6
commit fb70b45030
2 changed files with 77 additions and 0 deletions

View File

@ -639,6 +639,14 @@ bool TurnPort::SetAlternateServer(const rtc::SocketAddress& address) {
return false;
}
// Block redirects to a loopback address.
// See: https://bugs.chromium.org/p/chromium/issues/detail?id=649118
if (address.IsLoopbackIP()) {
LOG_J(LS_WARNING, this)
<< "Blocking attempted redirect to loopback address.";
return false;
}
LOG_J(LS_INFO, this) << "Redirecting from TURN server ["
<< server_address_.address.ToSensitiveString()
<< "] to TURN server ["

View File

@ -29,6 +29,7 @@
#include "webrtc/base/helpers.h"
#include "webrtc/base/logging.h"
#include "webrtc/base/physicalsocketserver.h"
#include "webrtc/base/socketadapters.h"
#include "webrtc/base/socketaddress.h"
#include "webrtc/base/ssladapter.h"
#include "webrtc/base/thread.h"
@ -441,6 +442,57 @@ class TurnPortTest : public testing::Test,
ASSERT_EQ(0U, turn_port_->Candidates().size());
}
// A certain security exploit works by redirecting to a loopback address,
// which doesn't ever actually make sense. So redirects to loopback should
// be treated as errors.
// See: https://bugs.chromium.org/p/chromium/issues/detail?id=649118
void TestTurnAlternateServerLoopback(ProtocolType protocol_type, bool ipv6) {
const SocketAddress& local_address = ipv6 ? kLocalIPv6Addr : kLocalAddr1;
const SocketAddress& server_address =
ipv6 ? kTurnIPv6IntAddr : kTurnIntAddr;
std::vector<rtc::SocketAddress> redirect_addresses;
SocketAddress loopback_address(ipv6 ? "::1" : "127.0.0.1",
TURN_SERVER_PORT);
redirect_addresses.push_back(loopback_address);
// Make a socket and bind it to the local port, to make extra sure no
// packet is sent to this address.
std::unique_ptr<rtc::Socket> loopback_socket(ss_->CreateSocket(
protocol_type == PROTO_UDP ? SOCK_DGRAM : SOCK_STREAM));
ASSERT_NE(nullptr, loopback_socket.get());
ASSERT_EQ(0, loopback_socket->Bind(loopback_address));
if (protocol_type == PROTO_TCP) {
ASSERT_EQ(0, loopback_socket->Listen(1));
}
TestTurnRedirector redirector(redirect_addresses);
turn_server_.AddInternalSocket(server_address, protocol_type);
turn_server_.set_redirect_hook(&redirector);
CreateTurnPort(local_address, kTurnUsername, kTurnPassword,
ProtocolAddress(server_address, protocol_type));
turn_port_->PrepareAddress();
EXPECT_TRUE_SIMULATED_WAIT(
turn_error_,
(protocol_type == PROTO_TCP ? kSimulatedRtt * 3 : kSimulatedRtt * 2),
fake_clock_);
// Wait for some extra time, and make sure no packets were received on the
// loopback port we created (or in the case of TCP, no connection attempt
// occurred).
SIMULATED_WAIT(false, kSimulatedRtt, fake_clock_);
if (protocol_type == PROTO_UDP) {
char buf[1];
EXPECT_EQ(-1, loopback_socket->Recv(&buf, 1, nullptr));
} else {
std::unique_ptr<rtc::Socket> accepted_socket(
loopback_socket->Accept(nullptr));
EXPECT_EQ(nullptr, accepted_socket.get());
}
}
void TestTurnConnection(ProtocolType protocol_type) {
// Create ports and prepare addresses.
PrepareTurnAndUdpPorts(protocol_type);
@ -917,6 +969,23 @@ TEST_F(TurnPortTest, TestTurnAlternateServerDetectRepetitionTCP) {
TestTurnAlternateServerDetectRepetition(PROTO_TCP);
}
// Test catching the case of a redirect to loopback.
TEST_F(TurnPortTest, TestTurnAlternateServerLoopbackUdpIpv4) {
TestTurnAlternateServerLoopback(PROTO_UDP, false);
}
TEST_F(TurnPortTest, TestTurnAlternateServerLoopbackUdpIpv6) {
TestTurnAlternateServerLoopback(PROTO_UDP, true);
}
TEST_F(TurnPortTest, TestTurnAlternateServerLoopbackTcpIpv4) {
TestTurnAlternateServerLoopback(PROTO_TCP, false);
}
TEST_F(TurnPortTest, TestTurnAlternateServerLoopbackTcpIpv6) {
TestTurnAlternateServerLoopback(PROTO_TCP, true);
}
// Do a TURN allocation and try to send a packet to it from the outside.
// The packet should be dropped. Then, try to send a packet from TURN to the
// outside. It should reach its destination. Finally, try again from the