High
CVE-2021-3490
CVE ID
AttackerKB requires a CVE ID in order to pull vulnerability data and references from the CVE list and the National Vulnerability Database. If available, please supply below:
Add References:
CVE-2021-3490
MITRE ATT&CK
Collection
Command and Control
Credential Access
Defense Evasion
Discovery
Execution
Exfiltration
Impact
Initial Access
Lateral Movement
Persistence
Privilege Escalation
Topic Tags
Description
The eBPF ALU32 bounds tracking for bitwise ops (AND, OR and XOR) in the Linux kernel did not properly update 32-bit bounds, which could be turned into out of bounds reads and writes in the Linux kernel and therefore, arbitrary code execution. This issue was fixed via commit 049c4e13714e (“bpf: Fix alu32 const subreg bound tracking on bitwise operations”) (v5.13-rc4) and backported to the stable kernels in v5.12.4, v5.11.21, and v5.10.37. The AND/OR issues were introduced by commit 3f50f132d840 (“bpf: Verifier, do explicit ALU32 bounds tracking”) (5.7-rc1) and the XOR variant was introduced by 2921c90d4718 (“bpf:Fix a verifier failure with xor”) ( 5.10-rc1).
Add Assessment
Ratings
-
Attacker ValueHigh
-
ExploitabilityLow
Technical Analysis
So this is a pretty interesting bug in the Linux Kernel from versions 5.7.x to 5.13-rc4. It occurs due to kernel/bpf/verifier.c
having a function named adjust_scalar_min_max_vals
which is responsible for adjusting the minimum and maximum values an eBPF register can represent, which is used as part of eBPF bounds tracking of registers to make sure that when users submit eBPF programs for the kernel to run, they don’t end up going out of bounds and leaking kernel memory or doing other nasty stuff.
adjust_scalar_min_max_vals
has a few switch statements for various types of operations, such as BPF_AND
, BPF_OR
, BPF_SUB
, etc. For the purpose of this we will focus just on the AND vulnerability but the OR vulnerability was also introduced at the same time, and the BPF_XOR
vulnerability was introduced with 5.10-rc1 of the Linux kernel.
asdf
asdf
asdf
asdf
asdfasdfasdf
asdf
asdfasfd
asdf
asdfa
sfasdf
asdf
adsf
asfd
asfd
asdf
asfdasfd
I have rated the exploitation of this vulnerability as fairly difficult as whilst chompie1337 did push out a PoC for this bug which we have used to transform it into a Metasploit module, and this is fairly platform agnostic, the actual exploitation details involve a lot of fairly advanced concepts and many steps to get successful code execution to work.
That being said the value of this is high as pretty much any Linux system you can think of is going to have eBPF enabled unless they have explicitly disabled it via sudo sysctl kernel.unprivileged_bpf_disabled=1
which will prevent unprivileged users from being able to load eBPF programs. This setting however is not default and will need to be applied to each affected machine so I’d imagine some people will either not be aware of this, or will end up deploying machines and forget that this setting needs to be set at some point, opening up the door for exploitation of this and similar issues.
When executing BPF_AND
’s switch code, as can be seen at https://github.com/torvalds/linux/blob/c9e73e3d2b1eb1ea7ff068e05007eec3bd8ef1c9/kernel/bpf/verifier.c#L7575-L7579, Of particular note is the call to scalar32_min_max_and
and the lines at https://github.com/torvalds/linux/blob/c9e73e3d2b1eb1ea7ff068e05007eec3bd8ef1c9/kernel/bpf/verifier.c#L7083-L7093.
In this case the code will call tnum_subreg_is_const(src_reg->var_off);
to check if the source register’s lower 32 bits are known, and tnum_subreg_is_const(dst_reg->var_off);
to check if the destination register’s lower 32 bits are known. If both the source register’s lower 32 bits are known and the destination register’s lower 32 bits are known, then it assumes that scalar64_min_max_and
will be called (it will) and that its safe to skip updating the registers for the known 32 bit values.
However here lies a problem, as if we look at scalar_min_max_and
, which is the official name for scalar64_min_max_and
, we see something odd in its code at https://github.com/torvalds/linux/blob/c9e73e3d2b1eb1ea7ff068e05007eec3bd8ef1c9/kernel/bpf/verifier.c#L7116-L7127. Specifically it uses tnum_is_const(src_reg->var_off);
and tnum_is_const(dst_reg->var_off);
to determine if the full contents of the 64 bit source and destination registers are known. Only if the full 64 bit content of the registers are known, does it then mark the destination register as having a known value.
This leads to an edge case where if the operation involves registers where the upper 32 bits are unknown but the lower 32 bits are known, then we can make eBPF improperly verify the register as the 32 bit version of the call will assume the 64 bit version of the call will have properly validated the register’s contents, however in reality the register will not be verified since its upper 32 bits are still unknown.
The patch fixes this by ensuring that scalar32_min_max_and
and related affected functions now call __mark_reg32_known
on the destination register before returning if the lower 32 bits of the destination and source registers are known. The contents of this function can be seen below:
/* Mark the unknown part of a register (variable offset or scalar * value) as known to have the value @imm. */ static void __mark_reg32_known(struct bpf_reg_state *reg, u64 imm) { reg->var_off = tnum_const_subreg(reg->var_off, imm); reg->s32_min_value = (s32)imm; reg->s32_max_value = (s32)imm; reg->u32_min_value = (u32)imm; reg->u32_max_value = (u32)imm; }
Essentially this code will ensure that all the signed and unsigned minimum and maximum versions of the register are properly set, ensuring the correct bounds will be conserved when calling the final boundary updating functions are called to determine the 64 bit value, whereas before when calling the __update_reg_bounds
, __reg_deduce_bounds
and __reg_bound_offset
functions within adjust_scalar_min_max_vals
, a condition could occur whereby due to some internal logic on how to calculate these values, there could be a case where the bounds would be incorrectly determined.
More details on exactly how this is done can be found in the The Vulnerability
section of chompie1337’s excellent paper at https://www.graplsecurity.com/post/kernel-pwning-with-ebpf-a-love-story, but suffice to say its possible to make a register with a maximum 32 bit value of 0 and a minimum 32 bit value of 1, thereby severely messing up the logic of the bounds tracker.
Would you also like to delete your Exploited in the Wild Report?
Delete Assessment Only Delete Assessment and Exploited in the Wild ReportCVSS V3 Severity and Metrics
General Information
Vendors
- canonical,
- linux
Products
- linux kernel,
- linux kernel 5.13,
- ubuntu linux 20.04,
- ubuntu linux 20.10,
- ubuntu linux 21.04
References
Miscellaneous
Additional Info
Technical Analysis
Report as Emergent Threat Response
Report as Exploited in the Wild
CVE ID
AttackerKB requires a CVE ID in order to pull vulnerability data and references from the CVE list and the National Vulnerability Database. If available, please supply below: