diff options
author | nick <nick@chromium.org> | 2014-11-13 20:11:49 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2014-11-14 04:13:06 +0000 |
commit | 4c8dfd482ad484f2402a5d9bea921870f3ea5d89 (patch) | |
tree | 539588cc49bc86d99d61391c3ac468c41eb55bda | |
parent | f0ba8deca9141fc42e0a9746395f6843541bcde0 (diff) | |
download | chromium_src-4c8dfd482ad484f2402a5d9bea921870f3ea5d89.zip chromium_src-4c8dfd482ad484f2402a5d9bea921870f3ea5d89.tar.gz chromium_src-4c8dfd482ad484f2402a5d9bea921870f3ea5d89.tar.bz2 |
IPC: a way for security exploit browsertests to simulate the appearance of a malicious IPC.
Use this to add a SecurityExploitBrowserTest for http://crbug.com/429922
Fix SecurityExploitBrowserTests on Android. Re-enable SecurityExploitBrowserTests on Android, except for two issues (1) the new test, which is actually disabled because of http://crbug.com/432737, discovered while developing this CL and (2) SetWebUIProperty, which is disabled because of http://crbug.com/433068, also discovered while developing this CL. Moral of the story being: never try.
BUG=429922,432737,338023,433068
TEST=content_browsertests
Review URL: https://codereview.chromium.org/712713002
Cr-Commit-Position: refs/heads/master@{#304170}
-rw-r--r-- | build/android/pylib/gtest/filter/content_browsertests_disabled | 9 | ||||
-rw-r--r-- | content/browser/security_exploit_browsertest.cc | 127 | ||||
-rw-r--r-- | ipc/ipc.gyp | 2 | ||||
-rw-r--r-- | ipc/ipc_channel_proxy.h | 4 | ||||
-rw-r--r-- | ipc/ipc_security_test_util.cc | 25 | ||||
-rw-r--r-- | ipc/ipc_security_test_util.h | 40 |
6 files changed, 190 insertions, 17 deletions
diff --git a/build/android/pylib/gtest/filter/content_browsertests_disabled b/build/android/pylib/gtest/filter/content_browsertests_disabled index 748f680..0314f5f 100644 --- a/build/android/pylib/gtest/filter/content_browsertests_disabled +++ b/build/android/pylib/gtest/filter/content_browsertests_disabled @@ -23,11 +23,6 @@ GpuPixelBrowserTest.* FileSystemBrowserTestWithLowQuota.QuotaTest ChildProcessSecurityPolicyInProcessBrowserTest.NoLeak -# Needs to start the test server before -# BrowserTestBase::SetUpCommandLine is called, but on Android -# the test server needs to be started in SetUpOnMainThread(). -SecurityExploitBrowserTest.SetWebUIProperty - # Plugins are not supported. BrowserPluginThreadedCompositorPixelTest.* BrowserPluginHostTest.* @@ -108,10 +103,6 @@ TouchActionBrowserTest.* # http://crbug.com/338408 TracingControllerTest.EnableCaptureAndDisableMonitoring -# http://crbug.com/338023 -SecurityExploitBrowserTest.AttemptDuplicateRenderViewHost -SecurityExploitBrowserTest.AttemptDuplicateRenderWidgetHost - # http://crbug.com/338411 FrameTreeBrowserTest.NavigateWithLeftoverFrames diff --git a/content/browser/security_exploit_browsertest.cc b/content/browser/security_exploit_browsertest.cc index 2a1be07..68430ad 100644 --- a/content/browser/security_exploit_browsertest.cc +++ b/content/browser/security_exploit_browsertest.cc @@ -4,14 +4,18 @@ #include "base/command_line.h" #include "base/containers/hash_tables.h" +#include "base/strings/utf_string_conversions.h" #include "content/browser/dom_storage/dom_storage_context_wrapper.h" #include "content/browser/dom_storage/session_storage_namespace_impl.h" #include "content/browser/frame_host/navigator.h" #include "content/browser/renderer_host/render_view_host_factory.h" #include "content/browser/renderer_host/render_view_host_impl.h" #include "content/browser/web_contents/web_contents_impl.h" +#include "content/common/frame_messages.h" #include "content/common/view_messages.h" #include "content/public/browser/browser_context.h" +#include "content/public/browser/interstitial_page.h" +#include "content/public/browser/interstitial_page_delegate.h" #include "content/public/browser/storage_partition.h" #include "content/public/common/content_switches.h" #include "content/public/test/browser_test_utils.h" @@ -19,6 +23,11 @@ #include "content/public/test/content_browser_test_utils.h" #include "content/public/test/test_utils.h" #include "content/shell/browser/shell.h" +#include "ipc/ipc_security_test_util.h" +#include "net/dns/mock_host_resolver.h" +#include "net/test/embedded_test_server/embedded_test_server.h" + +using IPC::IpcSecurityTestUtil; namespace content { @@ -35,10 +44,11 @@ namespace { // the attempt is the return value. RenderViewHostImpl* PrepareToDuplicateHosts(Shell* shell, int* target_routing_id) { - GURL foo("http://foo.com/files/simple_page.html"); + GURL foo("http://foo.com/simple_page.html"); // Start off with initial navigation, so we get the first process allocated. NavigateToURL(shell, foo); + EXPECT_EQ(base::ASCIIToUTF16("OK"), shell->web_contents()->GetTitle()); // Open another window, so we generate some more routing ids. ShellAddedObserver shell2_observer; @@ -55,7 +65,7 @@ RenderViewHostImpl* PrepareToDuplicateHosts(Shell* shell, shell->web_contents()->GetRenderViewHost()->GetRoutingID()); // Now, simulate a link click coming from the renderer. - GURL extension_url("https://bar.com/files/simple_page.html"); + GURL extension_url("https://bar.com/simple_page.html"); WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell->web_contents()); wc->GetFrameTree()->root()->navigator()->RequestOpenURL( wc->GetFrameTree()->root()->current_frame_host(), extension_url, @@ -84,25 +94,37 @@ RenderViewHostImpl* PrepareToDuplicateHosts(Shell* shell, class SecurityExploitBrowserTest : public ContentBrowserTest { public: SecurityExploitBrowserTest() {} + void SetUpCommandLine(CommandLine* command_line) override { - ASSERT_TRUE(test_server()->Start()); + ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); // Add a host resolver rule to map all outgoing requests to the test server. // This allows us to use "real" hostnames in URLs, which we can use to // create arbitrary SiteInstances. command_line->AppendSwitchASCII( switches::kHostResolverRules, - "MAP * " + test_server()->host_port_pair().ToString() + + "MAP * " + + net::HostPortPair::FromURL(embedded_test_server()->base_url()) + .ToString() + ",EXCLUDE localhost"); } }; +// Fails because the process kill on Android does not succeed. +// http://crbug.com/433068 +#if defined(OS_ANDROID) +#define MAYBE_SetWebUIProperty DISABLED_SetWebUIProperty +#else +#define MAYBE_SetWebUIProperty SetWebUIProperty +#endif + // Ensure that we kill the renderer process if we try to give it WebUI // properties and it doesn't have enabled WebUI bindings. -IN_PROC_BROWSER_TEST_F(SecurityExploitBrowserTest, SetWebUIProperty) { - GURL foo("http://foo.com/files/simple_page.html"); +IN_PROC_BROWSER_TEST_F(SecurityExploitBrowserTest, MAYBE_SetWebUIProperty) { + GURL foo("http://foo.com/simple_page.html"); NavigateToURL(shell(), foo); + EXPECT_EQ(base::ASCIIToUTF16("OK"), shell()->web_contents()->GetTitle()); EXPECT_EQ(0, shell()->web_contents()->GetRenderViewHost()->GetEnabledBindings()); @@ -166,4 +188,97 @@ IN_PROC_BROWSER_TEST_F(SecurityExploitBrowserTest, // If the above operation doesn't crash, the test has succeeded! } +class SecurityExploitTestInterstitialPage : public InterstitialPageDelegate { + public: + explicit SecurityExploitTestInterstitialPage(WebContents* contents) { + InterstitialPage* interstitial = InterstitialPage::Create( + contents, true, contents->GetLastCommittedURL(), this); + interstitial->Show(); + } + + // InterstitialPageDelegate implementation. + void CommandReceived(const std::string& command) override { + last_command_ = command; + } + + std::string GetHTMLContents() override { + return "<html><head><script>" + "window.domAutomationController.setAutomationId(1);" + "window.domAutomationController.send(\"okay\");" + "</script></head>" + "<body>this page is an interstitial</body></html>"; + } + + std::string last_command() { return last_command_; } + + private: + std::string last_command_; + DISALLOW_COPY_AND_ASSIGN(SecurityExploitTestInterstitialPage); +}; + +// Fails due to InterstitialPage's reliance on PostNonNestableTask +// http://crbug.com/432737 +#if defined(OS_ANDROID) +#define MAYBE_InterstitialCommandFromUnderlyingContent \ + DISABLED_InterstitialCommandFromUnderlyingContent +#else +#define MAYBE_InterstitialCommandFromUnderlyingContent \ + InterstitialCommandFromUnderlyingContent +#endif + +// The interstitial should not be controllable by the underlying content. +IN_PROC_BROWSER_TEST_F(SecurityExploitBrowserTest, + MAYBE_InterstitialCommandFromUnderlyingContent) { + // Start off with initial navigation, to allocate the process. + GURL foo("http://foo.com/simple_page.html"); + NavigateToURL(shell(), foo); + EXPECT_EQ(base::ASCIIToUTF16("OK"), shell()->web_contents()->GetTitle()); + + DOMMessageQueue message_queue; + + // Install and show an interstitial page. + SecurityExploitTestInterstitialPage* interstitial = + new SecurityExploitTestInterstitialPage(shell()->web_contents()); + + ASSERT_EQ("", interstitial->last_command()); + content::WaitForInterstitialAttach(shell()->web_contents()); + + InterstitialPage* interstitial_page = + shell()->web_contents()->GetInterstitialPage(); + ASSERT_TRUE(interstitial_page != NULL); + ASSERT_TRUE(shell()->web_contents()->ShowingInterstitialPage()); + ASSERT_TRUE(interstitial_page->GetDelegateForTesting() == interstitial); + + // The interstitial page ought to be able to send a message. + std::string message; + ASSERT_TRUE(message_queue.WaitForMessage(&message)); + ASSERT_EQ("\"okay\"", message); + ASSERT_EQ("\"okay\"", interstitial->last_command()); + + // Send an automation message from the underlying content and wait for it to + // be dispatched on this thread. This message should not be received by the + // interstitial. + content::RenderFrameHost* compromised_renderer = + shell()->web_contents()->GetMainFrame(); + FrameHostMsg_DomOperationResponse evil(compromised_renderer->GetRoutingID(), + "evil", MSG_ROUTING_NONE); + IpcSecurityTestUtil::PwnMessageReceived( + compromised_renderer->GetProcess()->GetChannel(), evil); + + ASSERT_TRUE(message_queue.WaitForMessage(&message)); + ASSERT_EQ("evil", message) + << "Automation message should be received by WebContents."; + ASSERT_EQ("\"okay\"", interstitial->last_command()) + << "Interstitial should not be affected."; + + // Send a second message from the interstitial page, and make sure that the + // "evil" message doesn't arrive in the intervening period. + ASSERT_TRUE(content::ExecuteScript( + interstitial_page->GetRenderViewHostForTesting(), + "window.domAutomationController.send(\"okay2\");")); + ASSERT_TRUE(message_queue.WaitForMessage(&message)); + ASSERT_EQ("\"okay2\"", message); + ASSERT_EQ("\"okay2\"", interstitial->last_command()); +} + } // namespace content diff --git a/ipc/ipc.gyp b/ipc/ipc.gyp index 28c112b..cd63b54 100644 --- a/ipc/ipc.gyp +++ b/ipc/ipc.gyp @@ -132,6 +132,8 @@ 'ipc_multiprocess_test.h', 'ipc_perftest_support.cc', 'ipc_perftest_support.h', + 'ipc_security_test_util.cc', + 'ipc_security_test_util.h', 'ipc_test_base.cc', 'ipc_test_base.h', 'ipc_test_channel_listener.cc', diff --git a/ipc/ipc_channel_proxy.h b/ipc/ipc_channel_proxy.h index dda5fa5..9ef8bf3 100644 --- a/ipc/ipc_channel_proxy.h +++ b/ipc/ipc_channel_proxy.h @@ -177,7 +177,7 @@ class IPC_EXPORT ChannelProxy : public Sender, public base::NonThreadSafe { private: friend class ChannelProxy; - friend class SendCallbackHelper; + friend class IpcSecurityTestUtil; // Create the Channel void CreateChannel(scoped_ptr<ChannelFactory> factory); @@ -225,7 +225,7 @@ class IPC_EXPORT ChannelProxy : public Sender, public base::NonThreadSafe { Context* context() { return context_.get(); } private: - friend class SendCallbackHelper; + friend class IpcSecurityTestUtil; // By maintaining this indirection (ref-counted) to our internal state, we // can safely be destroyed while the background thread continues to do stuff diff --git a/ipc/ipc_security_test_util.cc b/ipc/ipc_security_test_util.cc new file mode 100644 index 0000000..4ae5a06 --- /dev/null +++ b/ipc/ipc_security_test_util.cc @@ -0,0 +1,25 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ipc/ipc_security_test_util.h" + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/run_loop.h" +#include "ipc/ipc_channel_proxy.h" + +namespace IPC { + +void IpcSecurityTestUtil::PwnMessageReceived(ChannelProxy* channel, + const IPC::Message& message) { + base::RunLoop run_loop; + base::Closure inject_message = base::Bind( + base::IgnoreResult(&IPC::ChannelProxy::Context::OnMessageReceived), + channel->context(), message); + channel->context()->ipc_task_runner()->PostTaskAndReply( + FROM_HERE, inject_message, run_loop.QuitClosure()); + run_loop.Run(); +} + +} // namespace IPC diff --git a/ipc/ipc_security_test_util.h b/ipc/ipc_security_test_util.h new file mode 100644 index 0000000..1ec2555 --- /dev/null +++ b/ipc/ipc_security_test_util.h @@ -0,0 +1,40 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IPC_IPC_SECURITY_TEST_UTIL_H_ +#define IPC_IPC_SECURITY_TEST_UTIL_H_ + +#include "base/basictypes.h" + +namespace IPC { + +class ChannelProxy; +class Message; + +class IpcSecurityTestUtil { + public: + // Enables testing of security exploit scenarios where a compromised child + // process can send a malicious message of an arbitrary type. + // + // This function will post the message to the IPC channel's thread, where it + // is offered to the channel's listeners. Afterwards, a reply task is posted + // back to the current thread. This function blocks until the reply task is + // received. For messages forwarded back to the current thread, we won't + // return until after the message has been handled here. + // + // Use this only for testing security bugs in a browsertest; other uses are + // likely perilous. Unit tests should be using IPC::TestSink which has an + // OnMessageReceived method you can call directly. Non-security browsertests + // should just exercise the child process's normal codepaths to send messages. + static void PwnMessageReceived(ChannelProxy* channel, const Message& message); + + private: + IpcSecurityTestUtil(); // Not instantiable. + + DISALLOW_COPY_AND_ASSIGN(IpcSecurityTestUtil); +}; + +} // namespace IPC + +#endif // IPC_IPC_SECURITY_TEST_UTIL_H_ |