CVE-2019-17059: Preauth-RCE în Cyberoam-ul Sophos explicat

Am lucrat din greu cu cercetătorii de securitate internă și externă aici, la TheBestVPN, pentru a descoperi lacune grave exploatabile de la distanță în VPN-urile SSL și Firewalls precum VPN-urile Cyberoam, Fortigate și Cisco. Acest articol este o abordare tehnică despre o vulnerabilitate critică afectată care afectează VPN-ul Cyberoam SSL, cunoscut și sub numele de CyberoamOS.


Acest exploat Cyberoam, denumit CVE-2019-17059 este o vulnerabilitate critică care permite atacatorilor să acceseze dispozitivul dvs. Cyberoam fără a furniza niciun nume de utilizator sau parolă. În plus, accesul acordat este cel mai înalt nivel (root), ceea ce oferă, în esență, unui atacator drepturi nelimitate asupra dispozitivului dvs. Cyberoam.

În majoritatea mediilor de rețea, dispozitivele Cyberoam sunt utilizate ca firewall-uri și gateway SSL VPN. Acest lucru oferă unui potențial atacator o poziție puternică într-o rețea. Acest lucru face mai ușor să atace gazdele în rețea și, deoarece dispozitivele Cyberoam sunt de obicei de încredere în majoritatea mediilor, acest lucru oferă un atacator care ar putea fi mai mare.

Potrivit lui Shodan (un motor de căutare pentru dispozitive conectate la internet), există peste 96.000 de dispozitive Cyberoam cu vedere la internet din întreaga lume. Majoritatea acestor dispozitive sunt instalate în întreprinderi, universități și unele în băncile de renume mondial. Acest lucru duce la atacuri cu impacturi mari asupra acestor medii.

Colaborarea cu echipa de securitate Sophos a fost o mare încântare, deoarece au acționat rapid, recunoscând și dărâmând patch-uri la doar câteva zile după raportul nostru inițial. Kudos pentru ei! (joc de cuvinte intentionat!)

detectarea Cyberoamului

Și având în vedere că majoritatea acestor entități sunt ținte atractive pentru atacatori, face ca aceste bug-uri să fie mai critice.

CyberoamOS Execuție de comandă rădăcină neautentificată la distanță

CyberoamOS este un sistem de operare modificat bazat pe Linux pentru dispozitive Cyberoam. Acest sistem de operare are o interfață de configurare bazată pe web și un portal SSLVPN.

Interfața web este împărțită în două părți principale:

  • Un frontend scris în Java
  • Un backend care folosește o combinație de C și Perl

Nu ne vom scufunda în profunzime în interiorul codului frontal sau posterior, în principal pentru a economisi timp și a limita cantitatea de informații dezvăluite. Dar vom discuta pe scurt cum este declanșat eroarea.

Atât configurația, cât și interfețele SSLVPN au un servlet care se ocupă de operațiile principale. Aceste operațiuni sunt definite folosind un parametru numit „mod”.

Majoritatea acestora sunt autentificate. Dar există câteva op-uri pe care le putem accesa fără autentificare (cum ar fi autentificare).

Bug-urile pe care le-am găsit se află în modulul antivirus / antispam e-mail. Modul de solicitare pentru acest punct final (modul, op) este 458.

Un lucru de remarcat este că opcodurile sunt mapate cu numele lor în baza de date Cyberoam (baza de date internă Postgres). Căutând 458, putem afla cum este numele acestui cod de opcod.

Iată o linie din scriptul SQL de inițializare a bazei de date care arată numele opcode 458:

inserați în tblcrevent (cod opțional, descriere, mod, tip solicitare)
valori ("RELEASEQUARANTINEMAILFROMMAIL", "RELEASE QUARANTINE MAIL FROM MAIL", "458", 2);

Funcțiile opcode sunt stocate în directorul / _conf / csc / cscconf /. Nu vom dezvălui întregul cod al funcției vulnerabile, dar vom oferi câteva fragmente care arată unde și cum apare eroarea.

Un cod din frontendul Java care gestionează opcode 458:

