The Linux kernel is the "engine" of Linux - it is a free and open-source Unix-like operating system kernel that was first created in 1991 by Linus Torvalds.
Kernel versions are identified by a three level numbering in the form of x.y.z - at the time of writing this web page it was up to version 6.8.x - but 6.6.x is the current long term support version.
The version of the kernel being used in any particular Linux distribution can be seen using the command
uname -r
If the version number contains a string of characters after the initial x.y.z then that shows that the kernel has been modified by the developers of the distribution.
The Linux kernel was originally designed around the i386 processor, but subsequently was expanded to work on numerous different types of processor.
Version 3.7.x was the last version which supported the i386 processor.
Early versions of the Linux kernel were monolithic - ie - they were one big chunk of code.
By version 1.2 the concept of kernel modules was being introduced.
By version 2.2 loadable kernel modules were totally established as one of the characteristics of the kernel.
The modular design of the kernel means that it can insert and remove loadable kernel modules as required - a loadable kernel module is an object file that contains code that can extend the kernel functionality - they can be loaded as required on the fly.
When a kernel module is no longer needed, it can be unloaded - if a module is unloaded it is no longer consuming resources like memory.
Most of the device drivers used in Linux are in the form of kernel modules.
If the functionality of the kernel needs to be expanded to handle a new application or service or hardware it is a lot easier to add a new loadable kernel module than it is to rewrite the whole kernel.
Loadable kernel modules are not exclusive to Linux, many other operating systems such as Solaris, FreeBSD, macOS, Netware also use them.
Once loaded, a loadable kernel module doesn`t sit on the fringe of the kernel, it becomes an integral part of the kernel.
A disadvantage of loadable kernel modules is that it can lead to memory segmentation - when a kernel is loaded into memory it sits in a contiguous block of memory - when a loadable kernel module is subsequently loaded, it may well end up sitting in a separate block of memory, away from the memory block containing to the kernel - this can cause a performance hit.
Loadable kernel modules can add several different types of functionality to the kernel, including functions such as -
device drivers - a device driver is designed for a specific piece of hardware, and the kernel uses it to contact that piece of hardware without having to know about the harware
file system drivers - these interpret the contents of file systems
system calls - userspace applications use system calls to access resources in the kernel. For example, there are system calls to read a file, create a new process, and close the program
network drivers - the network operator interprets the network protocol - there are dozens of different network protocols - it would be very wasteful of system resources to have the ability to interact with all these protocols built into the kernel
netfilter modules - netfilter relies extensively on loadable kernel modules - more on this below
Loadable kernel modules do come with a security risk - if an attacker can install a well designed malicious kernel module onto a Linux installation he can alter the way the kernel works - many rootkits are based on malicious loadable kernel modules.
A rootkit is a set of software tools used by an attacker after gaining unauthorized access to a system - it can have many different functions, such as -
hide itself from administrators
set up covert communication channels to outside
modifying system calls
hiding files
modify the "lsmod" tool so that it doesn`t show the malicious LKM or LKM`s
extract data
key logging
escalate privileges
modify the boot-up sequence so that it gets reloaded automatically after each restart
The attacker can control more or less everything the kernel does, and control what the system is reporting to the administrators in terms of processes, ports, services, hard drive space, etc.
As far as the security of the Linux installation is concerned - it`s game over !
One of the early loadable kernel module rootkits was Knark - Knark was designed around kernel version 2.2/2.4. It hides ports, files and processes from the administrator. This rootkit was very well writen, and is very powerful - it has been well used by black hats, and there are a lot of compromised machines out there.
Knark comes with another LKM, modhide, which hides the LKM most recently loaded prior to its invocation, making Knark invisible to the command "lsmod".
A later LKM rootkit is Diamorphine - this is an LKM rootkit for Linux Kernels 2.6.x/3.x/4.x/5.x/6.x - the code for it is open source and easily available, and bits of it are regularily used in other LKM rootkits.
I have found references to other KLM rootkits - including
adore-ng - linux rootkit adapted for 2.6 and 3.x
suterusu - for Linux 2.6.x/3.x on x86, and ARM
Reptile - runs on Linux kernel 2.6.x/3.x/4.x
rkduck - runs on kernel version 4.x
wukong - an LKM rootkit for Linux kernel 2.6.x, 3.x and 4.x
keysniffer - A Linux kernel module to grab keys pressed in the keyboard
reveng_rtkit - a LKM based rootkit targeting Linux Kernel: 5.11.0-49-
Ftrace-hook - another open source LKM rootkit
lkm-rootkit - developed on kernel version 4.4.13 - loads two other modules - "nf_reject_ipv4" and "nf_reject_ipv6"
Now some of the above were designed as tools for kernel developers, but they can also be used for malicious purposes.
There may well be others - but even those that I have listed show that the security risk from loadable kernel modules is significant.
Apart from the usual security precautions to keep black hats out, one posible way forward is using the command
sysctl kernel.modules_disabled=1
This sets a toggle value indicating if modules are allowed to be loaded in an otherwise modular kernel. This toggle defaults to off (0), but can be set true (1). Once it has been set to "1", modules can be neither loaded nor unloaded, and the toggle cannot be set back to false.
Some modules required for system operation may normally be loaded during system operation and not at boot. To ensure these modules are available, they must be loaded at startup prior to when loading is disabled. To load these modules, list them in a file located in /etc/modules-load.d.
Another strategy would be to require all LKM`s to be signed - the signing of LKM`s is part of Secure Boot in UEFI, and has been supported since kernel version 3.7. But it is quite a complex process.
Somewhat ironically, one of the best defences against LKM rootkits is to use a kernel that doesn`t use loadable kernel modules - by disallowing loadable modules entirely, the system’s attack surface can be drastically reduced.
There are hundreds of kernel modules - in Linux they are stored in
/lib/modules/<kernel-name>
Each kernel module is a separate file with the file name extension of .ko
Many kernel modules depend on one or more other kernel modules - a complete list of kernel module dependencies for each kernel version is held in the file -
/lib/modules/<kernel-name>/modules.dep
There is a lot of data about the modules in the folder -
/sys/module
The list of loaded modules is shown in the file -
/proc/modules
The netfilter firewall capability of the Linux kernel has evolved along with the evolution of the kernel - kernel version 2.2.x introduced ipchains.
Kernel version 2.4.x introduced a significant change by bringing in iptables instead of ipchains.
Kernel version 3.13.x introduced nftables, but iptables continues to interface with netfilter, even up to the present kernel version of 6.6.
There are a lot of kernel modules designed as part of the netfilter firewall - as far as I can see, the modules associated with netfilter are stored in three locations -
/lib/modules/<kernel-name>/kernel/net/netfilter
/lib/modules/<kernel-name>/kernel/net/ipv4/netfilter
/lib/modules/<kernel-name>/kernel/net/ipv6/netfilter
It probably depends on the kernel version, but typically you might find around 200 modules across the three folders - but few of them are loaded by default - the ones that are typically loaded by default might include the following -
iptable_filter
iptable_nat
iptable_mangle
ip_tables
ip6table_filter
ip6table_nat
ip6table_mangle
ip6_tables
nf_nat
nf_conntrack
nf_conntrack_ipv4
nf_defrag_ipv4
nf_nat_ipv4
nf_conntrack_ipv6
nf_defrag_ipv6
nf_nat_ipv6
x_tables
xt_tcpudp
xt_conntrack
The above list is typical of the modules that would be required to enable netfilter to provide a basic stateful firewall for port and address filtering on TCP and UDP.
There are a lot of interactions between many of these loadable modules and thus a lot of dependencies - the command "lsmod" prints out dependencies in a pseudo table form.
I have really struggled to find out how kernel modules are loaded during start up - partly because there have been different mechanisms used to manage the startup, and different distribution developers have different ways of doing it.
In the past SystemV was used, and it was based on shell scripts in /etc/init.
Then systemd was developed - I think by Red Hat - and several distributions now use systemd.
However this isn`t clear cut - as there is a lot of contention in the Linux world as to whether systemd should be used at all - the opponents of systemd have many arguments against it.
Some distribution developers refuse to use it.
On top of that systemd contains - or did contain - software which allows backward compatibility with SystemV and the init files - so some distributions use or used a mixture of SystemV and systemd.
There are a very few websites that mention the "systemd-modules-load.service" - which is run during the early part of the bootup sequence.
There is mention of folders and files which contain lists of modules which should be loaded - these include -
/etc/modules-load.d/*.conf
/run/modules-load.d/*.conf
/usr/lib/modules-load.d/*.conf
But on my various Linux computers these folders either don`t exist or are empty.
In addition there are some websites that suggest the kernel itself works out what modules to load, rather than relying on lists - which may be fine for the likes of drivers which can be identified from the hardware - but what about modules like all the netfilter modules - how can the kernel work out which of these are required.
Some sources suggest the list of modules that should be loaded during the startup of Linux can be listed in the file -
/etc/modules
or
/etc/modules_load.d/modules.conf
which is a link to /etc/modules.
But others say that the use of these files was deprecated after kernel version 2.6
So at the moment I can`t provide any information about how or why kernel modules are loaded during start up.
Some applications that are using TCP or UDP connections sometimes use a different port number from the port number that they normally use - this is done dynamically, and the connection may well use the default port number as well as the new different number.
This can be challenging for firewall design, as it means that either the firewall has to allow through a lot of extra port numbers all the time - which weakens the firewall - or else there has to be a mechanism that looks at OSI layer 7 to see what port number the application wants to use, and briefly allows this new port through the firewall.
So a kernel helper module is used to augment the ability of the conntrack mechanism by analysing one particular type of traffic, and if it sees that the connection is going to start using a different port number, the helper advises the conntrack mechanism that it should allow traffic on the new port number as well as on the original port number.
The new port number is allowed through by using the conntrack RELATED state.
The number of helper modules may depend on the kernel version, but typically these are the helper modules -
nf_conntrack_amanda
nf_conntrack_ftp
nf_conntrack_h323
nf_conntrack_irc
nf_conntrack_netbios_ns
nf_conntrack_pptp
nf_conntrack_sane
nf_conntrack_sip
nf_conntrack_snmp
nf_conntrack_tftp
In the early days of helper modules the helper modules were often loaded by default during startup.
However it was subsequently found that helper modules give rise to new vulnerabilities - in particular they are vulnerable to spoofed addresses.
There have been various ways to address this - one of them is to add some specific rules to the Prerouting chain in the Raw table.
Another mechanism is to partially disable the helper modules through the content of a file -
/proc/sys/net/netfilter/nf_conntrack_helper
However it doesn`t seem to be a sensible approach to load a module, then partially disable it through the contents of a file.
And another mechanism is to use the command "modprobe" to set the port to zero, but this doesn`t work on all helper modules.
Another mechanism to address the vulnerabilities is the introduction of the CT target - there seems to be differences of opinion in the various web pages as to what version of the kernel introduced the CT target - but it appears to be the current favoured approach.
Using ftp as an example, a typical iptables rule to use the CT target with any particular helper module might be -
iptables -t RAW -A PREROUTING -p tcp --dport 21 -j CT --helper ftp
or
iptables -t RAW -A OUTPUT -p tcp --dport 21 -j CT --helper ftp
Again, there seems to be a variety of opinion as to whether it should go in the PREROUTING chain or the OUTPUT chain.
It appears that helper modules maintain their own tracking list which is separate from the more usual CONNTRACK tracking list - and they have a very short extinction time, much shorter than the CONNTRACK tracking list.
Helper modules are not the easiest of things to understand and using them in a netfliter firewall without prejudicing the effectiveness of the firewall is quite complex.
There isn`t much information on the internet about them - and the ways of using them have very much evolved with different kernel versions.
After extensive searching I have only found a few independent web pages providing some information - but there is obvious plagiarism going on between some of them.
There is more information about helper modules and ufw in a page on the Ubuntu website - it calls it a manpage, but it has different content from the more commomly available man page - and despite this page I don`t think that ufw is using them safely.
It is possible to blacklist a module so that it will not get loaded - there seems to be two ways of doing it - the first is to add a file -
/etc/modprobe.d/blacklist-<module-name>.conf
The second is that instead of creating a .conf file for each module that needs to be blacklisted, all modules that need to be blacklisted are listed in one file -
/etc/modprobe.d/blacklist.conf
Some distributions use both methods.
Blacklisting a module can be a useful way to remove the effect of a suspect module - it is also a useful way to reduce the attack surface of a Linux installation, as a malicious script cannot load a module that has been blacklisted.
There are several userspace tools available for the administration of kernel modules - many of them are bundled inside the kmod package - most distributions of Linux include it, but if it isn`t installed -
apt install kmod
or
yum install kmod
should do it for you, depending on the Linux distribution.
The userspace tools in kmod are command line tools - they include :-
lsmod - shows all the modules that are loaded along with their dependencies - in effect it is showing the content of /proc/modules
modinfo - shows information about a specific named module
depmod - creates a list of module dependencies
insmod - simple instruction to add modules to the kernel
rmmod - simple instruction to remove a module from the kernel
modprobe - much more intelligent command with a lot of options to add or remove modules from the kernel
The man pages for each tool will provide the information required to use them.
The "modprobe" command has a big advantage over "insmod" and "rmmod" because it understands about dependencies, and will take account of them when installing or removing LKM`s.
There doesn`t seem to be a lot of information out there about parameters for loadable kernel modules - it appears that they are a way of telling the LKM something about the environment it is going to be loaded into.
There doesn`t seem to be standard parameters for LKMs and very few conventions.
Each LKM author decides what parameters should be supplied when the LKM is loaded.
So parameters should be documentated in the LKM documentation.
It looks like the command "modinfo" will print out the parameters for a LKM in amongst all the other information.
You can be more selective using the command line
modinfo -F parm <module-name>
in order to just show the list of parameters.
If the Linux installation is on hardware that includes Secure Boot as part of UEFI then kernel modules have to be signed.
It isn`t a trivial procedure, so beyond the scope of this web page - however the requirement to sign loadable kernel modules does add a significant barrier to black hats installing malicious LKM`s - so a useful part of a security strategy.
It is possible to create your own custom kernel modules - there is plenty of information on the internet about how to do it - again, it isn`t a trivial procedure, and way beyond the scope of this wep page.
Termina is the virtual environment that hosts Linux on a Chromebook.
The Google website is quite clear - Termina does not include kernel module support - which means that trying to use software that requires the building of, or the loading of, custom kernel modules (e.g. VirtualBox) will not work.
You cannot install custom kernel modules.
The folder /lib/modules/<kernel-name>/ doesn`t exist - there are no loadable kernel modules.
The command "lsmod" runs - but it doesn`t provide any information - it says that /proc/modules doesn`t exist - which is correct, it doesn`t exist.
The command "modinfo" doesn`t provide any information.
The folder /etc/modules exists but doesn`t contain a list of modules
The folder /sys/module/ does exist.
The folder /lib/modules-load.d is empty.
The folder /usr/lib/modules-load.d is empty.
It is a little difficult to understand how all the hype on various websites can claim that the Linux environment on Chromebooks is suitable for developers when the kernel used is a cut down Googlised kernel that doesn`t use or support loadable kernel modules.