CVE-2019-17059: Explication de Preauth-RCE dans le Cyberoam de Sophos

Nous avons travaillé dur avec des chercheurs en sécurité internes et externes ici à TheBestVPN pour découvrir de sérieuses failles exploitables à distance dans les VPN SSL et les pare-feu comme Cyberoam, Fortigate et les VPN Cisco. Cet article est une référence technique concernant une vulnérabilité critique corrigée affectant le VPN SSL Cyberoam, également connue sous le nom de CyberoamOS.


Cet exploit Cyberoam, baptisé CVE-2019-17059, est une vulnérabilité critique qui permet aux attaquants d'accéder à votre appareil Cyberoam sans fournir de nom d'utilisateur ou de mot de passe. En plus de cela, l'accès accordé est le plus haut niveau (root), ce qui donne essentiellement à un attaquant des droits illimités sur votre appareil Cyberoam.

Dans la plupart des environnements réseau, les appareils Cyberoam sont utilisés comme pare-feu et passerelles VPN SSL. Cela donne à un attaquant potentiel une forte présence dans un réseau. Cela facilite l'attaque des hôtes à l'intérieur du réseau, et comme les appareils Cyberoam sont généralement fiables dans la plupart des environnements, cela donne à un attaquant potentiel un avantage supplémentaire.

Selon Shodan (un moteur de recherche pour les appareils connectés à Internet), il existe plus de 96 000 appareils Cyberoam accessibles sur Internet dans le monde entier. La plupart de ces appareils sont installés dans des entreprises, des universités et certains dans des banques de renommée mondiale. Cela conduit à des attaques ayant des impacts énormes sur ces environnements.

Travailler avec l'équipe de sécurité de Sophos a été un grand plaisir car ils ont agi rapidement en reconnaissant et en déployant les correctifs quelques jours seulement après notre rapport initial. Bravo à eux! (jeu de mots volontaire!)

détecter Cyberoam

Et comme la plupart de ces entités sont des cibles attrayantes pour les attaquants, cela rend les bogues encore plus critiques.

Exécution de la commande racine non authentifiée à distance de CyberoamOS

Le CyberoamOS est un système d'exploitation basé sur Linux modifié pour les appareils Cyberoam. Cet OS a une interface de configuration basée sur le Web et un portail SSLVPN.

L'interface Web est divisée en deux parties principales:

  • Un frontend écrit en Java
  • Un backend qui utilise une combinaison de C et Perl

Nous ne plongerons pas profondément dans les éléments internes du code frontal ou principal, principalement pour gagner du temps et limiter la quantité d'informations révélées. Mais nous verrons brièvement comment le bug est déclenché.

La configuration et les interfaces SSLVPN ont une servlet qui gère les opérations principales. Ces opérations sont définies à l'aide d'un paramètre nommé «mode».

La plupart d'entre eux sont authentifiés. Mais il y a quelques opérations auxquelles nous pouvons accéder sans authentification (comme la connexion).

Les bogues que nous avons trouvés se trouvent dans le module antivirus / antispam des e-mails. Le mode de demande pour ce noeud final (module, op) est 458.

Une chose à noter est que les opcodes sont mappés à leurs noms dans la base de données Cyberoam (base de données interne Postgres). En recherchant 458, nous pouvons découvrir quel est le nom de cet opcode.

Voici une ligne du script SQL d'initialisation de la base de données affichant le nom opcode 458:

insérer dans tblcrevent (opcode, description, mode, requesttype)
valeurs ('RELEASEQUARANTINEMAILFROMMAIL', 'RELEASE QUARANTINE MAIL FROM MAIL', '458', 2);

Les fonctions opcode sont stockées dans le répertoire / _conf / csc / cscconf /. Nous ne révélerons pas tout le code de la fonction vulnérable, mais nous fournirons quelques extraits montrant où et comment le bogue se produit.

Un code du frontend Java qui gère l'opcode 458:

