Table of Contents



(Click here to return to main LKRG page)

LKRG

LKRG is free and Open Source project distributed primarily in source code form. You can download it and prepare custom build by yourself. However, if you would rather use a commercial product tailored for your specific operating system, please consider LKRG Pro, which is distributed primarily in the form of “native” packages for the target operating systems and in general is meant to be easier to install and use while delivering optimal performance. Additionally, you will help in development of the project (economically). LKRG Pro is available <here>.

LKRG - experimental branch

Given that LKRG correctly prevents unauthorized modifications, experimental branch includes three useful user-mode protections (called “Protected Features”) which are not available in the “main” branch:

“Experimental” branch has completely different communication channel logic (please read this section) comparing to “main” one which uses sysctl interface (please read this section). Additionally, it also includes a self-defense code which is not available in “main” one.

LKRG-experimnetal is being hosted on the bitbucket git repository, which can be cloned to the local directory via following command:

$ git clone https://bitbucket.org/Adam_pi3/lkrg-experimental.git

Protected features

Experimental branch of LKRG includes a specially designed Protected Features as well as different communication channel comparing to the “main” branch.

Protected process

The main idea behind this feature is to be able to harden certain processes even from the highest privileged accounts (like “root” account) and to warrant that some secrets remain inaccessible. Proper implementation of Protected Process (PP) is not trivial from a couple of reasons:

There is couple of “official” ways how to interact with the processes, and all of them needed to be hardened:

Additionally, process can be affected via direct memory access:

LKRG leverages *kprobes interfaces to “lock down” all possible ways of interacting with processes. As an end-result no one from user-space is able to interact with the process protected by LKRG. There is one exception for compatibility reasons:

LKRG maintains its own red-black tree to track all PP. When process dies it is automatically removed from the list. There are 2 ways how the process might become Protected Process:

Protected file

The main idea behind this feature is to be able to harden certain files even from the highest privileged accounts (like “root” account) and to warrant that some secrets remain inaccessible. Proper implementation of Protected File (PF) is not trivial from a couple of reasons:

Files can be modified / deleted using official API, as well as indirectly changed via raw disk access. Both methods must be stopped:

There could be 2 ways of doing it:

2nd option is used by LKRG client user-mode application. Additionally, LKRG makes it PF by default.

Protected logs

Works exactly the same as PF feature but allows file to be opened only in “append” mode. The main idea behind this feature is to be able to harden certain files even from the highest privileged accounts (like “root” account) and to warrant that some secrets remain inaccessible but at the same time give a possibility to append new information to it. Such a functionality might be desired for certain files (e.g. log files) to provide warranty that information which was already written down won't be modified neither deleted.

Limitation

Proper implementation of the Protected Features requires from LKRG to leverage and virtually extend CAP_SYS_RAWIO capability. Unfortunately, some of the software (e.g. Xorg or doesmu) requires this capability for proper behavior (e.g. accessing /dev/mem device which LKRG must lock down). If Protected Features are being enabled such a software won't properly work.

Mitigation

If you still want to use Protected Features and run the software which requires access to /dev/mem device (uses CAP_SYS_RAWIO capability) you might initialize and run desired software before you load LKRG into the kernel. By doing it, desired software should properly gain necessary resources and correctly use it and LKRG will lock down access to that device after that fact. Note: If for any reasons such a software tries to regain access to restricted device it won't be allowed anymore and proper functionality will be broken.

LKRG files - experimental branch

Comparing to the “main” branch, more files are generated in “experimental” one. After successful compilation of the project, “output” directory is created with the following files:

p_lkrg.ko

This is the core of the project - same as in “main” branch. Module itself accepts one parameter which determines the default logging level. By default, it is “active” level (levels will be described in details in the next section). Each level of logging is represented by numbers. By providing numbers between 0-4 or 0-6 (if compiled with debugging option) LKRG will immediately use this level even during installation in the kernel memory. It is a very useful option for debugging purpose when LKRG encounters a problem during installation in the system:

Installation

Installation of LKRG is exactly the same as loading normal kernel module. LKRG will fail to install if it detects the following pre-existing files:

After successful installation, new randomly generated password (of random length) will be stored in the password file and correct initialization of protected features will create indicator file. This password is used for changing control data in LKRG. During execution of user mode client, it will ask to provide this password. Please be aware that you need to delete the password file as soon as possible after installation and do not allow accessing it from the machine where LKRG is running. The best option is to store it on 3rd part USB device not connected to the machine. Protected features file is automatically protected as Protected File by LKRG. No-one from user-space is capable to delete this file which is a good indicator if LKRG is loaded and avoids “multiple” load of the project:

As soon as system is installed it starts the work. If default logging level is used, LKRG produces one short sentence saying that system is clean unless corruptions are detected. Otherwise LKRG informs where corruption happened, e.g.:

If system is started with more detailed log level, more information will be visible, e.g.:

(this is not the latest version of the project so might not have acquired all information)

If debugging compilation is available, it may produce enormous amount of information regarding the running system (including every function name where LKRG is entering and leaving, etc.). Sometimes even default “active” log level might be too noisy (every time when LKRG checks the system, one-line status information will be produced in case the system is clean). That’s why “none” log level option was introduced. System at that log level is quiet until abnormal situation is detected by LKRG. In that case only alerting logs will be produced.
After LKRG is inserted in the kernel memory it can’t be unloaded. You must reboot machine to remove it unless “unhiding” compilation has been done (by default it should be) and that option will be visible in user-mode client.

client/cli/p_lkrg-client

This is user mode application which consumes (parses and changes) kernel mode configuration module. You can look at it as a “wrapper” which makes communication channel between administrator and LKRG as easy as it could be. First let’s look closer at a kernel configuration module:

This module accepts two parameters:

