Extending and Customizing Kali

Topic Progress:

10.3. Extending and Customizing Kali Linux

Sometimes you need to modify Kali Linux to make it fit your local needs. The best way to achieve this is to maintain your own package repository hosting the modified versions of the Kali packages that you had to fork, as well as supplementary packages providing custom configuration and extra software (not provided by Kali Linux).

10.3.1. Forking Kali Packages

Please refer to Section 9.1, "Modifying Kali Packages" for explanations about this topic.

All packages can be forked if you have a good reason but you must be aware that forking a package has a cost, since you have to update it every time that Kali publishes an update. Here are some reasons why you might want to fork a package:

  • To add a patch to fix a bug or add a new feature. Although in most cases, you will want to submit that patch to the upstream developers so that the bug is fixed or the feature is added at the source.
  • To compile it with different options (assuming that there are good reasons why Kali did not compile it with those options; otherwise it might be best to discuss this with Kali developers to see if they can enable the desired options).

By contrast, here are some bad reasons to fork a package along with suggestions of how to handle your problem:

  • To modify a configuration file. You have multiple, better options like using configuration management to automatically install a modified configuration file or installing a configuration package that will put a file in a configuration directory (when available) or that will divert the original configuration file.
  • To update to a newer upstream version. Again, it is better to work with developers to update the package directly in Debian or Kali. With the rolling release model, updates are rather quick to reach end users.

Among all the available packages, there are some that are building blocks of Kali Linux and that could be interesting to fork in some situations:

  • kali-meta: this source package builds all the kali-linux-* and kali-tools-* meta packages and notably kali-linux-default, which defines what packages are installed in the default Kali Linux ISO image.
  • desktop-base: This source package contains various miscellaneous files that are used by default in desktop installations. Consider forking this package if you would like to show your organization's brand in the default background or change the theme of the desktop.
  • kali-menu: this package defines the structure of the Kali menu and provides .desktop files for all applications that should be listed in the Kali menu.

10.3.2. Creating Configuration Packages

Now that we have touched on PXE booting and discussed configuration management with SaltStack as well as package forking, it is time to wrap these processes up into a practical example and extend the scenario by creating a custom configuration package to deploy a custom configuration to multiple machines semi-automatically.

In this example, you will create a custom package that sets up and utilizes your own package repository and GnuPG signing key, distributes a SaltStack configuration, pushes a custom background, and provides default desktop settings in a unified way to all your Kali installations.

This may seem like a daunting task (especially if you glance through the Debian New Maintainer Guide) but fortunately for us, a configuration package is mainly a sophisticated file archive and turning it into a package is rather easy.

Looking into a Sample Package

If you want to look into a real package that is basically a configuration package, consider the kali-defaults package. It is not as simple as the sample in this section but it has all the relevant characteristics and even uses some advanced techniques (like dpkg-divert) to replace files already provided by other packages.

The offsec-defaults package will contain a few files:

  • /etc/apt/sources.list.d/offsec.list: a sources.list entry for APT, enabling the company's internal package repository
  • /etc/apt/trusted.gpg.d/offsec.gpg: the GnuPG key used to sign the company's internal package repository
  • /etc/salt/minion.d/offsec.conf: a SaltStack configuration file to indicate where to find the Salt master
  • /usr/share/images/offsec/background.png: a nice background image with the Offensive Security logo
  • /usr/share/glib-2.0/schemas/90_offsec-defaults.gschema.override: a file providing alternate default settings for the GNOME desktop

First, create an offsec-defaults-1.0 directory and put all the files in that directory. Then run dh_make --native (from the dh-make package) to add Debian packaging instructions, which will be stored in a debiansub-directory:

