Small Computer System Interface (SCSI) data transport is for connecting computers via peripheral devices on older devices. Even though this functionality is not common by itself and was written 15+ years ago, many Linux packages rely on this for weird reasons.
While visually scanning for bugs in this area, the author noticed that the string handling of SCSI did not look secure. There was a pointer to a string that was being copied via snprintf directly without any length checks! This creates a very large buffer overflow for all strings that are bigger than one page.
When a iSCSI transport handler is registered, it is available via the SYS file system at /sys/class/iscsi_transport/$TRANSPORT_NAME/handle. When read, the function leaks the handle, which is simply a pointer to a kernel address.
The final bug was an out of bounds kernel read (many of them). A netlink message can be crafted with malicious size lengths that are simple NOT validated. Using this, an OOB read can be performed by specifying invalid length values.
To get a full POC, we need to leak a bunch more data. The SCSI string buffers are neither NULL terminated or initialize the memory. So, by grooming the heap properly and specifying a size larger than the amount of data written, a leak occurs via the kstrdup function. Using this, we can break KASLR. The second vulnerability above leaks a pointer to the kernels module's global region.
The SLUB allocator maintains a cache of objects of the same size by using a singly linked list. By using our previous heap overflow to overwrite one of these pointers, we can specify the next location for where the kernel creates a chunk at. Of course, this requires proper heap grooming, which is discussed in the article. Additionally, this would not work with distros of Linux with harder checks on freelists.
The target of this overwrite is the iscsi_transport structure within the ib_iser module. This structure contains several function pointers with parameters that we control, making it an ideal target.
The functions are replaced with seq_buf_to_user and seq_buf_putmem in order to obtain a simple read/write primitive. There are some caveats to this, as the pointers are not exactly what we expect though.
With the read/write primitive in the kernel, how do we become root? The exploit calls run_cmd in the kernel SE Linux context. The function takes a parameter from the iscsi_transport struct that points to a shell script in userland. Because the attack never dereferences or executes memory from userland in the kernel, the exploit bypasses SMEP, SMAP and KPTI.
Overall, this is an awesome article! Sometimes, finding the right place to look for bugs is the most important thing. In this case, it netted the author some easy/obvious bugs.