What user-mode client essentially does, is that it tries to parse kernel module (ELF_REL object) to discover .text section (which is next modified) and tries to load it in the kernel by providing password as module parameter. If administrator changes PF, second parameter is filled as well. In theory user could do it by hand but it will require more technical work (like parsing ELF and finding correct offset in the .text section where control structure resides).

User mode client application and kernel control module is compiled at the same time as LKRG. If debugging compilation is done, user-mode application will understand 2 extra logging levels which is not available in normal/production compilation, e.g.:

The following options are available:

  1. timestamp (-t switch) – changes how often kernel timer will be launched (kernel timer periodically calls integrity function). It can’t be less than 5 seconds (to not eat too much system resources) and not more than 1800 seconds (half an hour) – to not be silent for too long
  2. log level (-l switch) – it might be number between 0-4 or 0-6 (if debugging compilation was used). Strong debug provides very useful data to identify where could be a specific problem with LKRG (if it ever appears). Unfortunately, it produces tons of logs per execution and must be used only for debugging purpose, not as normal run.
  3. Blocking module functionality (-b switch) – only two options are available:
    • 0 – do NOT lock the kernel and allow to load kernel module
    • 1 – lock the kernel and do NOT allow to load kernel module
  4. Hiding functionality (-u switch) – if “unhiding” compilation has been done, LKRG has extra functionality of (un)hiding itself:
    • 0 – hide LKRG (if is not already hidden)
    • 1 – unhide LKRG (if it is not already unhidden)
  5. Protected Process functionality (-P switch) – if user wants to (un)protect any existing process, this switch informs about desired action. IT MUST BE USED WITH ”-p” switch!
    • 0 – unprotect process
    • 1 – protect process
  6. (Un)protect process pid (-p switch) – if user chose to (un)protect process via ”-P” switch, this argument expects PID of the desired process. MUST BE USED WITH ”-P” switch!
  7. Protected File functionality (-S switch) – if user wants to (un)protect any existing file, this switch informs about desired action. IT MUST BE USED WITH ”-s” switch!
    • 0 – unprotect file
    • 1 – protect file
  8. (Un)protect file path (-s switch) – if user chose to (un)protect file via ”-S” switch, this argument expects path to the desired file. MUST BE USED WITH ”-S” switch!
  9. Force (-f switch) – forces LKRG to run integrity function right now.
  10. Configuration module path (-m switch) – full path to the kernel configuration module. If not provided, default one will be used.

Example of usage:

* Yellow box is only visible in debug compilation (echoing password)

This specific example of the communication with LKRG disables blocking modules functionality. At the beginning I tried to insert floppy module, but it was correctly blocked by LKRG. Next I tried to change this procedure (to allow modules to be loaded). I entered the correct password and user-mode client successfully pushed new configuration to the LKRG. Next I tried to load module again and it was successful. This operation could be reversed (if blocking module feature is not enabled and modules can be loaded, we can enable blocking module feature again). Entire operation can be seen in the logs (active log level was used in the example):

First, LKRG detected new module activity and blocked it from loading. Next (after disabling blocking module feature), we can see in logs a floppy module correctly initialized itself and printed some information. LKRG rebuilt database and next time when integration routine was launched, the new module was taken into account (it was already in the database) and module itself was protected.

At the end of running the user mode client application a warning message is always printed. Client detects if administrator entered correct password by analysing if control module was correctly loaded to the system. If password is wrong, LKRG by default should block module loading (blocking module feature is enabled by default) and returning code is analysed by user-mode client. In case blocking module feature is disabled, loading control module will always be successful and there is no way to detect whether password was correct or not.

User-mode client was specially designed to limit the possibility of leaking entered password. Custom password reading function was implemented to read character one-by-one, while buffering and echoing the entered character were disabled (echoed character is replaced by a star). Entire password is kept for short period of time in memory and zeroed as soon as used in syscall to load the module.

Application loads entire control module to the memory. Any modification is done only in private memory page of the process and none of the modifications is reflected in the real control module file. Client loads modified module to the kernel using memory pointer – because the process is a part of Protected Process nobody can ever interact with this memory.


Protected Process example

After successful installation of LKRG in the system, by default the following files are being marked as protected:

  1. User-mode client application
  2. Control module
  3. LKRG itself

As soon as anyone in user-land executes LKRG client application it becomes a protect process. Even superuser (like root) can’t interact with such processes:

Any existing process in the system can be dynamically added or removed from the Protected Process group. As soon as it happens process will be guarded or unguarded. E.g.:

(This section will be updated if needed.)


Protected File example

After successful installation of LKRG in the system, by default the following files are being marked as protected:

  1. User-mode client application
  2. Control module
  3. LKRG itself

Even superuser (like root) can’t modify / delete / rename / overwrite such files:

Any existing file in the system can be dynamically added or removed from the Protected Filegroup. As soon as it happens process will be guarded or unguarded. E.g.:

(This section will be updated if needed.)

Protected Logs example

TODO

Self-defence

Implementation of Protected Features relies on *kprobes interface. It is known that this interface was not designed for a security purpose and can be easily manipulated by administration of the system. The Following *kprobes configuration files are exposed by “debugfs”:

  1. /sys/kernel/debug/kprobes/blacklist
  2. /sys/kernel/debug/kprobes/enabled
  3. /sys/kernel/debug/kprobes/list

From the LKRG perspective the most critical are “enabled” and “list”:

LKRG makes special effort to block those interfaces:

Otherwise bypassing LKRG would be trivial! E.g.:

Additionally, LKRG removes any reference to itself from sysfs, debugfs also unlinks itself from module list, deletes any KOBJs related to itself and cleans ddebug_table. All of that is not sufficient, but in the current stage of the project it is not a priority. It will be improved!