Portmanteau - Unix Driver IOCTL security tool

Jeremy Brown (jbrown3264/gmail)
Oct 2015

-----
About
-----

First and foremost, this tool is experimental. It was built in an attempt to learn more about the *nix IOCTL
attack surface within many drivers which expose devices to local users on the system. It may or may not even work
correctly. As such, it may of course panic the box you run it on (I suppose a thanks is in order in that case). Just plan
ahead for that and it will be fine. 


------------
Dependencies
------------

Libsqlite
OpenSSL (Base64 encoder)

@Fedora: sudo yum install libsqlite3x-devel openssl-devel


--------
Features
--------

Static code-based importing of IOCTLs from header files
SQLite database support (no clouds)
Random, generational-based fuzzing (testing only)
Automatic generation of proof-of-concepts for crash repro / analysis
Misc device driver security utility functions


-----------
Limitations
-----------
The console output interface for displaying devices isn't great; ideally this would be easily read as a web interface.

Only 32-bit x86 Linux was tested -- more work would be necessary to support multiple archs

Logging: ./portmanteau [...] > output.txt


-----
Usage
-----

To build, simply do a "make" inside the main directory. To build fresh, "make clean && make".

Running portmanteau with no options displays all the driver signatures in the local database. It attempts to read the drsig.db file
from the local running directory. If no database file is found, a blank one will be generated.

> Getting Started

<< Upon initial execution, assuming no drsig.db file has been provided, a new one will be created (silently).

$ ./portmanteau 

[Portmanteau] v1.0

Usage: ./portmanteau [-O for options]

Driver Signatures

No driver signatures: Use -i to import IOCTLs from a header file


<< Now we can import IOCTLs from a driver header. In this example, it's for the tun device in the kernel source tree.

$ ./portmanteau -i /usr/src/kernels/`uname -r`/include/uapi/linux/if_tun.h

[Portmanteau] v1.0

Adding found ioctl "TUNSETNOCSUM" to the database...
Adding found ioctl "TUNSETDEBUG" to the database...
Adding found ioctl "TUNSETIFF" to the database...
.....


<< Now when we run portmaneau again, we can see it displays the found IOCTLs which have been added to the database.

$ ./portmanteau 

[Portmanteau] v1.0

Usage: ./portmanteau [-O for options]

Driver Signatures

device_name     ioctl_name      ioctl_macro
-----------     ----------      -----------
6955F2B0AD      TUNSETNOCSUM    _IOW(T,200,int)
6955F2B0AD      TUNSETDEBUG     _IOW(T,201,int)
6955F2B0AD      TUNSETIFF       _IOW(T,202,int)
.....


<< The device_name is arbitrarily a substring of the given file path's SHA1 hash. You can rename it to be more descriptive as
   upon import, it's absolute device path is not retrieved.

$ ./portmanteau -u "device_name:6955F2B0AD:/dev/net/tun"

[Portmanteau] v1.0

Updating device_name '6955F2B0AD' to '/dev/net/tun'...


<< Now the database looks good and we're ready to fuzz or do other exploration.

$ ./portmanteau 

[Portmanteau] v1.0

Usage: ./portmanteau [-O for options]

Driver Signatures

device_name     ioctl_name      ioctl_macro
-----------     ----------      -----------
/dev/net/tun    TUNSETNOCSUM    _IOW(T,200,int)
/dev/net/tun    TUNSETDEBUG     _IOW(T,201,int)
/dev/net/tun    TUNSETIFF       _IOW(T,202,int)
.....


<< From this information, we know the target device name, what name the IOCTL goes by in source code and relevant data provided
   by the IOCTL's macro, including it's buffer type (int in this case). We can see that /dev/net/tun is accessible by everyone,
   so a worthy fuzzing target in our example.

$ ls -al /dev/net/tun
crw-rw-rw-. 1 root root 10, 200 Apr 25 10:53 /dev/net/tun


<< Portmanteau only needs the device name and optionally how many fuzzing iterations wish to be performed for all the IOCTLs in
   the database in order to fuzz.

$ ./portmanteau -D /dev/net/tun -N 1 -z

[Portmanteau] v1.0

Database has 22 ioctls for '/dev/net/tun'

Fuzzing TUNSETNOCSUM --> 0x400454c8
[1/1] iteration initiated for TUNSETNOCSUM

[+] PoC saved at 'poc/pmt-400454c8_1.c'
[+] Opened /dev/net/tun as O_RDWR
[~] Sending data...
.....


<< PoCs (proof-of-concepts) are kept in the local directory "poc". Only the last two from each fuzzing run are kept on disk; the
   others are removed during execution. This method has dual utility: it prevents us from filling up the disk and allows us, in
   the case of a kernel panic (one of the intended results with driver fuzzing), to keep the last two test cases for analysis
   and of course crash repro.

$ head poc/pmt-400454c8_1.c
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>

int main() {
int buffer = 0x68c9f939;

printf("Portmanteau PoC [id=1]\n\n");
printf("device: /dev/net/tun\nioctl: TUNSETNOCSUM (0x400454c8)\nbuffer: 0x%x\nbuffer type: int\n\n", buffer);
.....


<< Now you should have an idea of how to import IOCTLs to the local database and start a fuzzing run.

> Working with the Database

Manually add a signature: $ ./portmanteau -a "[/dev/ice/name]:[IOCTL-NAME]:[IOCTL-CODE]:[buffer-type]"

..... delete a signature: $ ./portmanteau -d "[/dev/ice/name]:[IOCTL-NAME]"

..... update a signature: $ ./portmanteau -u "[column_name]:[/long/boring/import/path.h]:[/dev/pwn]"

You must use colons to separate database parameters. For more granular database modifications, use the SQLite client.

-------
Support
-------

Development phase has completed for this project, but you're welcome to shoot a mail to jbrown3264/gmail for feedback or suggestions.

