CVE-2019-17059: Explicado-RCE no Cyberoam da Sophos, explicado

Trabalhamos duro com pesquisadores de segurança interna e externa aqui no TheBestVPN para descobrir brechas sérias e exploráveis ​​remotamente em VPNs SSL e firewalls como VPNs Cyberoam, Fortigate e Cisco. Este artigo é um guia técnico sobre uma vulnerabilidade crítica corrigida que afeta o Cyberoam SSL VPN, também conhecido como CyberoamOS.

Essa exploração do Cyberoam, chamada CVE-2019-17059, é uma vulnerabilidade crítica que permite que os invasores acessem seu dispositivo Cyberoam sem fornecer qualquer nome de usuário ou senha. Além disso, o acesso concedido é o nível mais alto (raiz), o que essencialmente concede a um invasor direitos ilimitados no seu dispositivo Cyberoam.

Na maioria dos ambientes de rede, os dispositivos Cyberoam são usados ​​como firewalls e gateways SSL VPN. Isso dá a um invasor em potencial uma posição forte em uma rede. Isso facilita o ataque de hosts dentro da rede e, como os dispositivos Cyberoam geralmente são confiáveis ​​na maioria dos ambientes, isso oferece uma vantagem extra ao invasor.

De acordo com a Shodan (um mecanismo de busca de dispositivos conectados à Internet), existem mais de 96.000 dispositivos Cyberoam voltados para a Internet de todo o mundo. A maioria desses dispositivos é instalada em empresas, universidades e alguns em bancos de renome mundial. Isso leva a ataques com enormes impactos nesses ambientes.

Trabalhar com a equipe de segurança da Sophos foi um grande prazer, pois eles agiram rapidamente, reconhecendo e lançando patches apenas alguns dias após nosso relatório inicial. Parabéns a eles! (trocadilho!)


detectando o Cyberoam

E como a maioria dessas entidades são alvos atraentes para os invasores, isso torna os bugs ainda mais críticos.

Execução remota de comandos de raiz não autenticada do CyberoamOS

O CyberoamOS é um sistema operacional baseado em Linux modificado para dispositivos Cyberoam. Este sistema operacional possui uma interface de configuração baseada na Web e um portal SSLVPN.

A interface da web é dividida em duas partes principais:

  • Um front-end escrito em Java
  • Um back-end que usa uma combinação de C e Perl

Não vamos nos aprofundar nas partes internas do código de front-end ou back-end, principalmente para economizar tempo e limitar a quantidade de informações reveladas. Mas discutiremos brevemente como o bug é acionado.

As interfaces de configuração e SSLVPN têm um servlet que manipula as operações principais. Essas operações são definidas usando um parâmetro chamado "mode".

A maioria deles é autenticada. Mas existem algumas operações que podemos acessar sem autenticação (como login).

Os erros que encontramos estão no módulo antivírus / antispam de email. O modo de solicitação para este terminal (módulo, op) é 458.

Uma coisa a observar é que os códigos de operação são mapeados para seus nomes no banco de dados Cyberoam (banco de dados interno Postgres). Ao procurar 458, podemos descobrir qual é o nome desse opcode.

Aqui está uma linha do script SQL de inicialização do banco de dados mostrando o nome opcode 458:

insira no tblcrevent (opcode, descrição, modo, requesttype)
valores ('RELEASEQUARANTINEMAILFROMMAIL', 'RELEASE QUARANTINE MAIL FROM MAIL', '458', 2);

As funções do opcode são armazenadas no diretório / _conf / csc / cscconf /. Não revelaremos todo o código da função vulnerável, mas forneceremos alguns trechos mostrando onde e como o bug ocorre.

Um código do front-end Java que lida com o opcode 458:

