![23-12 Pool Party BH Blog Post](https://www.safebreach.com/wp-content/uploads/2023/12/23-12-Pool-Party-BH-Blog-Post.webp)
Microsoft Windows is the world’s most widely used desktop operating system (OS). It accounts for more than 70% of market share as of February 2024, according to market research firm Statista, and can be found on more than 1.4 billion active devices. As with any software vendor, Microsoft continuously evolves their OS with the release of updated versions that offer new features and functionality. Due to its large customer base, backwards compatibility is a key element of their release strategy—they must ensure users maintain uninterrupted access to the features, functionality, and third-party software they use on a regular basis. But, backwards compatibility comes with risk, as there are known issues in earlier versions of the OS that may remain unfixed and, as a result, remain available for exploitation by malicious actors.
My most recent research project—which I first presented at Black Hat Asia 2024—set out to explore the concept of whether seemingly harmless known issues could be exploited to uncover vulnerabilities and, ultimately, pose a significant security risk. Specifically, I chose to explore the known issue associated with the DOS-to-NT path conversion process—this known issue has been left unfixed for years to support backwards-compatibility.
Below, I’ll first provide a high-level overview of the key findings and takeaways from this research. Next, I’ll dive into the details about the DOS-to-NT path conversion process. I’ll explain how rootkits operate today and how my research process uncovered rootkit-like techniques to conceal and impersonate files, directories, and processes. Then, I will explore the vulnerabilities I discovered and provide examples and demonstrations about how they can be exploited. Finally, I will highlight the vendor response and explain how we are sharing this information with the broader security community to help organizations protect themselves.
Overview
Key Findings
When a user executes a function that has a path argument in Windows, the DOS path at which the file or folder exists is converted to an NT path. During this conversion process, a known issue exists in which the function removes trailing dots from any path element and any trailing spaces from the last path element. This action is completed by most user-space APIs in Windows. By exploiting this known issue, I was able to uncover:
- One remote code execution (RCE) vulnerability (CVE-2023-36396) in Windows’s new extraction logic for all newly supported archive types that allowed me to craft a malicious archive that would write anywhere I chose on a remote computer once extracted, leading to code execution.
- Two elevation of privilege (EoP) vulnerabilities: one (CVE-2023-32054) that allowed me to write into files without the required privileges by manipulating the restoration process of a previous version from a shadow copy and another that allowed me to delete files without the required privileges.
In addition to leading me to these vulnerabilities, the MagicDot paths also granted me rootkit-like abilities that were accessible to any unprivileged user. I discovered how a malicious actor—without admin privileges—could hide files and processes, hide files in archives, affect prefetch file analysis, make Task Manager and Process Explorer users think a malware file was a verified executable published by Microsoft, disable Process Explorer with a denial of service (DoS) vulnerability, and more.
Takeaways
While this research attempted to exploit a known issue specifically within Microsoft Windows, known issues persist from version to version within most publicly available software products. As such, we believe the implications are widely relevant to all software vendors and suggest several important takeaways:
- Known issues are risky to leave unfixed, even if they appear to be harmless. We have proven that an unfixed, known issue could be exploited to develop not one, but multiple vulnerabilities and post-exploitation techniques. We believe it is critical for software vendors to better understand the risks associated with leaving known issues unfixed and reevaluate their policies and procedures around this issue to better protect the users and organizations that use their products.
- We consider an attacker running code on a remote endpoint with weak privileges to be a very common scenario. Cybersecurity professionals tend to believe that an attacker’s malicious processes or malicious files cannot be hidden without admin privileges or without the ability to run code in the kernel. This is simply not the case. This research has proven that it is possible to manipulate information returned to users as an unprivileged user and without intervention in the chain of API calls that retrieve information. It’s important that the cybersecurity community recognize this risk and consider developing unprivileged rootkit detection techniques and rules.
- While Microsoft addressed the specific vulnerabilities we found, the DOS-to-NT path conversion known issue, which was the root cause of the vulnerabilities, remains unfixed. That means there might be many more potential vulnerabilities and post-exploitation techniques to find using this issue.
- Software developers can make their code safer against these types of vulnerabilities by utilizing NT paths rather than DOS paths. Most high-level API calls in Windows support NT paths. Using NT paths avoids the conversion process and ensures the provided path is the same path that is being actually operated on.
![Experience the Power of SafeBreach. Schedule a Personalized Demo Now.](https://no-cache.hubspot.com/cta/default/43692056/44f4ccac-63a0-478b-853a-2571fd8dff7a.png)
Background
Microsoft Windows Path Types & Path Conversion Process
In the Windows OS, users are able to engage with files or folders by executing functions on them. For example, if a user wants to open a text file with Notepad, Notepad will call a win32api function—the CreateFile function—and open the file by referencing the path where the file exists. Windows supports a few different path types. The most popular path that we all normally use is called a DOS path (e.g., C:\Users\User\Documents\example.txt).
However, when a win32api function like CreateFile is called, it actually calls another underlying function—the NtCreateFile function—to perform the operation of opening the file. Since NtCreateFile asks for an NT path and not a DOS path, the DOS path is converted to an NT path prior to calling NtCreateFile, which varies from the DOS path (e.g., \??\C:\Users\User\Documents\example.txt). A set of conversion functions that most, if not all, conversions go through (RtlpDosPathNameToRelativeNtPathName) are responsible for this conversion. During the conversion process, a known issue exists in which the function removes trailing dots from any path element and any trailing spaces from the last path element, resulting in what I have dubbed “MagicDot” paths. A few examples of this conversion process are included below:
DOS Path | NT Path (MagicDot) |
C:\example\example. | \??\C:\example\example |
C:\example\example… | \??\C:\example\example |
C:\example\example<space> | \??\C:\example\example |
C:\example\example<space><space> | \??\C:\example\example |
C:\example.\example | \??\C:\example\example |
C:\example<space>\example | \??\C:\example<space>\example |
When I understood how this conversion process worked, I knew it provided the perfect opportunity for some rootkit magic. I also discovered that this known issue had been documented in an article called “The Definitive Guide on Win32 to NT Path Conversion” by James Forshaw with Google Project Zero. For those interested, the article provides useful information about path types in Windows and how each one is converted into the NT path type.
Rootkits
One of the primary goals of rootkits is to help attackers remain undetected and conceal information from other users. The two most popular rootkit types we see in the wild are user-space rootkits and kernel rootkits. We will examine these in more depth below to understand how the rootkit-like abilities I discovered as part of this research differ.
User-Space Rootkits
A user-space rootkit typically attempts to hook user-space API calls that retrieve information, call the original function, remove malicious information, and return false information to the caller.
![MagicDot SafeBreach](https://www.safebreach.com/wp-content/uploads/2024/04/01-MagicDot-SafeBreach-1024x357.png)
To run a user-space rootkit, admin privileges are required as well. That is because the rootkit will need to hide from users on the computer, including the admin, which means, running in processes with admin privileges.
Kernel Rootkits
A kernel rootkit runs in the kernel and basically tries to hook system calls to return false information to the user-space process that is asking for the information.
![MagicDot Rootkit Windows Or Yair](https://www.safebreach.com/wp-content/uploads/2024/04/02-MagicDot-SafeBreach.png-1024x517.png)
To run a kernel rootkit, you need to have the ability to run in the kernel. This usually requires admin privileges and dealing with multiple security features, like Patch Guard, Driver Signature Enforcement, Driver Blocklist, and HVCI. For that reason, we see much less kernel rootkits these days.
The Research Process
My background research raised a major question: do attackers really require admin privileges to conceal themselves? We have seen many attackers who have managed to gain remote access to a computer and obtain their objectives without requiring admin privileges. Further, those attackers are often operating on fully patched machines, which means that in order to gain admin privileges, they need to use a 0-day escalation of privilege vulnerability, which is difficult to find.
That said, I did not find any existing rootkit-like abilities that allowed an unprivileged user to conceal elements from all users. What I realized is that there can be a method to conceal information without being a part of the chain of calls and without needing admin privileges. Because of the known issue that exists in the path conversion function, I could create paths that would then—intentionally—be converted to other paths, which may or may not exist. As a result, those paths could manipulate the information retrieved by a user.
![MagicDot Rootkit Windows Or Yair SafeBreach](https://www.safebreach.com/wp-content/uploads/2024/04/03-MagicDot-SafeBreach.png-1024x384.png)
So, my research goals were established. I wanted to:
- First, find a way to develop rootkit-like abilities that do not require admin privileges utilizing the file name known issue for concealment of things like files, directories, and processes. My assumption was that when normal software operated with problematic MagicDot paths, I would be able to manipulate what a user would see.
- Second, prove that unfixed, known issues could be leveraged to uncover vulnerabilities and become a significant security risk even if they initially appear to be harmless.
Files & Directories
Concealment
To begin, I focused on creating an inoperable file or directory. By placing a simple trailing dot at the end of a file name or by naming a file or a directory with dots and/or spaces only, I could make all user-space programs that use the normal API inaccessible to them. Such directories could not be listed or have any other operation done on them. Similarly, any files using this naming convention could not be operated on either. Users were not able to read, write, delete, or do anything else with them.
Another way I found to hide files or directories was by using archive files. Because Windows Explorer has difficulties functioning with my MagicDot paths, I simply ended a file name in an archive with a dot to prevent Explorer from listing or extracting it. As a result, I was able to place a malicious file inside an innocent ZIP—whoever used Explorer to view and extract the archive contents was unable to see that file existed inside.
Impersonation
Beyond making files and directories inoperable, I also wanted users to be presented with different contents and information when they tried to retrieve them. To accomplish this, I created what I called an “Impersonated File or Directory.” For example, if there was a harmless file called “benign” I was able to create a malicious file in the same directory but name it “benign.” As a result, when a user reads the malicious file, the content of the original harmless file would be returned instead. I could also create an impersonated file or directory not in the same directory as the file or directory I wanted to impersonate. For example, the file “\??\C:\Windows.\System32\svchost.exe” (notice the trailing dot after “Windows”) impersonates to “C:\Windows\System32\svchost.exe”.
Another backwards-compatible feature that I was able to leverage to impersonate files or directories is called “short names,” also known as 8.3 file names. These names—for example, TEXTFI~1.TXT— were the normal name back in the days of old DOS and Windows versions, but are still supported by the Windows OS. While they have significant limitations that do not exist in modern file names, we can still use and reference them with paths. And, in much the same way as before, I was able to craft paths that were converted to other paths that referenced the short names of path elements. For example, if I named a file/directory “benign.” using an NT path, file operations on it affected a file with the short name “benign” instead. Using short names for the impersonation makes it even more concealed, as short names are not presented to users at all unless they specifically want to view short names, for example, with “dir /x” (“/x” lists the short names too).
Demo
To see these capabilities in action, the following demo shows a presumably normal folder named “test” with some files, an archive, and folders inside. In the first part of the demo, we’ll see how Windows presents the folder’s contents all as “normal.” In the second part, however, we’ll see that Linux identifies that the folder contents actually contain malicious files (using WSL).
Processes
Once I determined what I was able to accomplish with files and directories, it was time to test out my capabilities to conceal and impersonate processes.
Concealment
First, I ran a process using the NtCreateUserProcess function from the NT path “\??\C:\Windows.\blabla\blabla.exe”. Then, when accessed with the DOS path “C:\Windows.\blabla\blabla.exe”, it was converted to a non-existent NT path (“C:\Windows\blabla\blabla.exe”). As a result, I was able to prevent a user from using a tool to view the properties of the executable of the process or carry out any operation on it.
Impersonation
To move beyond concealment into impersonation, I created what I called an “Impersonated Process.” I started by creating an “Impersonated File” as I described in the previous section using the path “\??\C:\Windows.\System32\svchost.exe” and executed it. This allowed me to impersonate the original svchost.exe and ensure any file operations on the executable would affect it. As a result, if a user attempted to use a tool to view the processes on the current computer, the tool would report false information about the process. Task Manager and Process Explorer, for example, would tell the user that the executable of the process was signed by Microsoft.
I was also able to manipulate prefetch analysis tools in the same way. Analyzing a prefetch file of such an impersonated process presented false information that actually belonged to the original svchost.exe executable.
Demo
To see these capabilities in action, the following demo shows how we were able to impersonate the svchost.exe executable and manipulate how it was viewed in Task Manager and Process Explorer.
Process Explorer Anti-Analysis Technique
As part of my research process to see how else I might use the MagicDot path problem to my advantage, I reverse engineered Process Explorer to see if this issue would lead to any vulnerabilities. While I did not find any more vulnerabilities as a result of the MagicDot issue, I did find a way to empower my unprivileged rootkit and add a nice anti-analysis technique.
In Process Explorer’s code, I saw that it writes a process name into a buffer that has the length of 256. The size is limited to 256 because the name of a process is determined by its executable name, and the NT file system (NTFS) does not support a longer file name than 255 characters (0xFF). So far, so good.
![RootKit Windows Or Yair Research](https://www.safebreach.com/wp-content/uploads/2024/04/04-MagicDot-SafeBreach-1024x92.png)
But then Process Explorer also adds the PID of the process surrounded by parenthesis to that buffer, while the final size is again limited to 256. After a few seconds, I immediately noticed that the total length of the buffer after the addition of the PID could be bigger than 256.
![SafeBreach Research MagicDot](https://www.safebreach.com/wp-content/uploads/2024/04/05-MagicDot-SafeBreach-1024x128.png)
But how is it any good for us to go over the 256 limitation? The code here uses the wcscat_s safe function, which is one of a large set of Safe C-Runtime functions. These functions are a more secure version of normal C-Runtime functions. Why? Usually because they add a few security checks of their own. In this example, the wcscat_s function will not allow a buffer overflow to happen.
But, what happens if their checks fail? According to Microsoft’s documentation, they will invoke an error handler. Using the wcscat_s example above, if the size limit was passed, the invalid_parameter error handler is called.
![Or Yair Research Windows](https://www.safebreach.com/wp-content/uploads/2024/04/06-MagicDot-SafeBreach.png)
But what does it do? Again, according to Microsoft’s documentation, “The invalid parameter handler dispatch function calls the currently assigned invalid parameter handler By default, the invalid parameter calls _invoke_watson, which causes the application to close and generate a mini-dump.”
Basically, a developer of a Windows program can assign an invalid parameter handler. But if one is not assigned, invoke_Watson is called and it closes the application and creates a dump file.
![Microsoft Windows MagicDot](https://www.safebreach.com/wp-content/uploads/2024/04/07-MagicDot-SafeBreach-1024x95.png)
So what does that mean? Safe C-Runtime functions seem safe, but using them for validation if you haven’t done the validation yourself, can actually create a Denial of Service (DoS) security risk.
To summarize, this vulnerability allows an attacker to fully disable Process Explorer for all users (including admins) from being able to run as long as a process with a certain name is running. An attacker could leverage the following process to exploit this:
- Rename an executable to a name that has a length of 255 characters without a file extension.
- Run that executable using NtCreateUserProcess.
- Going forward, Process Explorer crashes instantly if it runs, no matter under which privileges.
Demo
To see these capabilities in action, the following demo shows how I was able to create a process name that was 255 characters long so that it went over the 256 limit after the addition of the PID string. As a result, I created a Denial of Service (DoS) in Process Explorer.
Vulnerabilities
As noted above, my second research goal was to find vulnerabilities to prove that this seemingly harmless known issue within the DOS-to-NT path conversion process could be a security risk. To accomplish this, I explored how different attack surfaces function with the MagicDot paths and managed to uncover three different vulnerabilities that exist due to this known issue. Below, I’ll provide an overview of each vulnerability, beginning with the most simple EoP vulnerabilities and working up to the most significant RCE vulnerability.
EoP Deletion Vulnerability
The EoP deletion vulnerability allowed me to delete files without the required privileges. To demonstrate how it works, let’s say there is an existing folder at C:\demo that contains two files: a.txt and b.txt. In addition, we will assume I don’t have permission to perform any operation on the files themselves, but I do have permission to create new folders in the demo folder. I create a folder inside the demo folder called …<space> and inside, I write a file named c.txt (note that the name or content of the file do not matter).
![Windows MagicDot Vulnerability](https://www.safebreach.com/wp-content/uploads/2024/04/08-MagicDot-SafeBreach-300x253.png)
Then when an administrator attempts to delete the …<space> folder, the entire demo folder is deleted instead.
To see this process in action, check out the demo below.
How does that happen? When File Explorer wants to delete a folder, it first lists all of the files inside it recursively. However, listing the …<space> folder is equal to listing the demo folder itself, as the path conversion totally removes the name of the folder. So:
“C:\demo\…<space>” == “C:\demo”
![Or Yair SafeBreach Labs](https://www.safebreach.com/wp-content/uploads/2024/04/09-MagicDot-SafeBreach.png)
Trying to list a non-existent …<space> folder inside the …<space> folder actually lists the original … folder. That is because the path conversion in this case removes the last path element but does not do the same to its upper folder, as it ends with a space. Therefore, naming a folder with dots but ending it with a space will not lead to an endless loop when listing it recursively. So:
“C:\demo\…<space>\…<space>” == “C:\demo\…<space>”
![MagicDot](https://www.safebreach.com/wp-content/uploads/2024/04/10-MagicDot-SafeBreach.png)
However, after File Explorer lists all the files inside, it deletes each one of them, and finally, it deletes the top folder too. The top folder in this case is the C:\demo\…<space>, which is just equal to C:\demo, leading to the deletion of the entire folder by accident.
To summarize, an attacker could leverage the following process to exploit this vulnerability:
- Create a directory with the name “… “ (3 dots + space) with any file inside.
- Wait for a privileged user to try and delete this directory.
- The parent directory of the “… “ directory would be deleted instead, along with all of its files.
EoP Write Vulnerability
This EoP write vulnerability (CVE-2023-32054) allowed me to write into files without the required privileges by manipulating the restoration process of a previous version from a shadow copy. To achieve this, all I need to do is create a folder with the same name as my target folder, but add a trailing space to its name. Then, I create files with the same names and locations as my target folder in my test<space> folder. I set their content to be what I want written into the files inside my target folder.
![EoP Write Vulnerability](https://www.safebreach.com/wp-content/uploads/2024/04/11-MagicDot-SafeBreach.png-300x253.png)
As a result, I am able to control what the shadow copy remembers of the test folder. When the shadow copy is taken and later restored, the contents of my test<space> folder with malicious content will be written into the original test folder.
To see this process in action, check out the demo below.
To summarize, an attacker could leverage the following process to exploit this vulnerability:
- Create a directory with the same name as a target directory, but add a space character at the end of its name.
- When a privileged user saves a shadow copy and then later restores a previous version of the restricted directory, the previous versions of the files that will be restored inside the restricted directory will be taken from the directory created by the attacker.
RCE Vulnerability
This RCE vulnerability (CVE-2023-36396) allowed me to craft a malicious archive that would write a file anywhere I chose on a remote computer once extracted, leading to code execution. I began by exploring the 11 new archive types that Microsoft just added native support for in Windows:
- .rar
- .7z
- .tar
- .tar.gz
- .tar.bz2
- .tar.zst
- .tar.xz
- .tgz
- .tbz2
- .tzst
- .txz
I thought symbolic links could be a good lead for finding such a vulnerability. But how dangerous could symbolic links in archives really be? Even if a symbolic link points outside of an archive’s context to somewhere else on the computer, it wouldn’t be a threat because archive extractors do not allow the functionality of writing into symbolic links, only creating them.
In Windows, you must have the “Create Symbolic Links” user right in order to create symbolic links or have Developer Mode on. However, symbolic links are supported by archive extractors—and specifically by the newest extractor that was just added to Windows for the new archive types—and are commonly used in archives. For example, many open-source code projects use them, so they are a valid attack surface.
I set out to see how I could leverage my MagicDot paths. I hoped that if I placed two files with the same name in an archive and added a trailing dot to one of them, I would be able to affect the archive’s extractor. Ideally, the extractor would first extract the file with the normal name with its content and then, when it extracted the second file with the trailing dot, its content would be written into the previously extracted file. A secure behavior of the extraction should identify if the file being written to already exists, and if so, provide the following prompt:
![RCE Vulnerability Microsoft](https://www.safebreach.com/wp-content/uploads/2024/04/12-MagicDot-SafeBreach.png)
This is exactly why the extraction uses the CREATE_NEW create disposition—to detect whether the file exists and, if it does, provide an ERROR_FILE_EXISTS. However, only the FILE_ATTRIBUTE_NORMAL flag was given to this call, not the FILE_FLAG_OPEN_REPARSE_POINT flag. As a result, when a symbolic link with a target that doesn’t exist is opened, the function will not fail and create the target of the symbolic link.
![SafeBreach Labs](https://www.safebreach.com/wp-content/uploads/2024/04/13-MagicDot-SafeBreach-1024x287.png)
That means that I was able to set the first file to be a symbolic link, and then write content into the symbolic link’s target using the second file with the dot. I set the target of the symbolic link to be an executable in the startup folder, and I had remote code execution.
To see this process in action, check out the demo below.
To summarize, an attacker could leverage the following process to exploit this vulnerability:
- Create an archive of one of the newly supported types in Windows (7ZIP, RAR, TAR, TAR.GZ, TAR.XZ…) with two files inside.
- The files should be named the same, but one should have a trailing dot at the end of the name. The file with the normal name is a symbolic link towards a path of a new file to create on the file system. The file name with the trailing dot would contain the content to write into the target of the symbolic link. (The file records inside the archive should be ordered so the file with the dot in its name will be the second and last file to ensure it is written second).
- When the victim completes the extraction, the extraction logic first creates the symbolic link, but when it tries to create the second file with the dot in its file name, it accidentally writes its content into the symbolic link. This results in the creation of the target of the symbolic link with the written content. The vulnerability requires the “create symbolic links” user right or Windows “developer mode” to be enabled.
Vendor Response
All issues were reported to the Microsoft Security Response Center (MSRC) in 2023. Microsoft acknowledged these issues and took the following action:
- Remote Code Execution (CVE-2023-36396, CVSS: 7.8): The vulnerability was confirmed, reproduced, and fixed by Microsoft. It was assessed as an RCE with an “Important” severity.
- Elevation of Privilege (Write) (CVE-2023-32054, CVSS: 7.3): The vulnerability was confirmed, reproduced, and fixed by Microsoft. It was assessed as a privilege elevation (PE) with an “Important” severity.
- Elevation of Privilege (Deletion): The vulnerability was reproduced and confirmed by Microsoft. However, they did not issue a CVE or a fix, but instead provided the following response: “Thank you again for submitting this issue to Microsoft. We determined that this issue does not require immediate security service but did reveal unexpected behavior. A fix for this issue will be considered in a future version of this product or service.”
- Process Explorer Unprivileged DOS for Anti-Analysis (CVE-2023-42757): The vulnerability was reproduced, confirmed, and fixed by the engineering team of Process Explorer in version 17.04. CVE-2023-42757 was reserved for this vulnerability by MITRE. MITRE confirmed the vulnerability with Microsoft and will publish the CVE once online publication of the details is available.
The rest of the report cases that we submitted were closed because Microsoft defined the behaviors we described as post-exploitation techniques.
Conclusion
This research is the first of its kind to explore how known issues that appear to be harmless can be exploited to develop vulnerabilities and, ultimately, pose a significant security risk. We believe the implications are relevant not only to Microsoft Windows, which is the world’s most widely used desktop OS, but also to all software vendors, most of whom also allow known issues to persist from version to version of their software. To help mitigate the potential impact of the vulnerabilities identified by this research, we have:
- Responsibly disclosed our research findings to Microsoft as noted above. Microsoft did address the vulnerabilities, but has decided to leave the DOS-to-NT path conversion known issue unfixed.
- Shared our research openly with the broader security community here and at our recent Black Hat Asia presentation to enable software vendors and the organizations using them to better understand the risk posed by this, and all, unfixed known issues.
- Provided a research repository that includes tools that enable the verification of these vulnerabilities and serve as a basis for further research and development.
- Added original attack content to the SafeBreach platform that enables our customers to validate their environment against the vulnerabilities and techniques outlined in this research to significantly mitigate their risk.
For more in-depth information about this research, please:
- Contact your customer success representative if you are a current SafeBreach customer
- Schedule a one-on-one discussion with a SafeBreach expert
- Contact Kesselring PR for media inquiries
About the Researchers
Or Yair is a security research professional with more than six years of experience, currently serving as the Security Research Team Lead for SafeBreach Labs. Or started his professional career in the Israel Defense Force (IDF). His primary focus lies in vulnerabilities in Windows operating system’s components, though his past work also included research of Linux kernel components and some Android components. Or’s research is driven by innovation and a commitment to challenging conventional thinking. He enjoys contradicting assumptions and considers creativity a key skill for research. Or has already presented his vulnerability and security research discoveries internationally at conferences such as Black Hat USA 2023, Black Hat Asia 2024, Black Hat Europe 2022, SecTor 2023, RSAC 2023, Security Fest 2023, CONFidence 2023, and more.
![CTA - Schedule a Demo](https://no-cache.hubspot.com/cta/default/43692056/41bb54bf-8a01-44b0-af72-7bb211b4301b.png)