Verificamos a vulnerabilidade fechada e obtemos quatro novos CVEs

Me pediram para continuar a série de artigos sobre o fechamento de vulnerabilidades e sobre como elas são fechadas (nosso primeiro artigo pode ser lido aqui ). Na última vez, descobrimos que, mesmo que o fabricante relate o fechamento da vulnerabilidade, na realidade tudo pode não ser assim.



Critérios de seleção:


Os critérios de seleção para as vulnerabilidades consideradas desta vez foram os mesmos (com a exceção de que desta vez eu queria examinar outros tipos de vulnerabilidades):

  • deve haver uma exploração - queremos ver que, antes da atualização, tudo foi mal explorado e, depois disso, ficou bom;
  • a vulnerabilidade deve ser crítica (idealmente RCE) e com uma pontuação alta;
  • o produto deve ser de código aberto;
  • o produto não deve ser abandonado e usado ativamente;
  • a vulnerabilidade deve ser relativamente nova;
  • como sempre, o principal é que nós mesmos estaríamos interessados.

O que e como eu escolhi:


Fui ao vulners.com e pedi para mostrar todas as façanhas com o exploit-db.com nas últimas semanas. Dessa vez, na categoria Web, quase todas as explorações foram criadas por Ihsan Sencan, mas devido ao fato de elas geralmente se relacionarem a injeções de sql em aplicativos e plugins antigos não suportados, eu os removi. Dos demais produtos, apenas a Ferramenta de Gerenciamento de Projetos ProjeQtOr 7.2.5, com vulnerabilidade CVE-2018-18924, se enquadrava na categoria "não abandonado e em desenvolvimento ativo".
Essa vulnerabilidade atendeu a todos os critérios de seleção:

  • existe uma façanha ;
  • Vulnerabilidade do RCE (embora exija que o usuário seja autorizado);
  • o produto é bastante aberto;
  • o produto não foi abandonado, em 2018 houve 28 lançamentos e apenas o sourceforge.net havia 702 downloads (e a maioria dos downloads de atualizações resolve o problema do CVE, o que provavelmente indica que as pessoas viram o CVE e começaram a atualizar);
  • CVE de 4 de novembro, uma exploração de 25 de outubro, atende aos requisitos de novidade;
  • Eu olhei para o problema e sua solução, fiquei interessado (mais sobre isso mais tarde).

Entendendo a ferramenta de gerenciamento de projetos ProjeQtOr


Lemos a descrição da exploração e a descrição do CVE em nist.gov , entendemos que a versão 7.2.5 é vulnerável apenas para um usuário autorizado. Além disso, você pode fazer upload de um arquivo .shtml como imagem, embora a mensagem de erro "Este arquivo não seja uma imagem válida" seja exibida, mas o arquivo ainda será salvo nas imagens no servidor e acessível por meio de um link direto no host / arquivos / images / nome_da_imagem .



O acesso via link direto é bom, mas aqui você ainda precisa adivinhar o nome com o qual o arquivo será baixado. Temos sorte e não é aleatória, mas é gerada a partir da hora atual no formato: ano, mês, dia, horas, minutos, segundos. O resultado é esse número 20181114140320. Em seguida, o ID do usuário e o nome do arquivo original passam pelo sublinhado. Existem algumas incógnitas:

  • Fuso horário no servidor
  • se o relógio no servidor estiver inoperante;
  • ID do usuário

E, novamente, temos sorte: se você enviar uma imagem válida, todos esses parâmetros serão relatados para nós. Não é difícil analisar várias opções de links (vários, já que existem segundos, mas é difícil entrar neles imediatamente).



Em geral, obter o nome do arquivo não é um problema. Nós seguimos em frente. E pensamos: por que não apenas enviar um script php? Estamos tentando fazer o download, a mesma janela aparece, mas o arquivo não aparece no diretório É hora de olhar para o código!

O script uploadImage.php é responsável pelo upload da imagem para o servidor na versão 7.2.5. Estamos interessados ​​nas linhas 100 a 117.

if (substr($ext,0,3)=='php' or substr($ext,0,4)=='phtm') { if(@!getimagesize($uploadedFile['tmp_name'])) { $error=i18n('errorNotAnImage'); } else { traceHack("Try to upload php file as image in CKEditor"); } } else { if ( ! move_uploaded_file($uploadedFile['tmp_name'], $uploadfile)) { $error = htmlGetErrorMessage(i18n('errorUploadFile','hacking ?')); errorLog(i18n('errorUploadFile','hacking ?')); } } } if (!$error) { if(@!getimagesize($uploadfile)) { $error=i18n('errorNotAnImage'); } } 

