Friday 21 June 2013

A Linux rootkit tutorial - Makefile and symbols

We're going to use the classic method of loading our rootkit as a device driver.  The reason this is used so often is that it's an established method of granting ring-0 access to code.  However - we don't really want to be a device driver (indeed for this reason I'm going to refer to the rootkit as a module from now on), and indeed loading ourselves as one will create various issues and could leave behind an irremovable audit trail (see load_module in kernel/module.c in the kernel source code for more).  Later we'll look at other methods we could use - which assume we can get ring-0 some other way - normally due to a device driver bug.

To start with however, we're going to go through the method required to build the rootkit at https://github.com/nnewson/km.git and why it's set-up that way.  I'm not however, going to go in-depth into how a basic module is built and how it works.  You can get that here - http://lwn.net/Kernel/LDD3/ - in the second chapter.  It does a much better job that I could.

One thing I will quickly discuss is that the Makefile we use will actually call the kernel's Makefile, which will set up the environment, and then call our Makefile to retrieve some variables.  It's using these variables that the kernel's Makefile builds the module for us.

These means we're somewhat limited by what we can do without Makefile - as it's almost like it's a include file rather than a proper Makefile.  For instance, most module Makefiles on the Internet explicitly name each source file - mainly because the wildcard pattern doesn't work when the kernel Makefile calls back into our one.  I get around this by explicitly using the ls shell command to find all the .c files.  It saves us having to edit the file every time we add a new compilation unit.

The Makefile has one real build option (that calls the kernel Makefile) and two phony ones.  The first is the standard 'clean' entry - which again calls the kernel Makefile to handle the actual clean.  The second is more interesting - it's called symbols.

Building Symbols
Before I discuss what building symbols does, it's important to describe why we need to do this at all. We're going to be using a lot of functions and global variables from the kernel - and most of the time these will be exported.  This means we refer to them by name (i.e. symbol) in our built module, and when it's loaded these are mapped to the actual addresses of the function or global variable by looking up these symbols.

Not all functions will be exported though.  Static ones certainly won't - indeed by default the kernel build system will only export symbols which are explicitly registered as such - using the EXPORT_SYMBOL/EXPORT_SYMBOL_GPL define.  So anything that isn't registered as such isn't going to be available to us - either our module will fail to build, or it simply won't load.

However - these unexported symbols are available to us - via the System.map file (stored in your /boot directory).  This file details the location and name of all the function and global variables (amongst other things) that the kernel uses.  As such we can grep through this file to discover memory address as a function.  For instance if we use the following command (which we need to sudo because we want to read from /boot):

sudo grep -w sysfs_mutex /boot/System.map-$(uname -r)

We get the following:

ffffffff81c5d940 D sysfs_mutex

This means the sysfs_mutex global variable is actually at memory address 0xffffffff81c5d940.  Of course this is only true for this kernel build - if you update or rebuild your kernel, you'd have to perform this check again.  We don't need to rebuild this for changes to our module however.

What this means is that using awk we can extract that memory address into a Makefile variable - which you see here:

SYSFS_MUTEX_SYMBOL = $(shell grep -w sysfs_mutex /boot/System.map-$(shell uname -r) | awk '{print $$1}')

This variable can then be placed in a file to be used by the code as the location of the sysfs_mutex variable.  So after looking up a number of various symbols, the code used sed to process a symbols_template.h file, and replace specific tokens with the addresses we looked up.  So building symbols should give us a symbols.h file in our src directory.

sudo make symbols

With this done you should now see the resulting entry in the symbols.h file as:

#define      c_sysfs_mutex_symbol  ffffffff81c5d940

This can now be assigned to a pointer - meaning we now have a pointer to the sysfs_mutex variable - and can access it as though it were exported.

static mutex*   g_sysfs_mutex = ( mutex* ) c_sysfs_mutex_symbol;

So, to finalise, to build the module the first time you need two Make commands:

sudo make symbols
make

Wednesday 19 June 2013

A Linux rootkit tutorial - an introduction

I haven't really done a lot with this blog since starting it - mainly because I became swamped with other things. However, I recently visited a webpage from Volatility Labs detailing detection of the KBeast rootkit.

Well - challenge accepted. I'm going to provide a tutorial on how to create a Linux rootkit that can hide from the methods detailed in the page above - and show you how we interact with the kernel to accomplish this.  All the code featured will be made publish on github here: https://github.com/nnewson/km

