We’ve been working hard with internal and external security researchers here at TheBestVPN to uncover serious remotely exploitable loopholes in SSL VPNs and Firewalls like Cyberoam, Fortigate and Cisco VPNs. This article is a technical retrospective on a critical vulnerability affecting Cyberoam SSL VPN also known as CyberoamOS that has since been patched.
This Cyberoam exploit, dubbed CVE-2019-17059, was a critical vulnerability that allowed attackers to access Cyberoam devices without providing any username or password. On top of that, the access granted was the highest level (root), which essentially gave an attacker unlimited rights on the Cyberoam device.
In most network environments, Cyberoam devices were used as firewalls and SSL VPN gateways. This gave potential attackers a strong foothold in a network. It made it easier to attack hosts inside the network, and since Cyberoam devices were usually trusted in most environments, this gave would-be attackers an extra edge.
According to Shodan (a search engine for internet-connected devices), there were more than 96,000 internet-facing Cyberoam devices from all over the world at the time of discovery. Most of these devices were installed in enterprises, universities and some in world-renowned banks. This led to the attacks having huge potential impacts on these environments.
Working with Sophos security team was a delight as they acted quickly by acknowledging and rolling out patches only a few days after our initial report to them. Kudos to them! (pun-intended!)
And since most of these entities were attractive targets for attackers, it made the vulnerability all that more critical.
CyberoamOS Remote Unauthenticated Root Command Execution
The CyberoamOS was a modified Linux-based operating system for Cyberoam devices. This OS had a web-based configuration interface and an SSLVPN portal.
The web interface was divided into two main parts:
- A frontend written in Java
- A backend that used a combination of C and Perl
We will not dive deep into the internals of the front- or back-end code, mainly to save time and limit the amount of information revealed. But we will discuss briefly how the bug was triggered.
Both the configuration and SSLVPN interfaces had a servlet that handled main operations. These operations were defined using a parameter named “mode”.
Most of these were authenticated. But there were a few ops that could be accessed without authentication (like login).
The bugs we found lay in the email antivirus/antispam module. The request mode for this endpoint (module, op) was 458.
One thing to note is the opcodes were mapped to their names in the Cyberoam database (internal database Postgres). By looking up 458, we could find out what the name of this opcode was.
Here is a line from the database initialization SQL script showing the name opcode 458:
insert into tblcrevent(opcode,description,mode,requesttype) values('RELEASEQUARANTINEMAILFROMMAIL','RELEASE QUARANTINE MAIL FROM MAIL','458',2);
The opcode functions were 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 occurred.
A code from the Java frontend that handled 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 were checked for validity. If they were valid values, the following happened:
final EventBean eventByMode = EventBean.getEventByMode(363); ...redacted. final int sendWizardEvent = cscClient.sendWizardEvent(eventByMode, hashMap, sqlReader);
As we can see above, there was a new event code (363) that was sent to the backend. The bug we discovered was in the code that handled this in the backend.
The opcode was 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})$'; 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 received input from the frontend ($requestData) and how it attempted to verify some of the parameters we sent.
After the verification, if our parameters were valid, the following code was executed:
%mailreq=("mailaction"=>"$MAIL_FORWARD","subject"=>"$strSubject","toEmail"=>"$mailTo","attachmentfile"=>"$file","smtpserverhost"=>"$mailServerHost","fromaddress"=>"$mailFrom"); </code> out = OPCODE mail_sender json %mailreq
The code above set our request parameters into mailreq variable and called the mail_sender function (OPCODE). We will see how this opcode was executed and where exactly the RCE happened:
<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 did the same thing the other opcode did when it starts. It initialized variables (some from us or from the device if not specified).
After the variables were assigned, the following code block was 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 was, the command execution. The call here was EXECSH which called /bin/sh -c “ARGUMENTS”. With the execution happening using values we controlled, attackers could easily attain remote command execution, all without authentication.
Current Status of Cyberoam Products and Security Implications in 2025
It’s important to note that Cyberoam as a brand has now officially reached its End of Life status. The End of Life (EoL) process began with an End of Sales (EoS) announcement in 2019, during which Cyberoam was no longer available for purchase, but was still supported. The product has now officially entered the EOL stage, and the Cyberoam solution has officially migrated to Sophos Firewall.
Organizations still using Cyberoam devices in 2025 are at significant risk, as these systems are no longer receiving security updates. The current cybersecurity landscape has evolved considerably since this vulnerability was discovered, with VPN systems continuing to be prime targets for attackers.
Modern VPN Security Landscape
The security landscape for VPN solutions has seen significant changes since this vulnerability was discovered. In 2025, VPNs remain prime targets for attackers due to their role as network gateways.
VPNs are a prime attack vector for cybercriminals because they can typically leverage access into an entire network through the VPN tunnel. Recent security research has uncovered numerous critical vulnerabilities across various VPN providers.
Several major VPN vulnerabilities have been discovered in 2024-2025, including:
- CVE-2025-22457 – A buffer overflow vulnerability in Ivanti Connect Secure that allows remote code execution
- CVE-2025-0282 and CVE-2025-0283 – Critical vulnerabilities in Ivanti’s Connect Secure VPN discovered in January 2025
- CVE-2024-21762 – A critical security flaw in FortiOS SSL VPN allowing hackers to execute arbitrary code
- CVE-2024-53704 – An authentication bypass in SonicWall firewalls that allows remote attackers to hijack active SSL VPN sessions
Security Recommendations for VPN Users in 2025
If you’re still using Cyberoam devices, you should immediately consider migrating to supported solutions like Sophos Firewall, which offers modern security features and regular updates.
For all VPN users, we recommend the following security practices:
- Ensure your VPN appliances are kept up-to-date with the latest security patches
- Implement multi-factor authentication for all VPN access
- Apply the principle of least privilege for VPN user permissions
- Regularly audit VPN access logs for suspicious activities
- Consider implementing a zero-trust security model
- Use strong encryption protocols for all VPN connections
Update (April 2025): This research was originally covered on TechCrunch, read more here. Since then, the Cyberoam line has been replaced by Sophos Firewall products. According to recent findings, in 2025 there have been 0 vulnerabilities in Sophos SSL VPN Client, suggesting improved security in the successor products.