$ mkdir offsec-defaults-1.0; cd offsec-defaults-1.0
$ dh_make --native
Type of package: (single, indep, library, python)
[s/i/l/p]? i
Email-Address       : buxy`
License             : gpl3
Package Name        : offsec-defaults
Maintainer Name     : Raphaël Hertzog
Version             : 1.0
Package Type        : indep
Date                : Thu, 16 Jun 2020 18:04:21 +0200
Are the details correct? [Y/n/q] y
Currently there is not top level Makefile. This may require additional tuning
Done. Please edit the files in the debian/ subdirectory now.

First, you are prompted for a package type. In the example, we selected indep, which indicates that this source package will generate a single binary package that can be shared across all architectures (Architecture: all). single acts as a counterpart, and produces a single binary package that is dependent on the target architecture (Architecture: any). In this case, indep is more relevant, since the package only contains text files and no binary programs, so that it can be used similarly on computers of all architectures. The library type is useful for shared libraries, since they need to follow strict packaging rules. In a similar fashion, python should be restricted to Python modules.

Maintainer's Name and Email Address

Most of the programs involved in package maintenance will look for your name and email address in the DEBFULLNAMEand DEBEMAIL or EMAIL environment variables. Defining them, once and for all, prevents re-typing them multiple times. If your usual shell is Bash, it is a simple matter of adding the following two lines in your ~/.bashrc file. For example:

export EMAIL="buxy@kali.org"
export DEBFULLNAME="Raphael Hertzog"

The dh_make command created a debian subdirectory containing many files. Some are required, in particular rules, control, changelog, and copyright. Files with the .ex extension are example files that can be used by modifying them and removing the extension. When they are not needed, we recommend removing them. The compat file should be kept, since it is required for the correct functioning of the debhelper suite of programs (all beginning with the dh_ prefix) used at various stages of the package build process.

The copyright file must contain information about the authors of the documents included in the package, and the related license. If the default license selected by dh_make does not suit you, then you must edit this file. Here is the modified version of the copyright file:

Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: offsec-defaults

Files: *
Copyright: 2020 Offensive Security
License: GPL-3.0+

License: GPL-3.0+
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.
 This package is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 GNU General Public License for more details.
 You should have received a copy of the GNU General Public License
 along with this program. If not, see .
 On Debian systems, the complete text of the GNU General
 Public License version 3 can be found in "/usr/share/common-licenses/GPL-3".

The default changelog file is generally appropriate; replacing the "Initial release" with a more verbose explanation should be enough:

offsec-defaults (1.0) unstable; urgency=medium

  * Add salt minion's configuration file.
  * Add an APT's sources.list entry and an APT's trusted GPG key.
  * Override the gsettings schema defining the background picture.

 -- Raphaël Hertzog @kali.org>  Thu, 16 Jun 2020 18:04:21 +0200

In the example, we will make changes to the control file. We will change the Section field to misc and remove the Homepage, Vcs-Git, and Vcs-Browser fields. Lastly, we will fill in the Description field:

Source: offsec-defaults
Section: misc
Priority: optional
Maintainer: Raphaël Hertzog @kali.org>
Build-Depends: debhelper (>= 9)
Standards-Version: 3.9.8

Package: offsec-defaults
Architecture: all
Depends: ${misc:Depends}
Description: Default settings for Offensive Security
 This package contains multiple files to configure computers
 owned by Offensive Security.
 It notably modifies:
  - APT's configuration
  - salt-minion's configuration
  - the default desktop settings

The rules file usually contains a set of rules used to configure, build, and install the software in a dedicated subdirectory (named after the generated binary package). The contents of this subdirectory are then archived within the Debian package as if it were the root of the filesystem. In this case, files will be installed in the debian/offsec-defaults/ subdirectory. For example, to end up with a package installing /etc/apt/sources.list.d/offsec.list, install the file in debian/offsec-defaults/etc/apt/sources.list.d/offsec.list. The rules file is used as a Makefile, with a few standard targets (including clean and binary, used respectively to clean the source directory and generate the binary package).

What is a Makefile file?

You may have noticed the message concerning the missing Makefile at the end of the dh_make output and the mention of its similarity to the rules file. A Makefile is a script file used by the make program; it describes rules for how to build a set of files from each other in a tree of dependencies. For instance, a program can be built from a set of source files. The Makefile file describes these rules in the following format:

target: source1 source2 ...

The interpretation of such a rule is as follows: if one of the source* files is more recent than the target file, then the target needs to be generated, using command1 and command2.

Note that the command lines must start with a tab character; also note that when a command line starts with a dash character (-), failure of the command does not interrupt the whole process.

Although this file is the heart of the process, it contains only the bare minimum for running a standard set of commands provided by the debhelper tool. Such is the case for files generated by dh_make. To install most of your files, we recommend configuring the behaviour of the dh_install command by creating the following debian/offsec-defaults.install file:

apt/offsec.list etc/apt/sources.list.d/
apt/offsec.gpg etc/apt/trusted.gpg.d/
salt/offsec.conf etc/salt/minion.d/
images/background.png usr/share/images/offsec/

You could also use this to install the gsettings override file but debhelper provides a dedicated tool for this (dh_installgsettings) so you can rely on it. First, put your settings in debian/offsec-defaults.gsettings-override:


Next, override the dh_installgsettings call in debian/rules to increase the priority to the level expected for an organization override (which is 90 according to the manual page):

#!/usr/bin/make -f

        dh $@

        dh_installgsettings --priority=90

At this point, the source package is ready. All that is left to do is to generate the binary package with the same method used previously for rebuilding packages: run the dpkg-buildpackage -us -uc command from within the offsec-defaults-1.0 directory:

$ dpkg-buildpackage -us -uc
dpkg-buildpackage: info: source package offsec-defaults
dpkg-buildpackage: info: source version 1.0
dpkg-buildpackage: info: source distribution unstable
dpkg-buildpackage: info: source changed by Raphaël Hertzog @kali.org>
dpkg-buildpackage: info: host architecture amd64
 dpkg-source --before-build offsec-defaults-1.0
 fakeroot debian/rules clean
dh clean
 dpkg-source -b offsec-defaults-1.0
dpkg-source: info: using source format '3.0 (native)'
dpkg-source: info: building offsec-defaults in offsec-defaults_1.0.tar.xz
dpkg-source: info: building offsec-defaults in offsec-defaults_1.0.dsc
 debian/rules build
dh build
 fakeroot debian/rules binary
dh binary
   debian/rules override_dh_installgsettings
make[1]: Entering directory '/home/kali/offsec-defaults-1.0'
dh_installgsettings --priority=90
make[1]: Leaving directory '/home/kali/offsec-defaults-1.0'
dpkg-deb: building package 'offsec-defaults' in '../offsec-defaults_1.0_all.deb'.
 dpkg-genchanges  >../offsec-defaults_1.0_amd64.changes
dpkg-genchanges: info: including full source code in upload
 dpkg-source --after-build offsec-defaults-1.0
dpkg-buildpackage: info: full upload; Debian-native package (full source is included)

10.3.3. Creating a Package Repository for APT

Now that you have a custom package, you can distribute it through an APT package repository. Use reprepro to create the desired repository and to fill it. This tool is rather powerful and its manual page is certainly worth reading.

A package repository is typically hosted on a server. To properly separate it from other services running on the server, it is best to create a user dedicated to this service. In the dedicated user account, you will be able to host the repository files and also the GnuPG key that will be used to sign the package repository:

# apt install reprepro gnupg
# adduser --system --group pkgrepo
Adding system user `pkgrepo' (UID 136) ...
Adding new group `pkgrepo' (GID 142) ...
Adding new user `pkgrepo' (UID 136) with group `pkgrepo' ...
Creating home directory `/home/pkgrepo' ...
# chown pkgrepo $(tty)
# su - -s /bin/bash pkgrepo
pkgrepo@kali:~$ gpg --gen-key
gpg (GnuPG) 2.1.11; Copyright (C) 2020 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

gpg: directory '/home/pkgrepo/.gnupg' created
gpg: new configuration file '/home/pkgrepo/.gnupg/dirmngr.conf' created
gpg: new configuration file '/home/pkgrepo/.gnupg/gpg.conf' created
gpg: keybox '/home/pkgrepo/.gnupg/pubring.kbx' created
Note: Use "gpg --full-gen-key" for a full featured key generation dialog.

GnuPG needs to construct a user ID to identify your key.

Real name: Offensive Security Repository Signing Key
Email address: repoadmin@offsec.com
You selected this USER-ID:
    "Offensive Security Repository Signing Key "

Change (N)ame, (E)mail, or (O)kay/(Q)uit? o
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
gpg: /home/pkgrepo/.gnupg/trustdb.gpg: trustdb created
gpg: key B4EF2D0D marked as ultimately trusted
gpg: directory '/home/pkgrepo/.gnupg/openpgp-revocs.d' created
gpg: revocation certificate stored as '/home/pkgrepo/.gnupg/openpgp-revocs.d/F8FE22F74F1B714E38DA6181B27F74F7B4EF2D0D.rev'
public and secret key created and signed.

gpg: checking the trustdb
gpg: marginals needed: 3  completes needed: 1  trust model: PGP
gpg: depth: 0  valid:   1  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 1u
pub   rsa2048/B4EF2D0D 2020-06-17 [S]
      Key fingerprint = F8FE 22F7 4F1B 714E 38DA  6181 B27F 74F7 B4EF 2D0D
uid         [ultimate] Offensive Security Repository Signing Key 
sub   rsa2048/38035F38 2020-06-17 []

Note that when you are prompted for a passphrase, you should enter an empty value (and confirm that you don't want to protect your private key) as you want to be able to sign the repository non-interactively. Note also that gpg requires write access to the terminal to be able to securely prompt for a passphrase: that is why you changed the ownership of the virtual terminal (which is owned by root since you initially connected as that user) before starting a shell as pkgrepo.

Now you can start setting up the repository. A dedicated directory is necessary for reprepro and inside that directory you have to create a conf/distributions file documenting which distributions are available in the package repository:

pkgrepo@kali:~$ mkdir -p reprepro/conf
pkgrepo@kali:~$ cd reprepro
pkgrepo@kali:~/reprepro$ cat >conf/distributions <

The required fields are bname, which gives the name of the distribution, Architectures, which indicates which architectures will be available in the distribution (and accepted on the input side), and Components, which indicates the various components available in the distribution (components are a sort of sub-section of the distribution, which can be enabled separately in APT's sources.list). The Origin and Description fields are purely informative and they are copied as-is in the Release file. The SignWith field asks reprepro to sign the repository with the GnuPG key whose identifier is listed (put the full fingerprint here to ensure you use the correct key, and not another one colliding on the short identifier). The AlsoAcceptFor setting is not required but makes it possible to process .changes files whose Distribution field has a value listed here (without this, it would only accept the distribution's bname in that field).

With this basic setup in place, you can let reprepro generate an empty repository:

pkgrepo@kali:~/reprepro$ reprepro export
Exporting indices...
pkgrepo@kali:~/reprepro$ find .

As you can see, reprepro created the repository meta-information in a dists sub-directory. It also initialized an internal database in a db sub-directory.

It is now time to add your first package. First, copy the files generated by the build of the offsec-defaults package (offsec-defaults_1.0.dsc, offsec-defaults_1.0.tar.xz, offsec-defaults_1.0_all.deb, and offsec-defaults_1.0_amd64.changes) into /tmp on the server hosting the package repository and ask reprepro to include the package:

pkgrepo@kali:~/reprepro$ reprepro include offsec-internal /tmp/offsec-defaults_1.0_amd64.changes
Exporting indices...
pkgrepo@kali:~/reprepro$ find pool

As you can see, it added the files into its own package pool in a pool sub-directory.

The dists and pool directories are the two directories that you need to make (publicly) available over HTTP to finish the setup of your APT repository. They contain all the files that APT will want to download.

Assuming that you want to host this on a virtual host named pkgrepo.offsec.com, you could create the following Apache configuration file, save it to /etc/apache2/sites-available/pkgrepo.offsec.com.conf, and enable it with a2ensite pkgrepo.offsec.com):

    ServerName pkgrepo.offsec.com
    ServerAdmin repoadmin@offsec.com

    ErrorLog /var/log/apache2/pkgrepo.offsec.com-error.log
    CustomLog /var/log/apache2/pkgrepo.offsec.com-access.log "%h %l %u %t \"%r\" %>s %O"

    DocumentRoot /home/pkgrepo/reprepro

        Options Indexes FollowSymLinks MultiViews
        Require all granted
        AllowOverride All

And the corresponding sources.list entry to add on machines that need packages from this repository would look like this:

deb http://pkgrepo.offsec.com offsec-internal main

# Enable next line if you want access to source packages too
# deb-src http://pkgrepo.offsec.com offsec-internal main

Your package is now published and should be available to your networked hosts.

Although this has been a lengthy setup, the "heavy lifting" is now completed. You can boot your networked machines via PXE, install a customized version of Kali Linux without interaction thanks to a network-delivered preseed, configure SaltStack to manage your configurations (and control minions!), create forked custom packages, and distribute those packages through your own package repository. This provides centralized management and enterprise-level control over multiple Kali Linux installations. In short, you can now quickly deploy highly secure Kali systems preconfigured for your specific needs and keep them synchronized thanks to Kali's (semi-automatic) installation of all package updates.