A linha 100 é responsável por verificar a extensão do arquivo: se for php ou phtm, o arquivo será descartado e não será salvo. Portanto, os arquivos php não aparecem no diretório "files / images /". A linha 115 cria o erro que vemos, mas não faz nada com o arquivo.

Bem, não vamos deixar a exploração e fazer upload do arquivo com a extensão .shtml. Aqui vale a pena fazer uma pequena digressão e dizer o que é .shtml e o que é comido.

SHTML e SSI


Definição da Wikipedia:

SSI (Server Side Include - inclusão no servidor) - uma linguagem simples para a "montagem" dinâmica de páginas da Web no servidor a partir dos componentes individuais e a entrega do documento HTML recebido ao cliente. Implementado no servidor da web Apache usando o módulo mod_include. O recurso incluído nas configurações padrão do servidor da Web permite incluir arquivos HTML; portanto, para usar as instruções, o arquivo deve terminar com a extensão .shtml, .stm ou .shtm.

Com suas próprias palavras:

SHTML é um HTML que pode executar conjuntos de instruções do lado do servidor. Das úteis, há uma função exec que executa comandos arbitrários no servidor (sim, podemos fazer o download do arquivo usando o código HTML e executá-lo).

Aqui está um código de amostra para executar um código arbitrário:

 <!--#exec cmd=”ls” --> 

A boa notícia é que essa funcionalidade não está ativada por padrão no servidor Apache2 e, para ativá-la, você precisa dançar com um pandeiro. Algumas horas depois de escolher a configuração, consegui fazer o retorno das variáveis ​​de ambiente funcionar, mas não o comando. Aqui está o meu código SSI:

 <html> <head> <title>thegeekstuff.com</title> </head> <body> <p> Today is <!--#echo var="DATE_LOCAL" --> <!--#exec cmd="ls" --> </p> </body> </html>  : Today is Wednesday, 14-Nov-2018 17:29:14 MSK [an error occurred while processing this directive] 

Se alguém lhe disser o que escrever na configuração para que funcione corretamente, eu adoraria lê-la.

Explorar vulnerabilidade


Se as estrelas convergirem, você poderá fazer o download do arquivo shtml e executar comandos arbitrários (ou, como eu, ver a hora no servidor).

Assistindo o patch


A próxima versão é 7.2.6, mas não há alterações em relação à vulnerabilidade em que estamos interessados ​​(nist.gov novamente enganado).

Observamos a versão 7.2.7 e parece que tudo está consertado (os próprios desenvolvedores dizem que tudo está consertado apenas nesta versão). Há duas alterações principais:

