You can use Ocular to investigate your C language applications. This tutorial illustrates how, by using the example of CVE-2016-6480 Linux Kernel. The vulnerability is a race condition that exists in the Linux Kernel version 4.7, in the
ioctl_send_fib in drivers/scsi/aacraid/commctrl.c function.
This article will should you how to use Ocular to investigate your applications written in C. More specifically, you will see how Ocular finds the presence of CVE-2016-6480 Linux Kernel, a race condition in the ioctl_send_fib function. This function can be found in drivers/scsi/aacraid/commctrl.c in Linux kernels 4.7 (or earlier).
Before proceeding, please download the Linux kernel containing this vulnerability:
Step 1: Create the Code Property Graph (CPG)
Create a Code Property Graph (CPG) for the vulnerable driver:
Once you have the CPG, start Ocular:
When prompted, load the CPG you generated:
workspace to ensure that your CPG has been loaded. If
true, you're ready to proceed with your investigative queries.
Examine the Code
Examine the interaction from user to kernel space with
copy_from_user. To determine if any data from user space to kernel space is copied, use:
Ocular returns the following:
This indicates that there doesn't seem to be any problems with data from user space to kernel space.
To look at flows from
println(sinkArguments.reachableByFlows(cpg.identifier).l.size) returns the number 302.
Of interest is an estimate determining if the arguments of
copy_from_user are sanitized. There are no direct definitions at
if expressions, but information that flows into
if expressions is available. To access this information using
Main.scala, add the following lines
reachableByFlows is used to construct and print out the flows. To filter all that detail, use
reachableBy to have Ocular identify only the sources that are hit, rather than the details of the data flow paths. The following query collects the sources that are hit as a set
- Restricts the flows running to expressions that involve the less or greater keyword. Note that internally each binary operation (+,-,>,< etc.) is also treated as a function
- Tracks data dependency back to each identifier it hits and collected into a set
Now, check to see if there is an intersection between these two sets, which provides an estimate on which arguments of
copy_from_user might be sanitized
This query returns the following:
The return shows that most potential checks involve some kind of a size element as expected.
Some outputs from
kfib as their first argument, which may be a pointer giving access to a header. The size of
kfib seems to be involved with check
kfib->header.Size. To confirm this in the source code (
commctrl.c, line 90), use:
Use one of the following two queries to filter for
copy_from_user looking for kfib as an argument:
To print the return:
You should see output as follows:
Next, find the data flow from these sinks to a common ancestor which defines
kfib. This helps ensure that there is no other definition of
kfib which might have a double fetch.
This pattern is similar to reaching definitions to sanitizers. The start tells Ocular to start a fresh traversal at the given node. In this case, head and last are filtered to get those nodes. The output is as follows: