Commit message: None
CVE: CVE-2017-15402
Vulnerability description: Using an ID that can be controlled by a compromised renderer which allows any frame to overwrite the page_state of any other frame in the same process in Navigation in Google Chrome on Chrome OS prior to 62.0.3202.74 allowed a remote attacker who had compromised the renderer process to potentially perform a sandbox escape via a crafted HTML page.

Project: Chrome

void NavigationControllerImpl::RendererDidNavigateNewSubframe(
    RenderFrameHostImpl* rfh,
    const FrameHostMsg_DidCommitProvisionalLoad_Params& params,
    bool is_same_document,
    bool replace_entry) {
  DCHECK(ui::PageTransitionCoreTypeIs(params.transition,
                                      ui::PAGE_TRANSITION_MANUAL_SUBFRAME));

  DCHECK(GetLastCommittedEntry()) << "ClassifyNavigation should guarantee "
                                  << "that a last committed entry exists.";

  scoped_refptr<FrameNavigationEntry> frame_entry(new FrameNavigationEntry(
      params.frame_unique_name, params.item_sequence_number,
      params.document_sequence_number, rfh->GetSiteInstance(), nullptr,
      params.url, params.referrer, params.method, params.post_id));
  std::unique_ptr<NavigationEntryImpl> new_entry =
      GetLastCommittedEntry()->CloneAndReplace(
          frame_entry.get(), is_same_document, rfh->frame_tree_node(),
          delegate_->GetFrameTree()->root());


  InsertOrReplaceEntry(std::move(new_entry), replace_entry);
}

Project: Chrome

