
CyberoamOS Remote Unauthenticated Root Command Execution
The CyberoamOS is a modified Linux-based operating system for Cyberoam devices. This OS has a web-based configuration interface and an SSLVPN portal. The web interface is divided into two main parts:- A frontend written in Java
- A backend that uses a combination of C and Perl
insert into tblcrevent(opcode,description,mode,requesttype) values('RELEASEQUARANTINEMAILFROMMAIL','RELEASE QUARANTINE MAIL FROM MAIL','458',2);The opcode functions are stored in the directory /_conf/csc/cscconf/. We will not be revealing the whole code of the vulnerable function, but we will provide a few snippets showing where and how the bug occurs. A code from the Java frontend that handles the 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 Constant value " + CSCConstants.isCCC);As you can see above, a few parameters are checked for validity. If they are valid values, the following happens:
final EventBean eventByMode = EventBean.getEventByMode(363); ...redacted. final int sendWizardEvent = cscClient.sendWizardEvent(eventByMode, hashMap, sqlReader);As we can see above, we have a new event code (363) that will be sent to the backend. The bug we have discovered is in the code that handles this in the backend. The opcode is named sendmail, and to avoid exploitation of this bug, we will be redacting most of the code from the following code. The opcode handler for send_mail.
...redacted... <code>$param = $request->{release};</code> param = DLOPEN(base64_decode,param) LOG applog " Decode values :: $param \n" <code>%requestData = split(/[&=]/, $param); $mailServerHost = $requestData{hdnDestDomain}; $mailFrom = $requestData{hdnSender}; $mailTo = $requestData{hdnRecipient}; $file = $QUARANTINE_PATH."/".$requestData{hdnFilePath}; $mailfile=$requestData{hdnFilePath}; $validate_email="false"; my $email_regex='^([\.]?[_\-\!\#\{\}\$\%\^\&\*\+\=\|\?\'\\\\\\/a-zA-Z0-9])*@([a-zA-Z0-9]([-]?[a-zA-Z0-9]+)*\.)+([a-zA-Z0-9]{0,6})As we can see above, the pseudo-Perl code shows us how the backend receives input from the frontend ($requestData) and how it attempts to verify some of the parameters we send. After the verification, if our parameters are valid, the following code is executed:
%mailreq=("mailaction"=>"$MAIL_FORWARD","subject"=>"$strSubject","toEmail"=>"$mailTo","attachmentfile"=>"$file","smtpserverhost"=>"$mailServerHost","fromaddress"=>"$mailFrom"); </code> out = OPCODE mail_sender json %mailreqThe code above sets our request parameters into mailreq variable and calls the mail_sender function (OPCODE). We will see how this opcode is executed and where exactly the RCE happens:
<code> #mailaction 0=mail_with_var,1=mail_forward,2=mail_attachment $mailaction=$request->{mailaction}; $subject=$request->{subject}; $mailbody=''; $attachmentfile=$request->{attachmentfile}; $toEmail=$request->{toEmail}; </code> #mail body IF("defined $request->{mailbody} && '' ne $request->{mailbody}"){ <code>$mailbody=$request->{mailbody};</code> } #SMTP server host IF("defined $request->{smtpserverhost} && '' ne $request->{smtpserverhost}"){ <code>$smtpserverhost=$request->{smtpserverhost};</code> }ELSE{ result = QUERY "select servicevalue from tblclientservices where servicekey='MailServer'" IF("defined $result->{output}->{servicevalue}[0] && '' ne $result->{output}->{servicevalue}[0]"){ <code>$smtpserverhost=$result->{output}->{servicevalue}[0];</code> }ELSE{ <code>$smtpserverhost="127.0.0.1";</code> } } #SMTP server port IF("defined $request->{smtpserverport} && '' ne $request->{smtpserverport}"){ <code>$smtpserverport=$request->{smtpserverport};</code> }ELSE{ result = QUERY "select servicevalue from tblclientservices where servicekey='MailServerPort'" IF("defined $result->{output}->{servicevalue}[0] && '' ne $result->{output}->{servicevalue}[0]"){ <code>$smtpserverport=$result->{output}->{servicevalue}[0];</code> }ELSE{ <code>$smtpserverport="25";</code> } } #SMTP auth flag <code>$smtpauthflag="0";</code> IF("defined $request->{smtpauthflag} && '' ne $request->{smtpauthflag}"){ <code>$smtpauthflag=$request->{smtpauthflag};</code> }ELSE{ result = QUERY "select servicevalue from tblclientservices where servicekey='SMTPAuthenticationFlag'" IF("defined $result->{output}->{servicevalue}[0] && '' ne $result->{output}->{servicevalue}[0]"){ <code>$smtpauthflag=$result->{output}->{servicevalue}[0];</code> } } IF("$smtpauthflag == 1"){ IF("defined $request->{mailusername} && '' ne $request->{mailusername}"){ <code> $mailusername=$request->{mailusername}; $mailpassword=$request->{mailpassword}; </code> }ELSE{ result = QUERY "select servicevalue from tblclientservices where servicekey = 'MailServerUsername'" <code>$mailusername = $result->{output}->{servicevalue}[0];</code> result = QUERY "select servicevalue from tblclientservices where servicekey = 'MailServerPassword'" <code>$mailpassword = $result->{output}->{servicevalue}[0];</code> } }ELSE{ <code> $mailusername = ""; $mailpassword = ""; </code> } IF("defined $request->{fromaddress} && '' ne $request->{fromaddress}"){ <code>$fromaddress=$request->{fromaddress};</code> }ELSE{ result = QUERY "select servicevalue from tblclientservices where servicekey = 'FromAddress'" <code>$fromaddress = $result->{output}->{servicevalue}[0];</code> } #Security Mode IF("defined $request->{smtpsecurity} && '' ne $request->{smtpsecurity}"){ <code>$smtpsecurity=$request->{smtpsecurity};</code> }ELSE{ result = QUERY "select servicevalue from tblclientservices where servicekey = 'smtpsecurity'" <code>$smtpsecurity = $result->{output}->{servicevalue}[0];</code> } <code>$smtpsecuritymode=0;</code> IF("$smtpsecurity eq 'STARTTLS'"){ <code>$smtpsecuritymode=1;</code> }ELSE IF("$smtpsecurity eq 'SSL/TLS'"){ <code>$smtpsecuritymode=2;</code> } #SMTP Certificate <code> $smtpcertificate = ''; $certpassword=''; </code> IF("$smtpsecuritymode!=0"){ IF("defined $request->{smtpcertificate} && '' ne $request->{smtpcertificate}"){ result = QUERY "select certname,password from tblvpncertificate where certid=$request->{smtpcertificate}" }ELSE{ result = QUERY "select certname,password from tblvpncertificate where certid=(select servicevalue::int from tblclientservices where servicekey = 'smtpcertificate')" } <code> $smtpcertificate = $result->{output}->{certname}[0]; $certpassword=$result->{output}->{password}[0]; </code> } #From Address with Name IF("defined $request->{fromaddresswithname} && '' ne $request->{fromaddresswithname}"){ <code>$fromaddresswithname=$request->{fromaddresswithname};</code> }ELSE{ <code>$fromaddresswithname = $OEMNAME . " <" . $fromaddress . ">";</code> }The code above does the same thing the other opcode did when it starts. It initializes variables (some from us or from the device if not specified). After the variables are assigned, the following code block is executed.
out = EXECSH "/bin/cschelper mail_send '$fromaddress' '$fromaddresswithname' '$toEmail' '$toEmail' '$subject' '$mailbody' '$smtpserverhost' '$smtpserverport' '$mailusername' '$mailpassword' '$mailaction' '$smtpsecuritymode' '$smtpcertificate' '$certpassword' '1' '$attachmentfile'"And there it is, the command execution. Now the call here is EXECSH which calls /bin/sh -c “ARGUMENTS”. With the execution happening using values we control, we can easily attain remote command execution, all without authentication. We will be releasing a full report and the Proof of Concept with proper outlines in a few months. Update: This research was covered first on TechCrunch, read more here. The post CVE-2019-17059: Preauth-RCE in Sophos’ Cyberoam Explained appeared first on TheBestVPN.com.
; if($requestData{hdnRecipient} =~ /$email_regex/ && ((defined $requestData{hdnSender} && $requestData{hdnSender} eq '') || $requestData{hdnSender} =~ /$email_regex/) && index($requestData{hdnFilePath},'../') == -1){ $validate_email="true"; } ....redacted....As we can see above, the pseudo-Perl code shows us how the backend receives input from the frontend ($requestData) and how it attempts to verify some of the parameters we send. After the verification, if our parameters are valid, the following code is executed: The code above sets our request parameters into mailreq variable and calls the mail_sender function (OPCODE). We will see how this opcode is executed and where exactly the RCE happens: The code above does the same thing the other opcode did when it starts. It initializes variables (some from us or from the device if not specified). After the variables are assigned, the following code block is executed. And there it is, the command execution. Now the call here is EXECSH which calls /bin/sh -c “ARGUMENTS”. With the execution happening using values we control, we can easily attain remote command execution, all without authentication. We will be releasing a full report and the Proof of Concept with proper outlines in a few months. Update: This research was covered first on TechCrunch, read more here.
-