VNX-421: Race Condition During Access to Alternate Channel
Reports a Unix domain socket or FIFO created at a fixed path in a shared world-writable directory (/tmp, /var/tmp, /dev/shm) in C, C++, Go and Python, as a literal, a constant or a C buffer filled by snprintf, and a Windows CreateNamedPipe in a file that never passes FILE_FLAG_FIRST_PIPE_INSTANCE, where another local user can claim the channel's name first.
| Field | Value |
|---|---|
| Rule ID | VNX-421 |
| Severity | Low |
| SARIF level | note |
| Kind | sast |
| Languages | C, C++, Go, Python |
| CWE | CWE-421 |
| CAPEC | CAPEC-66 |
| ATT&CK | T1190 |
| CWSS vector | CWSS:1.0/TI:L/AP:RU/AL:S/IC:N/FC:LT/RP:RU/RL:S/AV:L/AS:N/IN:A/SC:A/BI:L/DI:H/EX:L/EC:N/P:L |
| Tags | race-during-alternate-channel unix-socket named-pipe cwe-421 |
| Source | internal/sast/rules/vnx-421.rego |
Overview
VNX-421 maps to CWE-421: Race Condition During Access to Alternate Channel. A service that opens a local side channel for its own clients (a control socket, a FIFO, a named pipe) races every other local user for that channel’s name. If the name lives somewhere anyone can create files, the attacker creates it first: the service fails to bind, or its clients connect to the attacker’s endpoint and hand it their requests.
Reported:
- C and C++:
mkfifo,mkfifoatandmknod, and asockaddr_un.sun_pathfilled bystrcpy,strncpy,strlcpy,snprintf,memcpyor a designated initializer, with a fixed path under/tmp,/var/tmpor/dev/shm, written as a literal, a#defineor a constant. - Go:
net.Listen/net.ListenPacket("unix", ...),net.UnixAddr{Name: ...},net.ResolveUnixAddrandsyscall.Mkfifo/unix.Mkfifowith such a path, includingfilepath.Join("/tmp", ...)andfilepath.Join(os.TempDir(), ...). - Python:
os.mkfifo, socket.bindin a file usingAF_UNIX,asyncio.start_unix_server/create_unix_serverandsocketserver.UnixStreamServer/UnixDatagramServerwith such a path, includingos.path.join(tempfile.gettempdir(), ...). - Windows named pipes:
CreateNamedPipe,CreateNamedPipeAandCreateNamedPipeWin a file that never passesFILE_FLAG_FIRST_PIPE_INSTANCE.
What Gets Flagged
strncpy(addr.sun_path, "/tmp/backupd.sock", sizeof(addr.sun_path) - 1); // FLAGGED
const ctlSocket = "/tmp/agent.sock"
ln, err := net.Listen("unix", ctlSocket) // FLAGGED
server = await asyncio.start_unix_server(handler, path="/tmp/agent-async.sock") # FLAGGED
What Is Not Flagged
- Sockets and FIFOs in a directory only the service can write:
/run/<service>/,$XDG_RUNTIME_DIR, amkdtemp()oros.MkdirTempresult. - Client-side connects (
net.Dial("unix", ...),socket.connect(...)). - Paths computed only at run time, test files, comments and string literals that are not passed to these calls.
CreateNamedPipein a file that creates the first instance withFILE_FLAG_FIRST_PIPE_INSTANCE.
Remediation
Create the channel inside a directory the service owns and others cannot write:
mkdir("/run/backupd", 0700);
strncpy(addr.sun_path, "/run/backupd/control.sock", sizeof(addr.sun_path) - 1);
On Windows, create the first pipe instance with FILE_FLAG_FIRST_PIPE_INSTANCE, so the call fails if someone else already owns the name.
References
- CWE-421: Race Condition During Access to Alternate Channel
- Microsoft: CreateNamedPipe, FILE_FLAG_FIRST_PIPE_INSTANCE
Suppressing this rule
Put a nosec comment on the line, or on line 1 to cover the whole file. Use the comment marker of the file’s language (#, //, -- or ;):
# nosec vnx-421
// nosec vnx-421
Or record an ignore rule: vulnetix ignore add --rule vnx-421 --reason "...". See Suppressing findings.