We'll go through it in various stages:

  1. Create our build environment.
    This will involve detailing the creation of a very basic device driver and the Makefile that will produce it.  It'll also go into loading and unloading the driver.  This is important - as this is a development project any change made by the rootkit should be reversible and allow you to unload it and return your system to it's previous state.
  2. Generate a symbols file.  
    While some of the symbols (functions, global variables etc.) are exported by the kernel, most aren't - and we're going to need a few of those to ensure we activate and deactivate our functionality in the correct way.  As such we're going to have to look some of these symbols up.  I'll show you how you can do this and integrate this process into the Makefile to auto-generate a symbols header file.
  3. Add a basic /proc control entry.
    For the sake of simplicity we're going to add a simply entry to allow us to pass commands to rootkit, allowing us to hide or show it when we want to.  Later we'll change this to something more covert, but for ease of understanding this will do for now.  This will provide a basic level of 'command and control'.
  4. Hide our rootkit from lsmod, /proc/modules and /sys/module.
    Our rootkit should be able to hide itself from this listing mechanisms (I'll also explain why our rootkit appeared in these lists in the first place).  Of course it should also be able to reappear in these list - so we can properly unload it as a normal device driver.
  5. Provide the ability to hook various function calls.
    Once a rootkit is hidden, one the most obvious things for to do is to intercept or filter functions calls.  It's this mechanism that allows you to add the hidden functionality of the rootkit.  The hooking needs to be done in such a way as to minimise detection without causing an exceptions. Again - we should be able to roll-back any changes made.
  6. Use a covert network communication channel.
    Once we've got the system hidden and doing work, the next obvious thing is to provide an ability to communicate with a remote source - for command and control.  This will allow us to take instructions and export any date collected.
The code on github already provides the first four stages, and we'll be adding to this as we go along - but each stage will be detailed in turn and we'll build on them one step at a time.

In the next update, I'll write about what a rootkit actually is, why we use the insmod command to load it a device driver (and the various problems this creates) and how we'd ideally like to do this.

Hopefully, eventually I'll be able to get around to talking about the really interesting stuff - namely covertly loading our rootkit, remotely providing code updates and patching as well as dealing with anti -forensics.

All the work for this will be done on the latest kernel on the development machine I'm using - namely 3.8.0-19.

Tuesday 27 September 2011

Small steps in assembly

The first thing most people really want to do in assembly is connect it to normal C code (or the high level language of your choice).  Assembly is all well and good, but no one wants to rewrite the OS (unless you do...in which case there are many other places you should be heading to).

Let's start then with two straight forward programs.  One piece of assembly we can call from C, and one that is the main program and that then calls a C function.

Calling Assembly from C
The first piece of C code looks very simple, namely:
#include <stdio.h>

extern    int    assemAdd( int input );

void main( void )
{
    printf( "%d\n", assemAdd( 57 ) );
}
assemAdd.c

This piece of code simply calls an external function - namely assemAdd - which we'll implement in assembly. It passes 57 to the function and prints the result.

Let's have a look at the assembly code:
global    _assemAdd

section .text

_assemAdd:
    mov    eax, [ esp + 4 ]
    add    eax, 5
    ret
assemAdd.s

We'll go through what the assembly code does line by line in detail next time (mainly because I'm going to have to discuss calling conventions), but we'll get this built and check the outcome.

Here's how we build these file and run the resulting executable:
$ nasm -f win32 assemAdd.s
$ gcc assemAdd.c assemAdd.obj -o assemAdd.exe
$ ./assemAdd.exe
62
$

The first line assembles the assemAdd.s file into the win32 format (because we're running this on Windows). This will produce an assemAdd.obj file.

The second line will compile the assemAdd.c file, and link the resulting object file with the assemAdd.obj file created by nasm.

It then creates the assemAdd.exe executable, which we run and see our output. As I said, we'll be going through this in detail next time, but see if you can work out what's happening on your own.

Calling C from Assembly
In this example we're going to create an assembly program that calls one of the libc methods - namely printf. Again, we'll go into detail next but for now we'll get something working:
global    _main

extern    _printf


section    .data

printStr:    
    db    'This is a test print', 0

section .text

_main:
    push    ebp
    mov     ebp, esp
    push    printStr
    call    _printf
    leave
    ret
assemMain.s

Now, if we assemble this using nasm we can link it using gcc. Gcc, by default, will automatically include libc into the build unless told not to, so we don't have to do anything complicated:
$ nasm -f win32 assemMain.s
$ gcc assemMain.obj -o assemMain.exe
$ ./assemMain.exe
This is a test print
$

So, there we go. We'll dig into things a little deeper next time (including explaining why assembly functions have a preceding '_') but for now we've managed to get some code built and working. Which is always a good place to start from!

Thursday 22 September 2011

Setting up a development environment

Before we can start playing around with some assembly we're going to download a development environment.  Although I will be playing around with Linux and Mac OS assembly in the future, at present I'm going to stick to Windows to begin with.  This means we're going to need an assembler and a C compiler suite.

Why are we going to use a C compiler suite?  Well, because we're going to link C code to assembly to show how we interact with normal code, and because it provides us with a linker.

Here's what you're going to need:
  1. A text editor.  I suggest Vim but feel free to use your favorite.  If you do end up using Vim, can I suggest you head over to gregsexton.org - one man's love affair with Vim taken to scary new levels.
  2. An assembler.  We're going to use the Netwide Assembler - because it uses the Intel assembly syntax (we'll get to that later) and it's regularly updated.
  3. A C compiler suite.  Cygwin is just too bloated for my liking so I'm going to use MinGW.  You go to the main site here but it's probably easier to go straight here to find the Windows installer.
Make sure that when you install MinGW, that you select the MSYS Basic System as shown below:


Next time we'll look at setting up our MSYS environment so we can use all our tools in the one terminal.

The reason this sprung into life...

This blog is being put together to investigate low level programming techniques.  Originally the aim is to explain basic assembly language concepts to some friends, but hopefully I'll be able to use it as a platform to various programming topics.