CWE-696, Incorrect Behavior Order, covers a product that performs several related behaviors in the wrong order. Each step is present and correct on its own. The sequence is what breaks. MITRE lists web based, not language specific and not technology specific as platforms, and records altered execution logic as the consequence.
How it happens
The record's examples are a good tour.
Dropping privileges. CVE-2007-1588: a C++ web server calls Process::setuid before Process::setgid, which prevents it from dropping privileges and potentially lets CGI programs run with higher privileges than intended. CVE-2007-5191: file system management programs call setuid and setgid in the wrong order and do not check the return values, letting attackers gain unintended privileges.
Using before ready. CVE-2019-9805: creation of a packet client occurs before initialization is complete, resulting in a read from uninitialized memory and memory corruption.
Limits that arrive late. CVE-2022-37734: the lexer in a Java based GraphQL server does not enforce a maximum number of tokens early enough, allowing excessive CPU consumption.
The setuid case is worth spelling out. Once a process gives up root by changing its user ID, it may no longer be allowed to change its group ID. So the group must be dropped first.
// Vulnerable: after setuid, the process can no longer change its group
setuid(unprivileged_uid);
setgid(unprivileged_gid); // may fail, and the failure is ignored
// Fixed: drop group first, then user, and check both
if (setgid(unprivileged_gid) != 0) { abort(); }
if (setuid(unprivileged_uid) != 0) { abort(); }
What an attacker gains
MITRE lists integrity impact through altered execution logic. In the examples that means retained privileges, memory corruption or a CPU exhausting request. The attacker needs no special trick beyond supplying the input that reaches the misordered code.
How to find it
The record names no detection methods. Review sequences where safety depends on order: privilege changes, initialization, validation and then use, limit and then work. Ask for each one what happens if the first step is late or fails. Test privilege dropping by checking the process identity afterwards, not by trusting the calls.
How to fix it
- Put validation and limits before the expensive or dangerous work.
- Finish initialization before publishing an object to other code.
- Drop group privileges, then user privileges, and check every return value.
- Write a test that verifies the end state, such as the effective user and group.
Quick check
Why must setgid come before setuid when dropping root?
After the user ID is changed the process may lack the right to change its group ID, leaving it with the privileged group.