1. Entre as extensões proibidas, "shtm" foi adicionado (se os 4 primeiros caracteres forem assim, o shtml também cai aqui):

 if (substr($ext,0,3)=='php' or substr($ext,0,4)=='phtm' or substr($ext,0,4)=='shtm') { 


2. Arquivos que não são imagens agora são excluídos:

  if(@!getimagesize($uploadfile)) { $error=i18n('errorNotAnImage'); kill($uploadfile); } 

Parece que você pode divergir, porque as não imagens são excluídas e o shtml nem sequer tenta persistir. Mas eu sempre não gostei se eles tentassem resolver um problema com listas negras. Por exemplo, em alguns países, as empresas proíbem as redes sociais. Isso leva ao fato de os usuários começarem a usar os "espelhos" das redes sociais, onde seus nomes de usuário e senhas são roubados. Suas senhas coincidem com senhas corporativas, mas pode haver problemas muito maiores do que um funcionário folheando um instagram com uma xícara de café.

Na programação da web e em sua segurança, as listas negras também são más.

Ignore a lista negra do ProjeQtOr


Bem, tudo é simples. Primeiro, vamos ver quais arquivos o Apache2 + PHP pode interpretar nas configurações padrão (tudo foi instalado no ubuntu 16.04 com um repositório atualizado). A diretiva "FilesMatch" é responsável pela capacidade de interpretar arquivos. Nós fazemos uma pesquisa nele com o comando “grep -r" <FilesMatch "/ etc / apache2" e aqui está o resultado:

 /etc/apache2/mods-available/php7.0.conf:<FilesMatch ".+\.ph(p[3457]?|t|tml)$"> /etc/apache2/mods-available/php7.0.conf:<FilesMatch ".+\.phps$"> /etc/apache2/mods-available/php7.0.conf:<FilesMatch "^\.ph(p[3457]?|t|tml|ps)$"> /etc/apache2/sites-available/default-ssl.conf: <FilesMatch "\.(cgi|shtml|phtml|php)$"> /etc/apache2/apache2.conf:<FilesMatch "^\.ht"> 

Na configuração default-ssl.conf, todas as extensões são simplesmente listadas na íntegra; são elas: cgi, shtml, phtml, php. Infelizmente, tudo, exceto o cgi, é filtrado no ProjeQtOr.

A configuração do php7.0.conf é muito mais interessante, pois as extensões são definidas pela expressão regular. Temos:
ExtensãoO que é filtrado
phpsubstr ($ ext, 0.3) == 'php'
php3substr ($ ext, 0.3) == 'php'
php4substr ($ ext, 0.3) == 'php'
php5substr ($ ext, 0.3) == 'php'
php7substr ($ ext, 0.3) == 'php'
phtNADA
phtml3substr ($ ext, 0.4) == 'phtm'

Ótimo, uma extensão de arquivo que não é filtrada foi encontrada. Verificamos que é realmente interpretado.

Crie um arquivo test.pht com o seguinte conteúdo:

 <?php phpinfo(); 

Vamos a este arquivo no navegador e vemos informações sobre o php instalado. Notavelmente, a lista negra foi ignorada, enquanto nas configurações não padrão, por algum motivo, outras extensões de interpretação podem ser permitidas.

Carregamos nosso arquivo de teste na ferramenta de gerenciamento de projetos ProjeQtOr. Obviamente, obtemos um erro, porque isso não é uma imagem (na versão anterior à 7.2.7, já temos execução de código no servidor, porque mudar o phpinfo para executar comandos não é difícil). Na versão 7.2.7, o arquivo é excluído e o código não é executado.

Mas não estamos chateados e ignoramos a verificação da imagem.

Imagem PHP


A verificação se o arquivo baixado é uma imagem na Ferramenta de Gerenciamento de Projetos ProjeQtOr é feita pela função getimagesize, que simplesmente olha para o cabeçalho do arquivo transferido.

Aproveitando o fato de que pode haver qualquer lixo no arquivo php, e a interpretação do código php começa apenas com os caracteres “<? Php”, escrevemos um pequeno código que primeiro grava a imagem e depois o código php de que precisamos. Como imagem, enviaremos uma captura de tela da janela de erro. 3 linhas de código python e pronto:

 data = open ('test.png','rb').read() data += open ('test.pht','rb').read() open ('new_pht_png.pht','wb').write(data) 

Provavelmente, você pode simplesmente escrever um cabeçalho de imagem válido no início do arquivo, mas é mais fácil e, mais ainda, a imagem é exibida em qualquer visualizador.

Carregamos essa criação no servidor e, eis que ela é carregada (e exibida no visualizador como uma imagem), e também é bom mostrar o nome completo com o qual esse arquivo foi carregado.



Vamos para o arquivo baixado localhost / files / images / 20181114171730_1_new_pht_png.pht e vemos a imagem baixada como texto e a saída phpinfo abaixo dela. É claro que substituir o phpinfo por um simples shell da web não é difícil. Por exemplo, isto: <? Php system ($ _ GET ['cmd']);



Depois de começar a escolher downloads de arquivos, é necessário concluir o trabalho e ver onde mais há um download de arquivos com ou sem listas negras.

Outro upload de arquivo


Vamos assistir na versão mais recente disponível. Supondo que você use a mesma função para fazer upload de arquivos como antes, ou seja, move_uploaded_file, procuramos por ele no diretório do projeto “grep -r“ move_uploaded_file ”./”. Obtemos os seguintes 5 arquivos:

./tool/uploadImage.php
./tool/saveDocumentVersion.php
./tool/uploadPlugin.php
./tool/import.php
./tool/saveAttachment.php

Arquivo uploadImage.php - já olhou.
Arquivo saveDocumentVersion.php - baixa versões de documentos (como o nome indica). Estamos tentando fazer o download de um documento e analisá-lo (para começar, sempre carregaremos uma foto). Após o download, vemos que a extensão .1 é adicionada ao arquivo. Examinamos no código como o nome é obtido (isso é feito na linha 229):

 $uploadfile = $dv->getUploadFileName(); 

A função getUploadFileName é declarada no arquivo DocumentVersionMain.php. Lá, na linha 227, vemos que "." É adicionado ao nome retornado. e ID do documento. Não podemos contornar nem mesmo o ponto adicional:

 return $uploaddir . $paramPathSeparator . $fileName . '.' . $this->id; 

O arquivo uploadPlugin.php pode ser acessado apenas por administradores e o fato de o plug-in ter um código incorreto é muito lógico e difícil de se livrar sem a necessidade de inserir a validação do plug-in (como fazem o CMS popular). Obviamente, quando você tenta fazer o download de algo lá, ele carrega com sucesso e é executado.

O arquivo import.php também está disponível apenas para administradores. Ao baixar um arquivo, somos informados de que ele deve ser um arquivo csv ou xlsx. Obviamente, tentamos carregar o arquivo php e vemos um erro:

ERRO - O tipo de arquivo fornecido e o formato de arquivo selecionado não correspondem


Importação anulada

O problema é que, como no bug original do CVE, o arquivo não é excluído, mas permanece disponível em localhost / files / attach / import / import / test.php .

O arquivo saveAttachment é usado ao carregar anexos (por exemplo, ao carregar sua própria imagem). O script PHP não rastreia para lá, pois há uma proteção do formulário:

 if (substr($ext,0,3)=='php' or substr($ext,0,4)=='phtm' or substr($ext,0,4)=='shtm') { $attachment→fileName.=".projeqtor"; 

verifica-se nos arquivos de extensão php *, phtm *, shtm * a extensão ".projeqtor" é adicionada, ou seja, obviamente nosso arquivo pht rastreará até lá (mesmo sem rastrear as imagens). Tentamos e obtemos tudo no endereço localhost / files / attach / attachment_1 / test.pht .

Resultado total de cinco locais de download de arquivos encontrados rapidamente:

  • conseguiu carregar o script php ou pht 4 vezes;
  • a validação da lista negra é de dois;
  • a validação da lista branca não está em lugar nenhum;
  • uma vez falhou ao baixar o arquivo (falhou ao baixar, mas falhou ao executar), porque expansão estava mudando

Conclusões sobre a ferramenta de gerenciamento de projetos ProjeQtOr e o CVE-2018-18924



  • a vulnerabilidade relatada foi praticamente eliminada;
  • existem outras vulnerabilidades no código (relatadas aos desenvolvedores e eles até prometeram listas brancas de extensões);
  • a configuração adequada do servidor Apache2 pode nos salvar de tudo (limitar os formatos executáveis ​​apenas ao necessário, proibir a execução de scripts nas pastas do usuário);
  • O nist.gov não contém a versão vulnerável mais recente.

Nota de amante


  • recusar listas negras sempre que possível (não sei onde isso é impossível);
  • tenha cuidado e atenção ao processar os arquivos baixados (é melhor que ele esteja em um só lugar e não se espalhe por 5);
  • um servidor da Web configurado corretamente evita muitos problemas no código do projeto (é importante escrever o código e configurar bem o servidor).

Resposta detalhada dos desenvolvedores


A primeira resposta dos desenvolvedores foi algo como "consertamos tudo, então veja o código corrigido". Eu tive que pintar detalhadamente onde estão alguns problemas e como eles podem ser explorados.

Então ele recebeu uma resposta detalhada: “Sim, existem problemas e eles serão corrigidos na versão 7.3.0. Também serão adicionadas listas brancas para imagens para xlslx e csv. ” Eles também escreveram que têm uma recomendação para adicionar os diretórios "anexos" e "documentos" fora do acesso à Web nas instruções de instalação.

Os desenvolvedores me permitiram registrar o CVE e escrever um artigo após a atualização (que saiu e está disponível para download ).

Conclusão


Como eu escrevi no começo, algumas pessoas baixaram a atualização que decidiu o CVE (mais de 500 downloads), e é legal que as pessoas atualizem seu software vulnerável, mas é triste que o software permaneça vulnerável.

Como resultado, quatro CVEs foram atribuídas a mim e à nossa empresa: CVE-2018-19307, CVE-2018-19308, CVE-2018-19309, CVE-2018-19310.

Source: https://habr.com/ru/post/pt430408/


All Articles