if ((jsonObject.getString ("hdnSender").é igual a("") ||
validateEmail (jsonObject.getString ("hdnSender"))) &&
validateEmail (jsonObject.getString ("hdnRecipient"))) &&
isSafeFilePath (jsonObject.getString ("hdnFilePath"))) && b) {
httpServletResponse.setContentType ("text / html");
CyberoamLogger.debug ("Antivírus / AntiSpam", "Valor constante do CSC " +
CSCConstants.isCCC);

Como você pode ver acima, alguns parâmetros são verificados quanto à validade. Se forem valores válidos, acontece o seguinte:

eventBean final eventByMode = EventBean.getEventByMode (363);
... redigido.
final int sendWizardEvent = cscClient.sendWizardEvent (eventByMode, hashMap, sqlReader);

Como podemos ver acima, temos um novo código de evento (363) que será enviado ao back-end. O bug que descobrimos está no código que lida com isso no back-end.

O opcode é chamado sendmail e, para evitar a exploração desse bug, redigiremos a maior parte do código do código a seguir.

O manipulador de opcode para send_mail.

...redigido ...

$ param = $ request->{liberação};
param = DLOPEN (base64_decode, param)
Applog LOG " Decodificar valores :: $ param \ n"
% requestData = split (/ [&=] /, $ param);
$ mailServerHost = $ requestData {hdnDestDomain};
$ mailFrom = $ requestData {hdnSender};
$ mailTo = $ requestData {hdnRecipient};
$ file = $ QUARANTINE_PATH."/".$ requestData {hdnFilePath};

$ mailfile = $ requestData {hdnFilePath};
$ validate_email ="falso";
meu $ email_regex = '^ ([\.]? [_ \ - \! \ # \ {\} \ $ \% \ ^ \&\ * \ + \ = \ | \? \ '\\\\\\ / a-zA-Z0-9]) * @ ([a-zA-Z0-9] ([-]? [a-zA- Z0-9] +) * \.) + ([A-zA-Z0-9] {0,6}) $ ';
if ($ requestData {hdnRecipient} = ~ / $ email_regex / && ((definido $ requestData {hdnSender} && $ requestData {hdnSender} eq '') || $ requestData {hdnSender} = ~ / $ email_regex /) && índice ($ requestData {hdnFilePath}, '.. /') == -1) {
$ validate_email ="verdade";
}
.... redigido....

Como podemos ver acima, o código pseudo-Perl nos mostra como o back-end recebe entrada do front-end ($ requestData) e como tenta verificar alguns dos parâmetros que enviamos.

Após a verificação, se nossos parâmetros forem válidos, o seguinte código é executado:

% mailreq = ("mailaction"=>"$ MAIL_FORWARD","sujeito"=>"$ strSubject","para email"=>"$ mailTo","ficheiro em anexo"=>"arquivo $","smtpserverhost"=>"$ mailServerHost","a partir do endereço"=>"$ mailFrom");

out = OPCODE mail_sender json% mailreq

O código acima define nossos parâmetros de solicitação na variável mailreq e chama a função mail_sender (OPCODE). Vamos ver como esse opcode é executado e onde exatamente o RCE acontece:

#mailaction 0 = mail_with_var, 1 = mail_forward, 2 = mail_attachment
$ mailaction = $ request->{mailaction};
$ subject = $ request->{sujeito};
$ mailbody = '';
$ attachmentfile = $ request->{ficheiro em anexo};
$ toEmail = $ request->{para email};

#mail body
E SE("$ request definido->{mailbody} && '' ne $ request->{mailbody}") {
$ mailbody = $ request->{mailbody};
}
Host do servidor #SMTP
E SE("$ request definido->{smtpserverhost} && '' ne $ request->{smtpserverhost}") {
$ smtpserverhost = $ request->{smtpserverhost};
}OUTRO{
resultado = QUERY "selecione servicevalue em tblclientservices em que servicekey = 'MailServer'"
E SE("$ result definido->{resultado}->{valor do serviço} [0] && '' ne $ result->{resultado}->{valor do serviço} [0]") {
$ smtpserverhost = $ resultado->{resultado}->{valor do serviço} [0];
}OUTRO{
$ smtpserverhost ="127.0.0.1";
}
}

Porta do servidor #SMTP
E SE("$ request definido->{smtpserverport} && '' ne $ request->{smtpserverport}") {
$ smtpserverport = $ request->{smtpserverport};
}OUTRO{
resultado = QUERY "selecione servicevalue em tblclientservices em que servicekey = 'MailServerPort'"
E SE("$ result definido->{resultado}->{valor do serviço} [0] && '' ne $ result->{resultado}->{valor do serviço} [0]") {
$ smtpserverport = $ resultado->{resultado}->{valor do serviço} [0];
}OUTRO{
$ smtpserverport ="25";
}
}