if ((jsonObject.getString ("hdnSender").équivaut à("") ||
validateEmail (jsonObject.getString ("hdnSender"))) &&
validateEmail (jsonObject.getString ("hdnRecipient")) &&
isSafeFilePath (jsonObject.getString ("hdnFilePath")) && b) {
httpServletResponse.setContentType ("texte / html");
CyberoamLogger.debug ("Antivirus / AntiSpam", "Valeur constante CSC " +
CSCConstants.isCCC);

Comme vous pouvez le voir ci-dessus, la validité de quelques paramètres est vérifiée. S'il s'agit de valeurs valides, les événements suivants se produisent:

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

Comme nous pouvons le voir ci-dessus, nous avons un nouveau code d'événement (363) qui sera envoyé au backend. Le bug que nous avons découvert se trouve dans le code qui gère cela dans le backend.

L'opcode est nommé sendmail, et pour éviter l'exploitation de ce bogue, nous supprimerons la plupart du code du code suivant.

Le gestionnaire d'opcode pour send_mail.

...expurgé ...

$ param = $ request->{Libération};
param = DLOPEN (base64_decode, param)
LOG applog " Décoder les valeurs :: $ param \ n"
% requestData = split (/ [&=] /, $ param);
$ mailServerHost = $ requestData {hdnDestDomain};
$ mailFrom = $ requestData {hdnSender};
$ mailTo = $ requestData {hdnRecipient};
$ file = $ QUARANTINE_PATH."/".$ requestData {hdnFilePath};

$ mailfile = $ requestData {hdnFilePath};
$ validate_email ="faux";
mon $ 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 / && ((défini $ requestData {hdnSender} && $ requestData {hdnSender} eq '') || $ requestData {hdnSender} = ~ / $ email_regex /) && index ($ requestData {hdnFilePath}, '.. /') == -1) {
$ validate_email ="vrai";
}
.... expurgé....

Comme nous pouvons le voir ci-dessus, le code pseudo-Perl nous montre comment le backend reçoit les entrées du frontend ($ requestData) et comment il tente de vérifier certains des paramètres que nous envoyons.

Après la vérification, si nos paramètres sont valides, le code suivant est exécuté:

% mailreq = ("mailaction"=>"$ MAIL_FORWARD","matière"=>"$ strSubject","envoyer un email"=>"$ mailTo","fichier joint"=>"$ fichier","smtpserverhost"=>"$ mailServerHost","de l'adresse"=>"$ mailFrom");

out = OPCODE mail_sender json% mailreq

Le code ci-dessus définit nos paramètres de demande dans la variable mailreq et appelle la fonction mail_sender (OPCODE). Nous verrons comment cet opcode est exécuté et où exactement le RCE se produit:

#mailaction 0 = mail_with_var, 1 = mail_forward, 2 = mail_attachment
$ mailaction = $ request->{mailaction};
$ subject = $ request->{matière};
$ mailbody = '';
$ attachmentfile = $ request->{fichier joint};
$ toEmail = $ request->{envoyer un email};

#mail body
SI("$ demande définie->{mailbody} && '' ne $ demande->{mailbody}") {
$ mailbody = $ request->{mailbody};
}
Hôte du serveur #SMTP
SI("$ demande définie->{smtpserverhost} && '' ne $ demande->{smtpserverhost}") {
$ smtpserverhost = $ request->{smtpserverhost};
}AUTRE{
result = QUERY "sélectionnez servicevalue dans tblclientservices où servicekey = 'MailServer'"
SI("$ résultat défini->{production}->{servicevalue} [0] && '' résultat ne $->{production}->{servicevalue} [0]") {
$ smtpserverhost = $ result->{production}->{servicevalue} [0];
}AUTRE{
$ smtpserverhost ="127.0.0.1";
}
}

Port du serveur #SMTP
SI("$ demande définie->{smtpserverport} && '' ne $ demande->{smtpserverport}") {
$ smtpserverport = $ request->{smtpserverport};
}AUTRE{
result = QUERY "sélectionnez servicevalue dans tblclientservices où servicekey = 'MailServerPort'"
SI("$ résultat défini->{production}->{servicevalue} [0] && '' résultat ne $->{production}->{servicevalue} [0]") {
$ smtpserverport = $ result->{production}->{servicevalue} [0];
}AUTRE{
$ smtpserverport ="25";
}
}

Indicateur d'authentification #SMTP
$ smtpauthflag ="0";
SI("$ demande définie->{smtpauthflag} && '' ne $ demande->{smtpauthflag}") {
$ smtpauthflag = $ request->{smtpauthflag};
}AUTRE{
result = QUERY "sélectionnez la valeur de service dans tblclientservices où servicekey = 'SMTPAuthenticationFlag'"
SI("$ résultat défini->{production}->{servicevalue} [0] && '' résultat ne $->{production}->{servicevalue} [0]") {
$ smtpauthflag = $ result->{production}->{servicevalue} [0];
}
}

SI("$ smtpauthflag == 1") {
SI("$ demande définie->{mailusername} && '' ne $ demande->{mailusername}") {

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

}AUTRE{
result = QUERY "sélectionnez servicevalue dans tblclientservices où servicekey = 'MailServerUsername'"
$ mailusername = $ result->{production}->{servicevalue} [0];
result = QUERY "sélectionnez servicevalue dans tblclientservices où servicekey = 'MailServerPassword'"
$ mailpassword = $ result->{production}->{servicevalue} [0];
}
}AUTRE{

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

}
SI("$ demande définie->{de l'adresse} && '' ne $ demande->{de l'adresse}") {
$ fromaddress = $ request->{de l'adresse};
}AUTRE{
result = QUERY "sélectionnez la valeur de service dans tblclientservices où servicekey = 'FromAddress'"
$ fromaddress = $ result->{production}->{servicevalue} [0];
}

#Mode sécurité
SI("$ demande définie->{smtpsecurity} && '' ne $ demande->{smtpsecurity}") {
$ smtpsecurity = $ request->{smtpsecurity};
}AUTRE{
result = QUERY "sélectionnez la valeur de service dans tblclientservices où servicekey = 'smtpsecurity'"
$ smtpsecurity = $ result->{production}->{servicevalue} [0];
}

$ smtpsecuritymode = 0;
SI("$ smtpsecurity eq 'STARTTLS'") {
$ smtpsecuritymode = 1;
} AUTRE SI ("$ smtpsecurity eq 'SSL / TLS'") {
$ smtpsecuritymode = 2;
}

#SMTP Certificate

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

SI("$ smtpsecuritymode! = 0") {
SI("$ demande définie->{smtpcertificate} && '' ne $ demande->{smtpcertificate}") {
result = QUERY "sélectionnez certname, mot de passe dans tblvpncertificate où certid = $ request->{smtpcertificate}"
}AUTRE{
result = QUERY "sélectionnez certname, mot de passe dans tblvpncertificate où certid = (sélectionnez servicevalue :: int dans tblclientservices où servicekey = 'smtpcertificate')"
}

$ smtpcertificate = $ result->{production}->{certname} [0];
$ certpassword = $ result->{production}->{mot de passe} [0];

}

#De l'adresse avec le nom
SI("$ demande définie->{fromaddresswithname} && '' ne $ demande->{fromaddresswithname}") {
$ fromaddresswithname = $ request->{fromaddresswithname};
}AUTRE{
$ fromaddresswithname = $ OEMNAME . " <" . $ fromaddress . ">";
}

Le code ci-dessus fait la même chose que l'autre opcode a fait quand il démarre. Il initialise les variables (certaines de nous ou de l'appareil si non spécifié).

Une fois les variables affectées, le bloc de code suivant est exécuté.

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

Et voilà, l'exécution de la commande. Maintenant, l'appel ici est EXECSH qui appelle / bin / sh -c "ARGUMENTS". L'exécution se faisant à l'aide de valeurs que nous contrôlons, nous pouvons facilement atteindre l'exécution de commandes à distance, le tout sans authentification.

Nous publierons un rapport complet et la preuve de concept avec les contours appropriés dans quelques mois.

Mise à jour: Cette recherche a été couverte en premier sur TechCrunch, en savoir plus ici.

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