if ((jsonObject.getString) ("hdnSender") .equals ("") ||
validateEmail (jsonObject.getString ("hdnSender"))) &&
validateEmail (jsonObject.getString ("hdnRecipient")) &&
isSafeFilePath (jsonObject.getString ("hdnFilePath")) && b) {
httpServletResponse.setContentType ("text / html");
CyberoamLogger.debug ("Antivirus / AntiSpam", "CSC Valoarea constantă " +
CSCConstants.isCCC);

După cum puteți vedea mai sus, câțiva parametri sunt verificați pentru validitate. Dacă sunt valori valide, se întâmplă următoarele:

final EventBean eventByMode = EventBean.getEventByMode (363);
... vor fi redactate.
final int sendWizardEvent = cscClient.sendWizardEvent (eventByMode, hashMap, sqlReader);

După cum putem vedea mai sus, avem un nou cod de eveniment (363) care va fi trimis backend-ului. Bug-ul pe care l-am descoperit este în codul care gestionează acest lucru în backend.

Codul opțional poartă numele sendmail, iar pentru a evita exploatarea acestui bug, vom redacta cea mai mare parte a codului din următorul cod.

Manipulator opcode pentru send_mail.

...... redactată

$ param = $ cerere->{eliberare};
param = DLOPEN (bază 64_decod, param)
Aplicație LOG " Valorile decodării :: $ param \ n"
% requestData = divizat (/ [&=] /, $ param);
$ mailServerHost = $ requestData {hdnDestDomain};
$ mailFrom = $ requestData {hdnSender};
$ mailTo = $ requestData {hdnRecipient};
$ file = $ QUARANTINE_PATH."/".$ RequestData {hdnFilePath};

$ Mailfile = $ requestData {hdnFilePath};
$ Validate_email ="fals";
my $ 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 / && ((definit $ requestData {hdnSender}) && $ requestData {hdnSender} eq '') || $ requestData {hdnSender} = ~ / $ email_regex /) && index ($ requestData {hdnFilePath}, '.. /') == -1) {
$ Validate_email ="Adevărat";
}
.... redactate....

După cum putem vedea mai sus, codul pseudo-Perl ne arată modul în care backendul primește intrarea de pe frontend ($ requestData) și cum încearcă să verifice unii dintre parametrii pe care îi trimitem.

După verificare, dacă parametrii noștri sunt valabili, se execută următorul cod:

% Mailreq = ("mailaction"=>"$ MAIL_FORWARD","subiect"=>"$ strSubject","a trimite un email"=>"$ mailto","fisier atasat"=>"$ fișier","smtpserverhost"=>"$ mailServerHost","FROMADDRESS"=>"$ mailFrom");

out = OPCODE mail_sender json% mailreq

Codul de mai sus stabilește parametrii cererii noastre în variabila mailreq și apelează funcția mail_sender (OPCODE). Vom vedea cum este executat acest opcode și unde se întâmplă exact RCE:

#mailaction 0 = mail_with_var, 1 = mail_forward, 2 = mail_attachment
$ Mailaction = $ cerere->{Mailaction};
$ Subiect = $ cerere->{subiect};
$ Mailbody = '';
$ Attachmentfile = $ cerere->{fisier atasat};
$ ToEmail = $ cerere->{a trimite un email};

#mail corp
DACĂ("cerere definită $->{Mailbody} && '' Ne cerere $->{Mailbody}") {
$ Mailbody = $ cerere->{Mailbody};
}
# Gazda serverului SSM
DACĂ("cerere definită $->{Smtpserverhost} && '' Ne cerere $->{Smtpserverhost}") {
$ Smtpserverhost = $ cerere->{Smtpserverhost};
} Else {
rezultat = ÎNTREBARE "selectați servicevalue din tblclientservices unde servicekey = 'MailServer'"
DACĂ("rezultat $ definit->{Ieșire}->{Servicevalue} [0] && '' ne $ rezultat->{Ieșire}->{Servicevalue} [0]") {
$ Smtpserverhost = $ rezultat->{Ieșire}->{Servicevalue} [0];
} Else {
$ Smtpserverhost ="127.0.0.1";
}
}

#SMTP port server
DACĂ("cerere definită $->{Smtpserverport} && '' Ne cerere $->{Smtpserverport}") {
$ Smtpserverport = $ cerere->{Smtpserverport};
} Else {
rezultat = ÎNTREBARE "selectați servicevalue din tblclientservices unde servicekey = 'MailServerPort'"
DACĂ("rezultat $ definit->{Ieșire}->{Servicevalue} [0] && '' ne $ rezultat->{Ieșire}->{Servicevalue} [0]") {
$ Smtpserverport = $ rezultat->{Ieșire}->{Servicevalue} [0];
} Else {
$ Smtpserverport ="25";
}
}