Sinalizador de autenticação #SMTP
$ smtpauthflag ="0 0";
E SE("$ request definido->{smtpauthflag} && '' ne $ request->{smtpauthflag}") {
$ smtpauthflag = $ request->{smtpauthflag};
}OUTRO{
resultado = QUERY "selecione servicevalue em tblclientservices em que servicekey = 'SMTPAuthenticationFlag'"
E SE("$ result definido->{resultado}->{valor do serviço} [0] && '' ne $ result->{resultado}->{valor do serviço} [0]") {
$ smtpauthflag = $ resultado->{resultado}->{valor do serviço} [0];
}
}

E SE("$ smtpauthflag == 1") {
E SE("$ request definido->{mailusername} && '' ne $ request->{mailusername}") {

$ mailusername = $ request->{mailusername};
$ mailpassword = $ request->{mailpassword};

}OUTRO{
resultado = QUERY "selecione servicevalue em tblclientservices em que servicekey = 'MailServerUsername'"
$ mailusername = $ result->{resultado}->{valor do serviço} [0];
resultado = QUERY "selecione servicevalue em tblclientservices em que servicekey = 'MailServerPassword'"
$ mailpassword = $ result->{resultado}->{valor do serviço} [0];
}
}OUTRO{

$ mailusername = "";
$ mailpassword = "";

}
E SE("$ request definido->{a partir do endereço} && '' ne $ request->{a partir do endereço}") {
$ fromaddress = $ request->{a partir do endereço};
}OUTRO{
resultado = QUERY "selecione servicevalue em tblclientservices em que servicekey = 'FromAddress'"
$ fromaddress = $ resultado->{resultado}->{valor do serviço} [0];
}

#Modo de segurança
E SE("$ request definido->{smtpsecurity} && '' ne $ request->{smtpsecurity}") {
$ smtpsecurity = $ request->{smtpsecurity};
}OUTRO{
resultado = QUERY "selecione servicevalue em tblclientservices em que servicekey = 'smtpsecurity'"
$ smtpsecurity = $ resultado->{resultado}->{valor do serviço} [0];
}

$ smtpsecuritymode = 0;
E SE("$ smtpsecurity eq 'STARTTLS'") {
$ smtpsecuritymode = 1;
} ELSE SE ("$ smtpsecurity eq 'SSL / TLS'") {
$ smtpsecuritymode = 2;
}

#SMTP Certificate

$ smtpcertificate = '';
$ certpassword = '';

E SE("$ smtpsecuritymode! = 0") {
E SE("$ request definido->{smtpcertificate} && '' ne $ request->{smtpcertificate}") {
resultado = QUERY "selecione certname, senha de tblvpncertificate em que certid = $ request->{smtpcertificate}"
}OUTRO{
resultado = QUERY "selecione certname, senha de tblvpncertificate em que certid = (selecione servicevalue :: int em tblclientservices em que servicekey = 'smtpcertificate')"
}

$ smtpcertificate = $ result->{resultado}->{certname} [0];
$ certpassword = $ result->{resultado}->{senha} [0];

}

#De endereço com nome
E SE("$ request definido->{fromaddresswithname} && '' ne $ request->{fromaddresswithname}") {
$ fromaddresswithname = $ request->{fromaddresswithname};
}OUTRO{
$ fromaddresswithname = $ OEMNAME . " <" . $ fromaddress . ">";
}

O código acima faz a mesma coisa que o outro opcode fez quando é iniciado. Inicializa variáveis ​​(algumas de nós ou do dispositivo, se não forem especificadas).

Depois que as variáveis ​​são atribuídas, o seguinte bloco de código é executado.

out = EXECSH "/ bin / cschelper mail_send '$ fromaddress' '$ fromaddresswithname' '$ toEmail' '$ toEmail' '$ subject' '$ mailbody' '$ smtpserverhost' '$ smtpserverport' '$ mailusername' '$ mailpassword' '$ mailaction' ' $ smtpsecuritymode '' $ smtpcertificate '' $ certpassword '' 1 '' $ attachmentfile '"

E aí está, a execução do comando. Agora a chamada aqui é EXECSH, que chama / bin / sh -c "ARGUMENTS". Com a execução acontecendo usando os valores que controlamos, podemos obter facilmente a execução de comandos remotos, tudo sem autenticação.

Em poucos meses, lançaremos um relatório completo e a Prova de Conceito com os devidos contornos.

Atualizar: Esta pesquisa foi abordada primeiro no TechCrunch, leia mais aqui.

Brayan Jackson
Brayan Jackson Administrator
Sorry! The Author has not filled his profile.
follow me