void NavigationControllerImpl::RendererDidNavigateToNewPage(
    RenderFrameHostImpl* rfh,
    const FrameHostMsg_DidCommitProvisionalLoad_Params& params,
    bool is_same_document,
    bool replace_entry,
    NavigationHandleImpl* handle) {
  std::unique_ptr<NavigationEntryImpl> new_entry;
  bool update_virtual_url = false;

  if (is_same_document && GetLastCommittedEntry()) {
    FrameNavigationEntry* frame_entry = new FrameNavigationEntry(
        params.frame_unique_name, params.item_sequence_number,
        params.document_sequence_number, rfh->GetSiteInstance(), nullptr,
        params.url, params.referrer, params.method, params.post_id);
    new_entry = GetLastCommittedEntry()->CloneAndReplace(
        frame_entry, true, rfh->frame_tree_node(),
        delegate_->GetFrameTree()->root());
    if (new_entry->GetURL().GetOrigin() != params.url.GetOrigin()) {
      new_entry->GetSSL() = SSLStatus();

      if (params.url.SchemeIs(url::kHttpsScheme) && !rfh->GetParent() &&
          handle->GetNetErrorCode() == net::OK) {
        UMA_HISTOGRAM_BOOLEAN(
            "Navigation.SecureSchemeHasSSLStatus.NewPageInPageOriginMismatch",
            !!new_entry->GetSSL().certificate);
      }
    }

    CHECK(frame_entry->HasOneRef());

    update_virtual_url = new_entry->update_virtual_url_with_url();

    if (params.url.SchemeIs(url::kHttpsScheme) && !rfh->GetParent() &&
        handle->GetNetErrorCode() == net::OK) {
      UMA_HISTOGRAM_BOOLEAN("Navigation.SecureSchemeHasSSLStatus.NewPageInPage",
                            !!new_entry->GetSSL().certificate);
    }
  }

  if (!new_entry &&
      PendingEntryMatchesHandle(handle) && pending_entry_index_ == -1 &&
      (!pending_entry_->site_instance() ||
       pending_entry_->site_instance() == rfh->GetSiteInstance())) {
    new_entry = pending_entry_->Clone();

    update_virtual_url = new_entry->update_virtual_url_with_url();
    new_entry->GetSSL() = handle->ssl_status();

    if (params.url.SchemeIs(url::kHttpsScheme) && !rfh->GetParent() &&
        handle->GetNetErrorCode() == net::OK) {
      UMA_HISTOGRAM_BOOLEAN(
          "Navigation.SecureSchemeHasSSLStatus.NewPagePendingEntryMatches",
          !!new_entry->GetSSL().certificate);
    }
  }

  if (!new_entry) {
    new_entry = base::WrapUnique(new NavigationEntryImpl);

    GURL url = params.url;
    bool needs_update = false;
    BrowserURLHandlerImpl::GetInstance()->RewriteURLIfNecessary(
        &url, browser_context_, &needs_update);
    new_entry->set_update_virtual_url_with_url(needs_update);

    update_virtual_url = needs_update;
    new_entry->GetSSL() = handle->ssl_status();

    if (params.url.SchemeIs(url::kHttpsScheme) && !rfh->GetParent() &&
        handle->GetNetErrorCode() == net::OK) {
      UMA_HISTOGRAM_BOOLEAN(
          "Navigation.SecureSchemeHasSSLStatus.NewPageNoMatchingEntry",
          !!new_entry->GetSSL().certificate);
    }
  }

  new_entry->set_page_type(params.url_is_unreachable ? PAGE_TYPE_ERROR
                                                     : PAGE_TYPE_NORMAL);
  new_entry->SetURL(params.url);
  if (update_virtual_url)
    UpdateVirtualURLToURL(new_entry.get(), params.url);
  new_entry->SetReferrer(params.referrer);
  new_entry->SetTransitionType(params.transition);
  new_entry->set_site_instance(
      static_cast<SiteInstanceImpl*>(rfh->GetSiteInstance()));
  new_entry->SetOriginalRequestURL(params.original_request_url);
  new_entry->SetIsOverridingUserAgent(params.is_overriding_user_agent);

  FrameNavigationEntry* frame_entry =
      new_entry->GetFrameEntry(rfh->frame_tree_node());
  frame_entry->set_frame_unique_name(params.frame_unique_name);
  frame_entry->set_item_sequence_number(params.item_sequence_number);
  frame_entry->set_document_sequence_number(params.document_sequence_number);
  frame_entry->set_method(params.method);
  frame_entry->set_post_id(params.post_id);

  if (is_same_document && GetLastCommittedEntry()) {
    new_entry->SetTitle(GetLastCommittedEntry()->GetTitle());
    new_entry->GetFavicon() = GetLastCommittedEntry()->GetFavicon();
  }

  DCHECK(!params.history_list_was_cleared || !replace_entry);
  if (params.history_list_was_cleared) {
    DiscardNonCommittedEntriesInternal();
    entries_.clear();
    last_committed_entry_index_ = -1;
  }

  InsertOrReplaceEntry(std::move(new_entry), replace_entry);
}

Project: Chrome

ResourceRequest CreateXHRRequest(const char* url) {
  ResourceRequest request;
  request.method = "GET";
  request.url = GURL(url);
  request.referrer_policy = blink::kWebReferrerPolicyDefault;
  request.request_initiator = url::Origin();
  request.load_flags = 0;
  request.origin_pid = 0;
  request.resource_type = RESOURCE_TYPE_XHR;
  request.request_context = 0;
  request.appcache_host_id = kAppCacheNoHostId;
  request.download_to_file = false;
  request.should_reset_appcache = false;
  request.is_main_frame = true;
  request.parent_is_main_frame = false;
  request.parent_render_frame_id = -1;
  request.transition_type = ui::PAGE_TRANSITION_LINK;
  request.allow_download = true;
  return request;
}

Project: Chrome

ResourceRequest CreateXHRRequestWithOrigin(const char* origin) {
  ResourceRequest request = CreateXHRRequest("http://bar.com/simple_page.html");
  request.site_for_cookies = GURL(origin);
  request.headers.SetHeader(net::HttpRequestHeaders::kOrigin, origin);
  return request;
}

Project: Chrome

void OnHttpHeaderReceived(const std::string& header,
                          const std::string& value,
                          int child_process_id,
                          content::ResourceContext* resource_context,
                          OnHeaderProcessedCallback callback) {
  callback.Run(HeaderInterceptorResult::KILL);
}

Project: Chrome

RenderFrameHostImpl* PrepareToDuplicateHosts(Shell* shell,
                                             int* target_routing_id) {
  GURL foo("http://foo.com/simple_page.html");

  NavigateToURL(shell, foo);
  EXPECT_EQ(base::ASCIIToUTF16("OK"), shell->web_contents()->GetTitle());

  ShellAddedObserver shell2_observer;
  EXPECT_TRUE(ExecuteScript(shell, "window.open(document.URL + '#2');"));
  Shell* shell2 = shell2_observer.GetShell();

  EXPECT_EQ(shell->web_contents()->GetMainFrame()->GetProcess()->GetID(),
            shell2->web_contents()->GetMainFrame()->GetProcess()->GetID());
  *target_routing_id =
      shell2->web_contents()->GetRenderViewHost()->GetRoutingID();
  EXPECT_NE(*target_routing_id,
            shell->web_contents()->GetRenderViewHost()->GetRoutingID());

  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, false,
      nullptr, std::string(), Referrer(), WindowOpenDisposition::CURRENT_TAB,
      false, false, true, blink::WebTriggeringEventInfo::kFromTrustedEvent);

  RenderFrameHostImpl* next_rfh;
  if (IsBrowserSideNavigationEnabled())
    next_rfh = wc->GetRenderManagerForTesting()->speculative_frame_host();
  else
    next_rfh = wc->GetRenderManagerForTesting()->pending_frame_host();

  EXPECT_TRUE(next_rfh);
  EXPECT_NE(shell->web_contents()->GetRenderProcessHost()->GetID(),
            next_rfh->GetProcess()->GetID());

  return next_rfh;
}

Project: Chrome

void SecurityExploitBrowserTest::TestFileChooserWithPath(
    const base::FilePath& path) {
  GURL foo("http://foo.com/simple_page.html");
  NavigateToURL(shell(), foo);
  EXPECT_EQ(base::ASCIIToUTF16("OK"), shell()->web_contents()->GetTitle());

  RenderFrameHost* compromised_renderer =
      shell()->web_contents()->GetMainFrame();
  RenderProcessHostWatcher terminated(
      shell()->web_contents(),
      RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);

  FileChooserParams params;
  params.default_file_name = path;

  FrameHostMsg_RunFileChooser evil(compromised_renderer->GetRoutingID(),
                                   params);

  IpcSecurityTestUtil::PwnMessageReceived(
      compromised_renderer->GetProcess()->GetChannel(), evil);
  terminated.Wait();
}

Project: Chrome

void TryCreateDuplicateRequestIds(Shell* shell, bool block_loaders) {
  NavigateToURL(shell, GURL("http://foo.com/simple_page.html"));
  RenderFrameHost* rfh = shell->web_contents()->GetMainFrame();

  if (block_loaders) {
    rfh->BlockRequestsForFrame();
  }

  const char* blocking_url = net::URLRequestSlowDownloadJob::kUnknownSizeUrl;
  ResourceRequest request(CreateXHRRequest(blocking_url));

  RenderProcessHostWatcher process_killed(
      rfh->GetProcess(), RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
  IPC::IpcSecurityTestUtil::PwnMessageReceived(
      rfh->GetProcess()->GetChannel(),
      ResourceHostMsg_RequestResource(rfh->GetRoutingID(),
                                      kRequestIdNotPreviouslyUsed, request,
                                      net::MutableNetworkTrafficAnnotationTag(
                                          TRAFFIC_ANNOTATION_FOR_TESTS)));
  IPC::IpcSecurityTestUtil::PwnMessageReceived(
      rfh->GetProcess()->GetChannel(),
      ResourceHostMsg_RequestResource(rfh->GetRoutingID(),
                                      kRequestIdNotPreviouslyUsed, request,
                                      net::MutableNetworkTrafficAnnotationTag(
                                          TRAFFIC_ANNOTATION_FOR_TESTS)));
  process_killed.Wait();
}