#SMTP auth flag
$ Smtpauthflag ="0";
DACĂ("cerere definită $->{Smtpauthflag} && '' Ne cerere $->{Smtpauthflag}") {
$ Smtpauthflag = $ cerere->{Smtpauthflag};
} Else {
rezultat = ÎNTREBARE "selectați servicevalue din tblclientservices unde servicekey = 'SMTPAuthenticationFlag'"
DACĂ("rezultat $ definit->{Ieșire}->{Servicevalue} [0] && '' ne $ rezultat->{Ieșire}->{Servicevalue} [0]") {
$ Smtpauthflag = $ rezultat->{Ieșire}->{Servicevalue} [0];
}
}

DACĂ("$ smtpauthflag == 1") {
DACĂ("cerere definită $->{Mailusername} && '' Ne cerere $->{Mailusername}") {

$ Mailusername = $ cerere->{Mailusername};
$ Mailpassword = $ cerere->{Mailpassword};

} Else {
rezultat = ÎNTREBARE "selectați servicevalue din tblclientservices unde servicekey = 'MailServerUsername'"
$ mailusername = $ rezultat->{Ieșire}->{Servicevalue} [0];
rezultat = ÎNTREBARE "selectați servicevalue din tblclientservices unde servicekey = 'MailServerPassword'"
$ mailpassword = $ rezultat->{Ieșire}->{Servicevalue} [0];
}
} Else {

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

}
DACĂ("cerere definită $->{FROMADDRESS} && '' Ne cerere $->{FROMADDRESS}") {
$ FROMADDRESS = $ cerere->{FROMADDRESS};
} Else {
rezultat = ÎNTREBARE "selectați servicevalue din tblclientservices unde servicekey = 'FromAddress'"
$ fromaddress = $ rezultat->{Ieșire}->{Servicevalue} [0];
}

# Modul de securitate
DACĂ("cerere definită $->{Smtpsecurity} && '' Ne cerere $->{Smtpsecurity}") {
$ Smtpsecurity = $ cerere->{Smtpsecurity};
} Else {
rezultat = ÎNTREBARE "selectați servicevalue din tblclientservices unde servicekey = 'smtpsecurity'"
$ smtpsecurity = $ rezultat->{Ieșire}->{Servicevalue} [0];
}

$ Smtpsecuritymode = 0;
DACĂ("$ smtpsecurity eq 'STARTTLS'") {
$ Smtpsecuritymode = 1;
} ELSE IF ("$ smtpsecurity eq 'SSL / TLS'") {
$ Smtpsecuritymode = 2;
}

Certificat #SMTP

$ smtpcertificate = '';
$ Certpassword = '';

DACĂ("$ Smtpsecuritymode! = 0") {
DACĂ("cerere definită $->{Smtpcertificate} && '' Ne cerere $->{Smtpcertificate}") {
rezultat = ÎNTREBARE "selectați numele de certificat, parola de la tblvpncertificate unde certid = $ cerere->{Smtpcertificate}"
} Else {
rezultat = ÎNTREBARE "selectați numele de certificat, parola de la tblvpncertificate where certid = (selectați servicevalue :: int din tblclientservices unde servicekey = 'smtpcertificate')"
}

$ smtpcertificate = $ rezultat->{Ieșire}->{Certname} [0];
$ Certpassword = $ rezultat->{Ieșire}->{Parola} [0];

}

#De adresă cu nume
DACĂ("cerere definită $->{Fromaddresswithname} && '' Ne cerere $->{Fromaddresswithname}") {
$ Fromaddresswithname = $ cerere->{Fromaddresswithname};
} Else {
$ fromaddresswithname = $ OEMNAME . " <" . $ FROMADDRESS . ">";
}

Codul de mai sus face același lucru pe care l-a făcut celălalt opcode la început. Initializeaza variabile (unele de la noi sau de pe dispozitiv, daca nu sunt specificate).

După ce variabilele sunt alocate, se execută următorul bloc de cod.

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

Și acolo este, execuția comenzii. Acum, apelul aici este EXECSH care numește / bin / sh -c „ARGUMENTS”. Dacă execuția se întâmplă folosind valorile pe care le controlăm, putem realiza cu ușurință executarea comenzii la distanță, toate fără autentificare.

Vom lansa un raport complet și Dovada conceptului cu schițe adecvate în câteva luni.

Actualizați: Această cercetare a fost abordată mai întâi pe TechCrunch, citiți mai multe aici.

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