There are a few WTFs in today's story. Let's get the first one out of the way: Jan S downloaded a shell script and ran it as root, without reading it. Now, let's be fair, that's honestly a pretty mild WTF; we've all done something similar, and popular software tools still tell you to install them with a curl … | sh
, and then sudo themselves extra permissions in the script.
The software being installed in this case is a tool for accessing Bitlocker encrypted drives from Linux. And the real WTF for this one is the install script, which we'll dig into in a moment. This is not, however, some small scale open source project thrown together by hobbyists, but instead released by Initech's "Data Recovery" business. In this case, this is the open source core of a larger data recovery product- if you're willing to muck around with low level commands and configs, you can do it for free, but if you want a vaguely usable UI, get ready to pony up $40.
With that in mind, let's take a look at the script. We're going to do this in chunks, because nearly everything is wrong. You might think I'm exaggerating, but here's the first two lines of the script:
#!/bin/bash
home_dir="/home/"${USER}"/initech.bitlocker"
That is not how you find out the user's home directory. We'll usually use ${HOME}
, or since the shebang tells us this is definitely bash, we could just use ~
. Jan also points out that while a username probably shouldn't have a space, it's possible, and since the ${USER}
isn't in quotes, this breaks in that case.
echo ${home_dir}
install_dir=$1
if [ ! -d "${install_dir}" ]; then
install_dir=${home_dir}
if [ ! -d "${install_dir}" ]; then
echo "create dir : "${install_dir}
mkdir ${install_dir}
Who wants indentation in their scripts? And if a script supports arguments, should we tell the user about it? Of course not! Just check to see if they supplied an argument, and if they did, we'll treat that as the install directory.
As a bonus, the mkdir
line protects people like Jan who run this script as root, at least if their home directory is /root
, which is common. When it tries to mkdir /home/root/initech.bitlocker
, the script fails there.
echo "Install software to ${install_dir}"
cp -rf ./* ${install_dir}"/"
Once again, the person who wrote this script doesn't seem to understand what the double quotes in Bash are for, but the real magic is the next segment:
echo "Copy runtime environment ..."
sudo cp -f ./libcrypto.so.1.0.0 /usr/lib/
sudo cp -f ./libssl.so.1.0.0 /usr/lib64
sudo cp -f ./libcrypto.so.1.0.0 /usr/lib/
sudo cp -f ./libssl.so.1.0.0 /usr/lib64
Did you have libssl
already installed in your system? Well now you have this version! Hope that's okay for you. We like our version of libssl
and libcrypto
so much we're copying them into your library directories twice. They probably meant to copy libcrypto
and libssl
to both lib
and lib64
, but messed up.
Well, that is assuming you already have a lib64
directory, because if you don't, you now have a lib64
file which contains the data from libssl.so.1.0.0
.
This is the installer for a piece of software which has been released as part of a product that Initech wants to sell, and they don't successfully install it.
sudo ln -s ${install_dir}/mount.bitlocker /usr/bin/mount.bitlocker
sudo ln -s ${install_dir}/bitlocker.creator /usr/bin/create.bitlocker
sudo ln -s ${install_dir}/activate.sh /usr/bin/initech.bitlocker.active
sudo ln -s ${install_dir}/initech.mount.sh /usr/bin/initech.bitlocker.mount
sudo ln -s ${install_dir}/initech.bitlocker.sh /usr/bin/initech.bitlocker
Hey, here's an install step with no critical mistakes, assuming that no other package or tool has tried to claim those names in /usr/bin
, which is probably true (Jan actually checked this using dpkg -S …
to see if any packages wanted to use that path).
source /etc/os-release
case $ID in
debian|ubuntu|devuan)
echo "Installing dependent package - curl ..."
sudo apt-get install curl -y
echo "Installing dependent package - openssl ..."
sudo apt-get install openssl -y
echo "Installing dependent package - fuse ..."
sudo apt-get install fuse -y
echo "Installing dependent package - gksu ..."
sudo apt-get install gksu -y
;;
Here's the first branch of our case. They've learned to indent. They've chosen to slap the -y
flag on all the apt-get
commands, which means the user isn't going to get a choice about installing these packages, which is mildly annoying. It's also worth noting that source
ing /etc/os-release
can be considered harmful, but clearly "not doing harm" isn't high on this script's agenda.
centos|fedora|rhel)
yumdnf="yum"
if test "$(echo "$VERSION_ID >= 22" | bc)" -ne 0; then
yumdnf="dnf"
fi
echo "Installing dependent package - curl ..."
sudo $yumdnf install -y curl
echo "Installing dependent package - openssl ..."
sudo $yumdnf install -y openssl
echo "Installing dependent package - fuse ..."
sudo $yumdnf install -y fuse3-libs.x86_64
;;
So, maybe they just don't think if
supports additional indentation? They indent the case
fine. I'm not sure what their thinking is.
Speaking of if
, look closely at that version check: test "$(echo "$VERSION_ID >= 22" | bc)" -ne 0
.
Now, this is almost clever. If your Linux version number uses decimal values, like 18.04
, you can't do a simple if [ "$VERSION_ID" -ge 22]…
: you'd get an integer expression expected
error. So using bc
does make sense…ish. It would be good to check if, y'know, bc
were actually installed- it probably is, but you don't know- and it might be better to actually think about the purpose of the check.
They don't actually care what version of Redhat Linux you're running. What they're checking is if your version uses yum
for package management, or its successor dnf
. A more reliable check would be to simply see if dnf
is a valid command, and if not, fallback to yum
.
Let's finish out the case statement:
*)
exit 1
;;
esac
So if your system doesn't use an apt
based package manager or a yum/dnf
based package manager, this just bails at this point. No error message, just an error number. You know it failed, and you don't know why, and it failed after copying a bunch of crap around your system.
So first it mostly installs itself, then it checks to see if it can successfully install all of its dependencies. And if it fails, does it clean up the changes it made? You better believe it doesn't!
echo ""
echo "Initech BitLocker Loader has been installed to "${install_dir}" successfully."
echo "Run initech.bitlocker --help to learn more about Initech BitLocker Loader"
This is a pretty optimistic statement, and while yes, it has theoretically been installed to ${install_dir}
, assuming that we've gotten this far, it's really installed to your /usr/bin
directory.
The real extra super-special puzzle to me is that it interfaces with your package manager to install dependencies. But it also installs its own versions of libcrypto
and libssl
, which don't come from your package manager. Ignoring the fact that it probably *installs them into the wrong places*, it seems bad. Suspicious, bad, and troubling.
Jan didn't send us the uninstall script, and honestly, I assume there isn't one. But if there is one, you know it probably tries to do rm -rf /${SOME_VAR_THAT_MIGHT_BE_EMPTY}
somewhere in there. Which, in consideration, is probably the safest way to uninstall this software anyway.