Kali Linux Blog
In Secure Kali Pi (2022), the first blog post in the Raspberry Pi series, we set up a Raspberry Pi 4 with full disk encryption. We mentioned that we can leave it somewhere as a drop box. This brought up the question, “If it is not on my local network how do I connect to it to unlock it?” So we will now answer this by showing a few different ways to connect to our secure Kali Pi drop box. This includes:
Wireless 802.11:
As a client on an existing network(s) (only if we know any details ahead of time to pre-configure)
Create an access point, to become a new network (that we can access if we are in physical distance to the device)
Wired ethernet:
Using static network settings (if we know the details ahead of time to pre-configure it)
DHCP to automatically discover network values (which creates noise)
After getting internet access, we will use a Virtual Private Network to remotely connect back to a server of our choosing, which we can also join from anywhere online, thus getting around the requirements of having to port forward on any firewalls.
Ingredients
Drop box - Raspberry Pi 4
Pre-configured as of our Secure Kali Pi blog post
Wi-Fi - We will be using the on-board wireless adapter (to make the device as compact as possible for our drop box)
However if the performance is not sufficient for your needs, an external compatible wireless adapter may give greater range
External server - A pre-created & harden OpenVPN service
Creating this is out-of-scope for this blog post
Pre-Config Wireless 802.11
Overview
While wired networking in the initramfs does not require a lot of extras, wireless has a few more moving parts. To enable wireless support, we need to find:
The kernel Wi-Fi modules that need to be in the initramfs (Depends on hardware)
The Wi-Fi firmware files that need to be in the initramfs (Depends on hardware)
The Wireless interface name (Kali defaults to:
wlan0
)Additional packages to increase functionally. Either:
wpa_supplicant to connect as a client to a wireless network
hostapd to create an access point for a new wireless network
Additionally, knowing the hostname of your Raspberry Pi can help find it, as well as blend in, in your target environment.
Interface Name
First, we need to know what our wireless interface is called.
In Kali we disable predictable interface names by default, so the first wireless device will be wlan0
.
As long as there is no other hardware plugged into the Raspberry Pi at this stage, it should stand out:
Wi-Fi Modules
We are now going to discover what modules are needed in order for our wireless device to come up.
On most ARM systems, the wireless device is typically connected via SDIO, and unfortunately we do not have a command like lspci to list any devices on the SDIO bus, but we can use dmesg and grep to look:
Since we were returned directly to the prompt, this means that “wlan” is not found in the dmesg output. As we mention in the Kali Raspberry Pi 4 documentation we use the nexmon firmware for the Raspberry Pi devices, so lets try searching for that instead:
As we can see in the output above, brcmfmac
is the driver that is giving us the message. There is a handy command that comes from the kmod package, called modinfo which will give us information about any module that the kernel has.
Now we know that the wireless card on the Raspberry Pi uses the brcmfmac
driver. So lets run modinfo brcmfmac
and see what information it gives us:
As you can see, there is quite a lot of information given there. A quick overview of it:
Where the module file is (
filename
)The license
The description
The author
The firmware files it can use
Aliases used to figure out if this is the module to use when a device is found
Any module dependencies (
depends
)Whether the module comes from in the kernel tree
The name of the module
The version magic
Any parameters (
params
)
For what we need, the dependencies section is the key.
When we read the man page for modinfo, we see that it offers the -F
flag to limit the output to certain fields. Since we currently care about the dependencies, let’s re-run modinfo
passing -F depends
since that is what we want to know.
To make it easier to understand the output, we will not group multiple modules together:
So in our case, the brcmfmac
module depends on both brcmutil
, and cfg80211
. So we run modinfo
on both of those as well to see their dependencies, if any:
Notice that the line is empty. This means that brcmutil does not have any additional module dependencies.
Now we check cfg80211
, the other dependency that was listed:
Here we see that cfg80211’s depends has an additional dependency on the rfkill
module. So we run modinfo
against it as well:
Like brcmutil
, rfkill
does not have any output, so there are no dependencies. We now have our list of modules that we need to add to the initramfs:
brcmfmac
brcmutil
cfg80211
rfkill
Wi-Fi Firmware
We now need the firmware for the Wi-Fi card. As before, we use the modinfo
command, but this time we will search for firmware
to see what firmware the module can use:
On Linux systems, the default firmware search path is /lib/firmware/
so the full path to the above would be:
/lib/firmware/brcmfmac*-sdio.*.bin
/lib/firmware/brcmfmac*-sdio.*.txt
Notice the wildcards (*
) in the firmware names. This means that it will match any of those files, so we will simply include all of the firmware that is in /lib/firmware/brcm
, and this would allow for using wireless on not just our current Raspberry Pi 4, but if we were to plug our secure Kali Pi SD Card into a Raspberry Pi 3, or maybe even the Raspberry Pi Zero 2 W, we would be able to get wireless on them as well.
Binaries
Lastly we need the the binaries that are used for connecting to wireless networks on Linux.
A typical Kali installation has NetworkManager
installed, and that handles wireless networks for us in a graphical desktop environment. But since we are doing this long before the full Kali system is available we need the wpa_supplicant
binary, from the wpasupplicant package.
Additionally, we will want to check we are online in our script using the wpa_cli
command, which will include that as well.
Change The Hostname
By default, Kali images for our Raspberry Pi images are set to the hostname of kali-raspberry-pi
. Keeping in mind that some environments have hostname policies, you might want to change the hostname to blend in with the target network better.
To change your hostname, you will want to run the command hostnamectl from the systemd package. Additionally, we will want to edit the /etc/hosts
file, which the system uses for local name resolution.
As an example, if we were to be deploying in a Windows heavy environment, we might want to use a host name similar to what a Windows machine might use:
And then edit the /etc/hosts
file as well, changing the line that has kali-raspberry-pi
in it to be DESKTOP-UL8M7HT
:
You will need to reboot the system for the changes to take effect.
Wi-Fi Connection
Like we did with secure Kali Pi, we need to make changes to our system, using the information we gathered above, to boot the system, handle the Wi-Fi network, making the device accessible.
Client Mode
We already know what the wireless network(s) credentials are, and now we are going to join them.
First up is the initramfs hook for the Wi-Fi firmware.
We will create the file /etc/initramfs-tools/hooks/zz-brcm
and add the following:
Next, we will do the hook for the modules and wpa_supplicant
files we need. We will use /etc/initramfs-tools/hooks/enable-wireless
which contains:
So now that we have our hooks that copy the Wi-Fi firmware, modules, and wpa_suppliant files, we need to write a script to use them in the initramfs.
One important thing to note about scripts in an initramfs, is that there is no guarantee on the order, so we create it with the name a_enable_wireless
so that alphabetically it should be the first script that gets run.
The file /etc/initramfs-tools/scripts/init-premount/a_enable_wireless
looks like:
Additionally, we need to kill the networking once we are booted, so that the actual system can use the device and connect properly.
This script /etc/initramfs-tools/scripts/local-bottom/kill_wireless
is made up with:
As a reminder, scripts need to be executable if you want them to run. Additionally, if a hook is not marked as executable, initramfs-tools will skip that hook when running update-initramfs
:
Now we use the information that we have gathered ahead of time to create our wpa_supplciant.conf
file to include the SSID & PSK, and any other possible options that the Wi-Fi network connection might need. You can read more information about the file by running man wpa_supplicant.conf
.
A shortcut to generating one is to simply run wpa_passphrase SSID PASSWORD
where SSID is the name of the wireless network, and PASSWORD is the passphrase (aka PSK) for the network.
In this example, we are going to be connecting our Raspberry Pi to the network kali wireless with a passphrase of secure kali wireless.
If your wireless network name has spaces in it, do not forget to quote the SSID in the command!
One thing to note here, when you use wpa_passphrase
to generate the PSK, it includes the passphrase in plain text. Because of this, we will strip that line out of the file, just in case if anyone else happens to come across the device and knows how to look inside an initramfs file, we do not want them seeing the plain text password to the network! And because we are using tee
rather than >
to write the file, we will see the file’s contents as its written:
If you want to add multiple wireless networks to your wpa_supplicant.conf
file, we can append the file rather than overwriting it:
Now we copy the newly generated configuration into /etc/initramfs-tools/
as that is where our enable-wireless
hook expects it to be:
As a reminder, we covered which kernel version to use in our secure Kali Pi post, and since we used the Raspberry Pi 4, we will continue to do so here, so our kernel version is 5.15.44-Re4son-v8l+
Now that we have all the parts that we need, we simply run mkinitramfs -o /boot/initramfs.gz 5.15.44-Re4son-v8l+
to generate the initramfs file with our changes to add wireless networking.
We can also verify that our changes are in the initramfs by running lsinitramfs /boot/initramfs.gz
and use grep to show the files we are looking for:
As we can see from the output, our initramfs has our modules, firmware, and wpa_supplicant files for the Wi-Fi chip the Raspberry Pi 4 uses!
If you are only interested in using the Raspberry Pi as a Wi-Fi client, you can stop here, and unmount everything like we did in our secure Kali Pi blog post.
Static IP
If we want to connect to a wireless network and set a static IP, we need to do similar to above. Adding in the wpa_supplicant files, but now we set the IP manually in the /boot/cmdline.txt
file, which is what the Raspberry Pi uses for the kernel command line arguments:
For more information, see the nfsroot kernel documentation.
The important thing is to set the options we need, and leave empty the ones we do not. The default cmdline.txt
has the following in it:
The cmdline.txt
requires everything to be on one line, so if we want to set our IP address to 192.168.42.3
, with a gateway of 192.168.42.1
, our hostname to securekalipi
, for the wlan0
device, our /boot/cmdline.txt
file will look like:
As the documentation states, anything that is not specified uses the default settings, so we simply skip putting anything in between the :
that we want to skip.
Access Point Mode
You should not use the wireless in both access point mode and client mode at the same time. It is possible, however the networks need to be on the same channels, and we do not cover this in order to keep the blog post simple. You should only use client mode, or access point mode, but not both from this blog post. We will talk about this again at the end of the blog post.
Similarly to how we set up connecting our Raspberry Pi to a wireless network as a client, if we want to set it up as an access point to connect to, we need to add into the initramfs. Like last time, our Wi-Fi drivers, the firmware just this time, its different software and configurations.
The package you would use on Linux to set up an access point is hostapd which does not come installed by default, so we will install it first.
As always, before we install software, we update what packages are available to ensure we are installing the newest version:
Now we will set it up and test it, to make sure everything works, before we add it to our initramfs to use:
Our configuration will have us create a network on channel 7, with a network name of SecureKaliPi, and a password of SecureKaliPiWiFi.
To use WPA2 the passphrase should be between 8 and 64 characters in length.
The PSK is a value derived from the SSID of the network and the password. The easiest way to get this is very similar to the way we created the wpa_supplicant.conf file above - we run wpa_passphrase SecureKaliPi SecureKaliPiWiFi
and then we copy the psk line that is not the plaintext password:
You can, and should, change the configuration to match your needs. If you would like to set it up to use 5GHz, you would need to change hw_mode=g
to hw_mode=a
, but keep in mind that if you are using 5GHz you need to change the channel. Wikipedia has a list of allowed combinations for different countries.
One setting you may want to change as well, is the ignore_broadcast_ssid
setting.
If we read the default configuration file, we can see that this option is what will allow us to hide our SSID from being broadcast:
Now that we have written our hostapd.conf
we can quickly test if it works by running:
If everything is set up correctly, you should see the above output. If you get any errors, you will need to correct those and re-run the command.
Now that hostapd is set up, and we have tested that it works, lets add it to our initramfs.
Because we need to add some binaries to the initramfs, we also need to include any dependencies that may be needed. So first we check which binary we need:
We need the hostapd binary, and to check its dependencies we will run ldd which tells us what libraries the binary depends on:
Because hostapd
also uses iw
to control its interfaces, we need to also include it. And like above, we want to check the dependencies it uses:
As we can see, hostapd
and iw
rely on a number of libraries that will need to be in the initramfs.
So we create a hook to include hostapd with these additional libraries so the hostapd and iw binaries can run, /etc/initramfs-tools/hooks/hostapd
:
Now we add our script, which sets our IP address (192.168.42.1/24
) for the access point as well as makes hostapd, and DHCP server run, /etc/initramfs-tools/scripts/init-premount/hostapd
. We will address the networking side after this script:
Additionally, we want to start a DHCP server so that when we connect to the Raspberry Pi’s access point, we get an IP address. Normally, you would use a package like isc-dhcp-server to run a DHCP server, but since we have already got busybox which has a DHCP server applet enabled in the initramfs, we will just use that instead. We do not need a fully featured DHCP server just to unlock our Raspberry Pi and let it finish booting.
First we set up the configuration file for it /etc/udhcpd.conf
with the following information:
And we create our hook which copies in our DHCP config, /etc/initramfs-tools/hooks/udhcpd
:
Like our previous hooks and scripts, we need to make sure the executable flag is set:
And now that everything is in place for hostapd support, we need to build the initramfs so that it has our changes in there:
Once we see all the parts are there, we are able to reboot the Raspberry Pi and we should see our Wi-Fi network from another machine.
Connect to it, and we should be able to unlock the device via SSH!
Wired Connection
By default, the wired connection on a Raspberry Pi will attempt to use DHCP to connect to a network when it is plugged in. You may want to set a static IP, we need to do similar to above, and set the IP manually in the /boot/cmdline.txt
file, which is what the Raspberry Pi uses for the kernel command line arguments.
Static IP
The default /boot/cmdline.txt
is set to:
The format of /boot/cmdline.txt
should look similar to:
/boot/cmdline.txt
requires everything to be on one line, so if we want to set our IP address to 192.168.42.3
, with a gateway of 192.168.42.1
, our hostname to securekalipi
, for the eth0
device, our /boot/cmdline.txt
file will look like:
As the documentation states, anything that is not specified uses the default settings, so we simply skip putting anything in between the :
that we want to skip. And we tell it to use the eth0
device, as that is the default device name for ethernet on the Raspberry Pi.
VPN Tunnel
Before we go over connecting to a VPN, it is important to note that the information will be stored in the initramfs file, unencrypted. This particular use case is NOT about securing the connection, but instead using the VPN to tunnel out of the network to bypass various firewall rules. As such, this connection should be treated as if the traffic is clear text.
After we have got our device connected to the network, great! We are now wanting to remotely connect to it. Due to firewalls on the network, being able to directly SSH into the device will be next to impossible (as we cannot do port forwarding). So we are needing the device to connect back to us (bind vs reverse)! You may opt for a SSH reverse connection, where the device continuously polls back home, however, we have opted to use a VPN. You may wish to use OpenVPN, WireGuard, or something else. We have opted for OpenVPN, however we are not going to cover how to set up and secure an OpenVPN server.
Regardless of the reverse service used, network traffic may be filtered by firewall rules which may limit what services can be used. For example SSH (22/TCP) or OpenVPN (1194/UDP) default ports may not be allowed out. As a result, think of what typical end-users may often use the network for. Commonly you see a lot of web traffic, so HTTPS (443/TCP
) should hopefully give a higher chance of success, such as HTTPS (443/TCP
)! We will talk about this again at the end of the blog post.
If you have created a new private network by starting an access point, there is not going to be an upstream gateway configured. As a result, the VPN tunnel will not be able to connect to the internet. You will need to find another way to get online, by either using another mode (Wi-Fi client), or another interface (wired ethernet, mobile hotspot etc).
As always, to use OpenVPN before the system is booted we need our hook to copy the OpenVPN software and our client configuration in to our initramfs.
First up is the hook. This copies the software, its dependencies, and our configuration file into the initramfs. We gathered this information the same way we did with hostapd
above, so we will not go over that again.
The OpenVPN hook, /etc/initramfs-tools/hooks/openvpn
:
And then we have to add our OpenVPN script to run in the initramfs, that uses our configuration file to connect to our OpenVPN server. Because this is running before there is any way to interact with the system, we also need to be able to pass the username and password somehow. A quick check of the openvpn man page shows us:
--auth-user-pass
Authenticate with server using username/password.
Valid syntaxes:
auth-user-pass
auth-user-pass up
If up is present, it must be a file containing username/password on 2 lines. If the password line is missing, OpenVPN will prompt for > one.
If up is omitted, username/password will be prompted from the console.
The option we want is --auth-user-pass up
. So we will create a file called up
with our username (dropboxuser
) on the first line, and password (pass123
) on the second line:
If your VPN connection does not require a username/password, you can remove the --auth-user-pass /etc/openvpn/up
in the vpnflags
variable below.
The script, which starts OpenVPN, /etc/initramfs-tools/scripts/init-premount/openvpn
:
And like with the others, we make sure our hooks and scripts are executable:
And now that everything is in place for connecting to OpenVPN, we need to build the initramfs so that it has our changes in there:
Now that the initramfs is updated, and we see that our changes are in there, we are able to reboot the Raspberry Pi. Once it starts booting, and once the network connection is available, it should connect to our OpenVPN server.
You will want to test this in your home lab, before you deploy it anywhere, to make sure it’s working:
Summary
In this blog post, we have covered gathering information about our device, in this case it was a Raspberry Pi, but the information gathering holds true for any device running Kali that you might want to unlock remotely.
We also covered setting a static IP for both wired and Wi-Fi networks, setting up an access point, and using an OpenVPN connection, these are not specific to the Raspberry Pi, aside from the firmware and module for the Wi-Fi device.
We hope you found this blog post helpful, and if you have any questions or comments, please check out the Kali Discord server.
Food for Thought
To expand on this future, some improvements which we came up with:
Encapsulate the OpenVPN traffic (such as stunnel), making it legit HTTPS data, rather than only using default port
Using WireGuard rather than OpenVPN
Mobile connectivity (using an external 3G/4G/LTE adapter)
Adding fall back method(s) - If Wi-Fi client is not working, create then a Wi-Fi access point
“WLAN Knocking” - The Raspberry Pi is monitoring for a certain SSID being broadcasted (maybe from a certain MAC address), when detected, only then perform an action
We are sure you can also think outside of the box, and come up with additional ideas too. Please tweet us your ideas, and progress with your drop box!
Additional Resources
Wi-Fi in initramfs:
Setting up hostapd: