108 - An Android Kernel Bug & a Chrome+Edge Exploit
This is a interesting primitive, an unsigned 32bit integer can mistakenly be kept unsigned after it is supposedly converted to a signed 64bit integer and passed in somewhere expecting a signed value.
The root of the bug is in
InstructionSelector::VisitChangeInt32ToInt64 which as the name implies (there is a unsigned version also) is to decide what instruction to output to convert a signed Int32 into a signed Int64. The problem is that it actually operate conditionally, doing a sign extension if the input was signed, a zero-fill if unsigned. This one its own might not be an issue but other instructions assume the output will be signed.
As it appears this is normally used only with signed values, hitting it is as bit of an edge case. The optimizer when optimizing out an XOR by a value that is provably 0 (does nothing) will simply remove the XOR operation completely, the XOR operation implicitly would output signed values. But after optimizing, if the loaded operand was unsigned, it’ll be passed unsigned into the
ChangeInt32... procedure triggering the bug condition.
There will be a third post in this blog series regarding exploiting this primitive, but at the time of this podcast that was not yet released.
Patch: They removed the conditional logic and only do a sign extended conversion now.
Basic idea here is that you could mount unintended paths due to embeding null-bytes in acceptable mount and generally improper handling of null-bytes within a mount source. While Go does not provide any special handling for strings containing null bytes, the
send_mountsources written in C just iterates over the provided message mounting every null-terminated substring and passes the resultant fds to the child. An attacker with partial control of those mount sources may be able to bypass restrictions implemented by something like Kubernetes on hostpaths that can be mounted.
This can also be utilized by exploiting another bug, an integer overflow in the serialization process. To forward the mount sources to
nsexec.c the type
Bytemsg is used, this is sent along with a small header containing a 16bit unsigned integer reflecting the size of the data. The
Len() method however can overflow the 16bit size. This creates a situation where the reciever things the bytes over the 16bit length are part of a new message allowing arbitrary messages to be crafted; such the
CLONE_FLAGS_ATTR message which can be used to provide clone flags enabling the final container to run with the host’s namespace.
Len() bug was patched by panicing when length goes beyond 16bits. The patch commit does not seem to deal with the embedded null-bytes.
This is straight forward, yet subtle bug, basically taking a reference to a file while it is actively being deleted leading to a use after free despite holding onto a reference.
get_file_rcu instead of
get_file which will check for this condition and bail.