
Exploit para a vulnerabilidade zero-click do Outlook 2019 CVE-2020-1349, usando bugs de análise de cabeçalho MIME para alcançar heap overflow e controle de EIP via sobrescrita de vftable.
Esta vulnerabilidade ocorre no Outlook 2019 (16.0.12624.20424) instalado no Windows 10 1909 x64 coreano, página de código 949.

Não consegui contornar o ASLR usando essa vulnerabilidade, mas escrevi um exploit para Outlook 2019 32 bits que abre uma calculadora usando o endereço de uma biblioteca do sistema que é fixada na inicialização. E esta vulnerabilidade é uma vulnerabilidade de zero clique. Se a conta de um serviço de e-mail que não seja a conta "outlook.com" estiver vinculada ao Outlook 2019 usando IMAP, ela é acionada apenas pelo recebimento de e-mail.
v26 = IsDBCSLeadByte(*v13);
v12 = 0;
if ( v26 )
{
LODWORD(v5) = v5 - 1;
*v11++ = *v13++;
--v8;
}
LODWORD(v5) = v5 - 1;
*v11++ = *v13++;
--v8;
goto LABEL_51;
[OUTLMIME!CloseAllSockets+0x50804 Pseudo code]
O crash ocorre, e a parte onde a vulnerabilidade existe é como acima. Quando a string do cabeçalho To ou From analisado é Multibyte, a variável que conta as strings restantes a serem analisadas é diminuída em 2, e ocorre um estouro de inteiro (underflow) ao diminuir a variável em 2 quando a variável é 1.
if ( v23 != '=' )
{
if ( v23 == 34 )
v15 = 6;
goto LABEL_48;
}
v15 = 1;
}
if ( v15 )
goto LABEL_40;
[OUTLMIME!CloseAllSockets+0x50804 Pseudo code]
LABEL_40:
v24 = v13[v14];
if ( v24 )
{
if ( v5 == v14 )
goto LABEL_52;
if ( v8 != v14 )
{
v25 = IsDBCSLeadByte(v24);
v12 = 0;
if ( v25 )
++v14;
++v14;
}
}
goto LABEL_51;
}
[OUTLMIME!CloseAllSockets+0x50804 Pseudo code]
LABEL_51:
if ( v5 <= v14 )
goto EXIT_0;
}
[OUTLMIME!CloseAllSockets+0x50804 Pseudo code]
if ( v15 )
{
if ( v15 == 1 )
{
v15 = 2;
v21 = v13[v14];
v22 = v14 + 1;
if ( v21 != '?' )
v22 = v14;
if ( v21 != '?' )
v15 = v12;
v14 = v22;
}
[OUTLMIME!CloseAllSockets+0x50804 Pseudo code]
else if ( v15 > 1 )
{
if ( v15 <= 3 )
{
v18 = v13[v14];
v19 = v14 + 1;
if ( v18 != '?' )
v19 = v14;
v14 = v19;
v20 = v15 + 1;
if ( v18 != '?' )
v20 = v15;
v15 = v20;
}
[OUTLMIME!CloseAllSockets+0x50804 Pseudo code]
if ( v15 == 4 )
{
if ( v13[v14] == '?' )
v15 = 5;
LABEL_40:
v24 = v13[v14];
if ( v24 )
{
if ( v5 == v14 )
goto LABEL_52;
if ( v8 != v14 )
{
v25 = IsDBCSLeadByte(v24);
v12 = 0;
if ( v25 )
++v14;
++v14;
}
}
goto LABEL_51;
}
[OUTLMIME!CloseAllSockets+0x50804 Pseudo code]
if ( v15 == 5 )
{
v16 = v14;
v17 = v14 + 1;
v30 = v13[v16];
if ( v30 == '=' )
{
*v11++ = 34;
--v8;
}
memcpy(v11, v13, v17);
v13 += v17;
v11 += v17;
LODWORD(v5) = v5 - v17;
v8 -= v17;
if ( v30 == '=' )
{
*v11++ = 34;
--v8;
}
v12 = 0;
v14 = 0;
v15 = 0;
goto LABEL_51;
}
[OUTLMIME!CloseAllSockets+0x50804 Pseudo code]
else
{
v23 = v13[v14];
if ( v23 != '=' )
{
if ( v23 == '"' )
v15 = 6;
goto LABEL_48;
}
v15 = 1;
}
if ( v15 )
goto LABEL_40;
[OUTLMIME!CloseAllSockets+0x50804 Pseudo code]
LABEL_48:
v26 = IsDBCSLeadByte(*v13);
v12 = 0;
if ( v26 )
{
LODWORD(v5) = v5 - 1;
*v11++ = *v13++;
--v8;
}
LODWORD(v5) = v5 - 1;
*v11++ = *v13++;
--v8;
goto LABEL_51;
[OUTLMIME!CloseAllSockets+0x50804 Pseudo code]
No entanto, ao acionar um estouro de heap usando o bug acima, não consegui satisfazer a condição de sair do loop novamente, então não consegui realizar a sobrescrição parcial pelo tempo que quis. Então tive que encontrar uma entrada que satisfizesse uma condição específica para sair do loop. Pela mesma razão acima, auditei rigorosamente a parte do código ao redor, e ao invés de encontrar o valor de entrada que pode escapar do loop, o bug de cair em um loop infinito sem aumentar o índice ou escrever ao processar uma string especial.

Após realizar heap spray e heap feng shui, o sequestro de EIP foi bem-sucedido ao sobrescrever a vftable do objeto em execução em outra thread que não foi capturada em um loop infinito usando o bug acima.