-
Install Visual Studio Code.
-
Install the CodeQL extension for Visual Studio Code.
-
(Optionally) Install Docker if you want to build your own CodeQL database.
-
(Optionally) Install the CodeQL CLI if you want to build your own CodeQL database.
-
Clone this repository recursively:
git clone --recursive https://github.com/rvermeulen/codeql-workshop-elements-of-syntactical-program-analysis-cpp
-
Install the CodeQL pack dependencies using the command
CodeQL: Install Pack Dependenciesand selectexercisesandsolutions. -
Download the prebuilt database or build the database using the predefined Makefile by running
make. -
Select the Vulnerable Linux Driver database as the current database by right-clicking on the file
vulnerable-linux-driver-db.zipin the File explorer and running the commandCodeQL: Set Current Database.
In this workshop you will learn how to describe syntactical elements of the C/C++ programming language. With the goal of describing the user-mode entry point of the intentionally vulnerable Linux driver you will:
- Discover how QL represents C/C++ program elements.
- Learn to query program elements.
- Learn how to encapsulate descriptions of program elements using QL classes.
This workshop focusses on the syntactical parts. Some parts in this workshop can be generalized using more advanced techniques, such as dataflow analysis, that are covered in other workshops.
The intentionally vulnerable Linux driver project implements a Miscellaneous Character Driver to expose multiple vulnerabilities to learn about Kernel driver exploitation.
The miscellaneous character driver was designed for use cases that require a small device driver to support custom hardware or software hacks. To register or unregister a miscellaneous driver, the misc driver exports two functions for user modules, that can be found in the header linux/miscdevice.h, called misc_register and misc_unregister.
With the misc_register function as the starting point we will traverse function calls, expressions, structure definitions, and variable initializations to find the user module entrypoint. This entrypoint will be the start of future workshops that will discuss the vulnerabilities that can be found in this intentionally vulnerable Linux driver.
Find all the function calls in the program by implementing Exercise1.ql.
Hints
- The class
FunctionCallcan be used to reason about all the function calls in the program.
A solution can be found in the query Exercise1.ql
Find all the function calls to the function misc_register by implementing Exercise2.ql.
Hints
- The class
FunctionCallprovides the member predicategetTargetto reason about the called function. - The class
Functionprovides the member predicategetNameto get the name of the function.
A solution can be found in the query Exercise2.ql
Recall that predicates and classes allow you to encapsulate logical conditions in a reusable format.
Convert your solution to Exercise2.ql into a class in Exercises3.ql by replacing the none formula in the characteristic predicate of the MiscRegisterFunction class.
Besides relying on the name, try to add another property to distinguish the correct function.
Hints
- Each program element represented by the class
Elementcan be related to the primary file the element occurs in using the member predicategetFile. - Each program element has an absolute path that can be accessed using the member predicate
getAbsolutePathon the classFile. - The QL string type provides builtins such as
matchesandregexpMatchto match patterns in strings. Thematchesbuiltin member predicate interprets_to match any single character and%to match any sequences of characters in the provided pattern.
A solution can be found in the query Exercise3.ql
The definition of the driver is passed as a parameter to the misc_register function.
Obtain the argument to the call, determine the arguments type and primary QL class by implementing Exercise4.ql.
Hints
- The class
FunctionCallprovides the member predicategetArgumentto get a provided argument by index. - Each expression represented by the class
Exprhas a type that can be retrieved with the member predicategetType. - Each program element represented by the class
Elementhas a member predicategetPrimaryQlClassthat returns the QL class that is the most precise syntactic category the element belongs to.
A solution can be found in the query Exercise4.ql
The definition of the miscellaneous driver is defined by the structure miscdevice.
Implement the characteristic predicate of the class MiscDeviceStruct in
Exercise5.ql so we can reason about its use.
Hints
- The class
Structinherits the member predicategetNamefrom the classUserTypethat returns the name of the struct. - Each program element represented by the class
Elementcan be related to the primary file the element occurs in using the member predicategetFile. - Each program element has an absolute path that can be accessed using the member predicate
getAbsolutePathon the classFile. - The QL string type provides builtins such as
matchesandregexpMatchto match patterns in strings. Thematchesbuiltin member predicate interprets_to match any single character and%to match any sequences of characters in the provided pattern.
A solution can be found in the query Exercise5.ql
Now that we can reason about the structure miscdevice we can look for all it instantiations.
Implement the characteristic predicate of the class MiscDeviceDefinition in
Exercise6.ql so we use it to find all its instances.
Hints
- The class
Variablehas a member predicategetTypethat gets the type of this variable.
A solution can be found in the query Exercise6.ql
The instantiation vuln_device initializes 3 members of the miscdevice structure.
Find the type of the third field initialized with &vuln_fops by implementing
Exercise7.ql.
Hints
- The class
Structinherits the member predicategetAMemberfrom the classClassthat gets the zero-based indexed member declared in the struct. - The class
Fieldinherits the member predicategetTypefrom the classMemberVariablethat returns the type of the field.
A solution can be found in the query Exercise7.ql
With knowledge of the type of third field om the miscdevice structure we can now identify all file operation definitions such as vuln_fops.
Implement the characteristic predicates for the class FileOperationsStruct and FileOperationsDefinition in Exercise8.ql.
Hints
- The class
Structinherits the member predicategetNamefrom the classUserTypethat returns the name of the struct. - Each program element represented by the class
Elementcan be related to the primary file the element occurs in using the member predicategetFile. - Each program element has an absolute path that can be accessed using the member predicate
getAbsolutePathon the classFile. - The QL string type provides builtins such as
matchesandregexpMatchto match patterns in strings. Thematchesbuiltin member predicate interprets_to match any single character and%to match any sequences of characters in the provided pattern.
A solution can be found in the query Exercise8.ql
The single file operation definition vuln_fops is initialized with, among others, a function pointer for the field unlocked_ioctl.
This is the function that is invoked when a user-mode application performs the ioctl system call to communicate with the driver.
Extend the class FileOperationsDefinition with a member predicate getUnlockedIoctl that returns a Function with which the file operations definition is initialized in
Exercise9.ql.
Hints
- The class
Variablehas the member predicategetAnAssignedValuethat returns anExprrepresenting an expression that is assigned to this variable somewhere in the program. - The class
Fieldinherits the member predicatehasNamefrom the classDeclarationthat holds if the field has the provided name. - The class
ClassAggregrateLiteralhas the member predicategetFieldExprthat returns anExprthat is part of the aggregrate literal that is used to initialize the provided field.
A solution can be found in the query Exercise9.ql
We have successfully identified the miscellaneous driver definition, the file operations definition, and linked the ioctl handler to the file operations definition.
Extend the class MiscDeviceDefinition with the member predicate getFileOperations that returns a FileOperationsDefinition that the miscellaneous driver definition is initialized with in Exercise10.ql.
Hints
- The class
Variablehas the member predicategetAnAssignedValuethat returns anExprrepresenting an expression that is assigned to this variable somewhere in the program. - The class
Fieldinherits the member predicatehasNamefrom the classDeclarationthat holds if the field has the provided name. - The class
ClassAggregrateLiteralhas the member predicategetFieldExprthat returns anExprthat is part of the aggregrate literal that is used to initialize the provided field. - A class can be cast to a subclass using the syntax
variable.(Class).predicate(). For example, to cast an expressionexprto aAddressOfExprto get an operand of the expression you can use the syntaxexpr.(AddressOfExpr).getOperand(). - The class
AddressOfExprthat represents the expression taking the address&exprhas a member predicategetOperandthat returns the expression of which the address is taken. - The class
Variablehas a member predicategetAnAccessthat returns all the access to this variable.
A solution can be found in the query Exercise10.ql
In this final exercise we have to relate the call to misc_register to the miscellaneous driver definition so we can find the driver's entrypoint for user-mode.
Implement the characteristic predicate for the class MiscDriverUserModeEntry in Exercise11.ql that relates the classes MiscRegisterFunction, MiscDeviceDefinition, and FileOperationsDefinition to obtain the unlocked ioctl handler function.
Hints
- The class
Functionhas a member predicategetACallToThisFunctionthat returns all the function call to this function. - The class
FunctionCallinherits the member predicategetArgumentfrom the classCallthat returns the nth argument for this call. - A class can be casted to a subclass using the syntax
variable.(Class).predicate(). For example, to cast an expressionexprto aAddressOfExprto get an operand of the expression you can use the syntaxexpr.(AddressOfExpr).getOperand(). - The class
Variablehas a member predicategetAnAccessthat returns all the access to this variable.
A solution can be found in the query Exercise11.ql