Introduction

When you first become a hacker, you develop something of a “hacker vision” that follows you when you go about. You tend to notice things like smart meters that control the city’s electricity supply, or kiosks that are running intel compute sticks, or whatever. When you understand how the tech works, you can’t help but ask yourself if you could exploit that.

And hospitals will drive you crazy.

Hospitals are highly digitized palaces with open ethernet jacks, presumed places of privacy, and computers running Windows 7 because their scanners aren’t compatible with anything else. I wouldn’t be surprised if every hospital is currently hacked.

But for all my pessimism, I noticed something about all of the “snatch-and-grabble computers” that they have around the standard medical offices. They are super tiny, under-spec’d, and look cheap. I found out the reason for this is for the longest time, those devices were hot-ticket theft items. A nurse or a doctor might leave a laptop on a cart in an inconvenient privacy spot away from the cameras. A thief could grab it, then use wifi from their car to access medical data, pilfer data, and who knows how you could misuse that laptop’s access to medical databases with drugs and insurance payment sheets.

In short, this was a problem, and they needed a solution.

Enter thin clients.

A thin client is a computer whose only job is to boot up into some full screen remote control program and grab a VDI or, virtual desktop infrastructure, machine from a server somewhere.

If a thief steals a thin client, there is no data to pilfer on the hard-drive, and without proper creds, they couldn’t get access to the remote server. Add in modern requirements for smart cards, and suddenly a thief has access to a machine that could be outperformed by a raspberry pi or a 100$ chromebook. If the network admin was clever, he would have made the gateway to the VDI only accessible through a VPN router on the hospital’s network, so the thin client will just time out.

The Project

So thin clients have been a thing forever, but I haven’t been impressed by the linux offerings. You can install a linux operating system, install Remmina or some other RDP software, etc… but it just didn’t have that nice ephemerality that comes with RAMFSes and that bothered me.

I came across an article put out by the “tiny core” linux guys that explains how to create an ephemeral live drive with remote software that did just what I wanted. Unfortunately, I’m not taken with overwhelming confidence with the tiny core project. The website doesn’t have any encryption, and when I booted tiny core on my laptop, it took a lot of finangling to get features online that were supposed to be “quick install”.

Note: Please don’t infer that I have anything less than huge respect for the tiny core team. Those guys and gals are hardcore enthusiast who’ve built an AWESOME project, it just wasn’t for my use case. If I happen to have a Vocore board, I’ll know who to use.

I decided that I would make a version of the live drive project from that book but with Debian and xfreerdp. At some point, I’m going to also add tailscale to it because I love everything about how tailscale does things, but that’ll be a different time. The following is how you create a live USB stick with just enough software to get an ethernet attached thin client.

Note: I didn’t even add wireless into it because like the hospital scenerio, I like the idea that a seperate device handles the connection and if someone takes the thin client machine it becomes a paperweight. Adding it wouldn’t be hard. It would just take a couple more installs in the chroot stage.

Instructions:

Keep in mind that I’m running an Ubuntu 24.04 machine as my base.

Get the tools to bootstrap a debian instance onto your ubuntu machine.

sudo apt-get install \
    debootstrap \
    squashfs-tools \
    xorriso \
    isolinux \
    syslinux-efi \
    grub-pc-bin \
    grub-efi-amd64-bin \
    grub-efi-ia32-bin \
    mtools \
    dosfstools

Create the boot folder to install everything to.

mkdir -p "${HOME}/LIVE_BOOT"

Bootstrap and configure Debian.

sudo debootstrap \
    --arch=amd64 \
    --variant=minbase \
    stable \
    "${HOME}/LIVE_BOOT/chroot" \
    http://ftp.us.debian.org/debian/

Choose your Hostname.

echo "debian-live" | sudo tee "${HOME}/LIVE_BOOT/chroot/etc/hostname"

Install the image/kernel.

sudo chroot "${HOME}/LIVE_BOOT/chroot" /bin/bash << EOF
apt-get update && \
apt-get install -y --no-install-recommends \
    linux-image-amd64 \
    live-boot \
	 zsh \
    systemd-sysv
EOF

Drop into chroot.

Note: Will drop you into the dev jail.

sudo chroot "${HOME}/LIVE_BOOT/chroot" 

Install rdp tools and supporting OS goodies.

apt-get install -y --no-install-recommends \
    iwd \
    curl openssh-client \
    openbox xserver-xorg-core xserver-xorg xinit xterm lightdm tint2 freerdp2-x11\
    vim iproute2 net-tools isc-dhcp-client iputils-ping network-manager htop

(STILL WORKING ON IT, not ready for primetime) Install tailscale.

# curl -fsSL https://tailscale.com/install.sh | sh

Set up openbox.

mkdir -p ~/.config/openbox && cp /etc/xdg/openbox/* ~/.config/openbox

Configure Openbox’s Autostart and make sure to replace the variables.

vim ~/.config/openbox/autostart

add these lines:

# Launch taskbar
tint2 &
#Tailscale (uncomment next line when error is figured out)
#tailscale up --auth-key=INSERT_KEY_HERE &
# Start xfreerdp
xfreerdp /u:USERNAME /v:IP_ADDRESS /sound:sys:alsa /f /cert:ignore +clipboard &

Configure lightdm.

vim /etc/lightdm/lightdm.conf

make sure these lines are there:

[Seat:*]
autologin-user=root

Clean up uneeded packages.

apt-get clean

Exit the chroot

exit

Set the root password.

sudo chroot "${HOME}/LIVE_BOOT/chroot" passwd root

Create Live environment files.

mkdir -p "${HOME}/LIVE_BOOT"/{staging/{EFI/BOOT,boot/grub/x86_64-efi,isolinux,live},tmp}

Compress the chroot env into a squashfs.

sudo mksquashfs \
    "${HOME}/LIVE_BOOT/chroot" \
    "${HOME}/LIVE_BOOT/staging/live/filesystem.squashfs" \
    -e boot

Copy the kernel and initramfs from inside the chroot to the live directory.

cp "${HOME}/LIVE_BOOT/chroot/boot"/vmlinuz-* \
    "${HOME}/LIVE_BOOT/staging/live/vmlinuz" && \
cp "${HOME}/LIVE_BOOT/chroot/boot"/initrd.img-* \
    "${HOME}/LIVE_BOOT/staging/live/initrd"

Prepare Boot Loader Menus.

cat <<'EOF' > "${HOME}/LIVE_BOOT/staging/isolinux/isolinux.cfg"
UI vesamenu.c32

MENU TITLE Boot Menu
DEFAULT linux
TIMEOUT 600
MENU RESOLUTION 640 480
MENU COLOR border       30;44   #40ffffff #a0000000 std
MENU COLOR title        1;36;44 #9033ccff #a0000000 std
MENU COLOR sel          7;37;40 #e0ffffff #20ffffff all
MENU COLOR unsel        37;44   #50ffffff #a0000000 std
MENU COLOR help         37;40   #c0ffffff #a0000000 std
MENU COLOR timeout_msg  37;40   #80ffffff #00000000 std
MENU COLOR timeout      1;37;40 #c0ffffff #00000000 std
MENU COLOR msg07        37;40   #90ffffff #a0000000 std
MENU COLOR tabmsg       31;40   #30ffffff #00000000 std

LABEL linux
  MENU LABEL Debian Live [BIOS/ISOLINUX]
  MENU DEFAULT
  KERNEL /live/vmlinuz
  APPEND initrd=/live/initrd boot=live

LABEL linux
  MENU LABEL Debian Live [BIOS/ISOLINUX] (nomodeset)
  MENU DEFAULT
  KERNEL /live/vmlinuz
  APPEND initrd=/live/initrd boot=live nomodeset
EOF

Create Grub menu.

cat <<'EOF' > "${HOME}/LIVE_BOOT/staging/boot/grub/grub.cfg"
insmod part_gpt
insmod part_msdos
insmod fat
insmod iso9660

insmod all_video
insmod font

set default="0"
set timeout=30

# If X has issues finding screens, experiment with/without nomodeset.

menuentry "Debian Live [EFI/GRUB]" {
    search --no-floppy --set=root --label DEBLIVE
    linux ($root)/live/vmlinuz boot=live
    initrd ($root)/live/initrd
}

menuentry "Debian Live [EFI/GRUB] (nomodeset)" {
    search --no-floppy --set=root --label DEBLIVE
    linux ($root)/live/vmlinuz boot=live nomodeset
    initrd ($root)/live/initrd
}
EOF

Copy grub to boot.

cp "${HOME}/LIVE_BOOT/staging/boot/grub/grub.cfg" "${HOME}/LIVE_BOOT/staging/EFI/BOOT/"

Create grub-embed.cfg.

cat <<'EOF' > "${HOME}/LIVE_BOOT/tmp/grub-embed.cfg"
if ! [ -d "$cmdpath" ]; then
    # On some firmware, GRUB has a wrong cmdpath when booted from an optical disc.
    # https://gitlab.archlinux.org/archlinux/archiso/-/issues/183
    if regexp --set=1:isodevice '^(\([^)]+\))\/?[Ee][Ff][Ii]\/[Bb][Oo][Oo][Tt]\/?$' "$cmdpath"; then
        cmdpath="${isodevice}/EFI/BOOT"
    fi
fi
configfile "${cmdpath}/grub.cfg"
EOF

Prepare boot loader files:

cp /usr/lib/ISOLINUX/isolinux.bin "${HOME}/LIVE_BOOT/staging/isolinux/" && \
cp /usr/lib/syslinux/modules/bios/* "${HOME}/LIVE_BOOT/staging/isolinux/"

Copy EFI/Modern boot files.

cp -r /usr/lib/grub/x86_64-efi/* "${HOME}/LIVE_BOOT/staging/boot/grub/x86_64-efi/"

Create EFI bootable Grub images.

grub-mkstandalone -O i386-efi \
    --modules="part_gpt part_msdos fat iso9660" \
    --locales="" \
    --themes="" \
    --fonts="" \
    --output="${HOME}/LIVE_BOOT/staging/EFI/BOOT/BOOTIA32.EFI" \
    "boot/grub/grub.cfg=${HOME}/LIVE_BOOT/tmp/grub-embed.cfg"

Create Fat16 UEFI Boot disk image with EFI Bootloaders.

(cd "${HOME}/LIVE_BOOT/staging" && \
    dd if=/dev/zero of=efiboot.img bs=1M count=20 && \
    mkfs.vfat efiboot.img && \
    mmd -i efiboot.img ::/EFI ::/EFI/BOOT && \
    mcopy -vi efiboot.img \
        "${HOME}/LIVE_BOOT/staging/EFI/BOOT/BOOTIA32.EFI" \
        "${HOME}/LIVE_BOOT/staging/EFI/BOOT/BOOTx64.EFI" \
        "${HOME}/LIVE_BOOT/staging/boot/grub/grub.cfg" \
        ::/EFI/BOOT/
)

Use xorriso to generate iso.

xorriso \
    -as mkisofs \
    -iso-level 3 \
    -o "${HOME}/LIVE_BOOT/debian-custom.iso" \
    -full-iso9660-filenames \
    -volid "DEBLIVE" \
    --mbr-force-bootable -partition_offset 16 \
    -joliet -joliet-long -rational-rock \
    -isohybrid-mbr /usr/lib/ISOLINUX/isohdpfx.bin \
    -eltorito-boot \
        isolinux/isolinux.bin \
        -no-emul-boot \
        -boot-load-size 4 \
        -boot-info-table \
        --eltorito-catalog isolinux/isolinux.cat \
    -eltorito-alt-boot \
        -e --interval:appended_partition_2:all:: \
        -no-emul-boot \
        -isohybrid-gpt-basdat \
    -append_partition 2 C12A7328-F81F-11D2-BA4B-00A0C93EC93B ${HOME}/LIVE_BOOT/staging/efiboot.img \
    "${HOME}/LIVE_BOOT/staging"

Ok, so at this point you have an iso file that’ll launch a minimal version of debian, set up xfreerdp on fullscreen to point to your server that’s running RDP that you want to act as your virtual infrastructure.

Transfer that ISO to a USB stick using etcher, dd, or my personal favorite: gnome-disks.

Now as far as the VDI that you’re connecting to is concerned, it could be a windows virtual machine or something else. Personally, I prefer debian-based linux systems, so let’s set up an RDP server on another debian instance. I’m assuming you installed it clean and didn’t add any software yet, so it’s just a terminal.


Installing an RDP Server on Debian

Update apt.

sudo apt update && sudo apt upgrade -y

Install a gui server and a desktop environment.

sudo apt install xfce4 xfce4-goodies xorg dbus-x11 x11-xserver-utils -y

Install xrdp (The server program).

sudo apt install xrdp -y

Make sure it’s running.

sudo systemctl status xrdp

let the program use SSL so there’s a layer of encryption.

sudo adduser xrdp ssl-cert

Configure your debian instance to use the encryption files.

vim /etc/xrdp/xrdp.ini

change these:

security_layer=negotiate
certificate=
key_file=

To these:

security_layer=tls
certificate=/etc/xrdp/cert.pem
key_file=/etc/xrdp/key.pem

Restart the xRDP service.

sudo systemctl restart xrdp

If you have a firewall, let it through. Debian doesn’t have one by default.

sudo ufw allow 3389/tcp

If you have a firewall, reload it.

sudo ufw reload

A Note about RDP failure

There was a feature that I wasn’t aware of with RDP. Only one person can be RDP’ing into the server at one time.

If you set up xrdp and you try to log in with xfreerdp and you get a message like “user is being logged out”, it’s because it’s trying to take ownership of the one graphical shell that’s being offered, but It’s already being taken.

Try logging off on the server and give it a go.

Where all this comes from:

Taken almost directly from: https://www.willhaley.com/blog/custom-debian-live-environment/

RDP: https://www.aguslr.com/blog/2017/04/15/debian-thinclient.html

RDP Server: https://itslinuxfoss.com/install-xrdp-server-remote-desktop-debian-12/

Xfreerdp: https://commandmasters.com/commands/xfreerdp-linux/