<h1> Plausible Deniability Protection for an .onion Service</h1>
<p>In this tutorial we're going to look at how you can protect a set of QEMU VMs running in a veracrypt hidden partition from being discovered by an adversary.</p>
<p>For this tutorial we have the following threat model:</p>
<ol>
<li><p>What if an adversary enters the room where the physical server is ?</p></li>
<li><p>What if an adversary opens up the case of the physical server ?</p></li>
<li><p>What if an adversary plugs in or removes any usb device on the server ?</p></li>
<li><p>What if an adversary shuts down the electricity of the entire house before coming in ?</p></li>
<li><p>What if an adversary steals your password or ssh key somehow and manages to login via SSH on the server ?</p></li>
<li><p>What if an adversary physically destroys the harddrive containing the veracrypt partition ?</p></li>
<li><p>What if an adversary tries to do a cold boot attack ? (meaning forensics regarding server logs and RAM live memory)</p></li>
<li><p>What if an adversary forces you to type in your password to your encrypted data ?</p></li>
</ol>
<p>Let's take all those threat vectors into account, and setup our homeserver with the following physical security setup:</p>
<imgsrc="0.png"class="imgRz">
</div>
</div><!-- /row -->
</div><!-- /container -->
</div><!-- /grey -->
<!-- +++++ Second Post +++++ -->
<divid="anon3">
<divclass="container">
<divclass="row">
<divclass="col-lg-8 col-lg-offset-2">
<h2><b>Initial Setup </b></h2>
<p>First we setup a debian QEMU VM in the hidden veracrypt partition:</p>
<b>DISCLAIMER: we're using only harddrives (HDDs) here, because using SSDs are not a secure way to have Plausible Deniability, that is due to hidden Volumes being detectable on devices that utilize wear-leveling</b>
"Also as mentioned earlier, disabling Trim will reduce the lifetime of your SSD drive and will significantly impact its performance over time (your laptop will become slower and slower over several months of use until it becomes almost unusable, you will then have to clean the drive and re-install everything). But you must do it to prevent data leaks that could allow forensics to defeat your plausible deniability. The only way around this at the moment is to have a laptop with a classic HDD drive instead."
</pre></code>
<p>Look at <ahref="../veracrypt/index.html">this tutorial</a> on how to create a veracrypt hidden partition, now create a volume that can contain a debian VM inside the hidden partition (ex: outer volume 140G, and inner/hidden volume 70G). Also follow <ahref="../antiforensics/index.html">this tutorial</a> to know how to setup a QEMU hypervisor to virtualize VMs on linux. Now in the hidden partition we will create the debian QEMU VM (whose disk is 50Gb) as follows (keep in mind that we will also need to copy it into the decoy partition without overwriting the hidden partition, so make sure you can fit the same VM there aswell when you are creating the veracrypt volume!):</p>
<p>side note: make sure that you also copy the same VM into the decoy partition in case if you are asked to disprove the claim that said vm name inside the veracrypt partition contains something sensitive. </p>
<p>we make it run a .onion service like so as shown on <ahref="../torwebsite/index.html">this tutorial</a></p>
<p>So now we have the following graph:</p>
<imgsrc="10.png"style="width:300px">
<p>We now have a server at home, that contains a veracrypt hidden partition (whose existance shouldnt be revealed), that hidden partition contains a VM, which contains a .onion service we want to hide the existance of. So now let's protect it: </p>
<p>First let's define how to shutdown the services and hide the veracrypt hidden partition in one simple bash script:</p>
<p>We have the following order of events:</p>
<p>First we open the veracrypt volume, then we add the QEMU VM in virt-manager while naming it, then we power it on, and then we have our plausibly-deniable onion service.</p>
<imgsrc="12.png"class="imgRz">
<p>So for an emergency shutdown, we need to do the same in the reverse order: First we shutdown the VM forcefully, then we close the hidden veracrypt partition forcefully, then we wipe the logs, then we kill the veracrypt process, and then then we wipe the ram 3 times to erase all potential trace of VMs in live memory. </p>
<imgsrc="14.png"class="imgRz">
<p>Then we assemble that in a bashscript like so:</p>
<pre><codeclass="nim">
[ Wonderland ] [ /dev/pts/1 ] [~]
→ sudo cat /root/emergencyshutdown.sh
[sudo] password for nihilist:
#!/bin/bash
#remove VM and undefine it: (make sure that the VM name is exact!!!)
sudo virsh -c qemu:///system destroy debian12-VM
sudo virsh -c qemu:///system undefine debian12-VM
#make sure that there is also an innocent VM called debian12-VM in the outer partition (for plausible deniability)
#unmount veracrypt drives forcefully
sudo veracrypt -d -f
# then cleanup logs
sudo rm -rf /dev/shm/*
sudo rm -rf /var/log/*
sudo dmesg -c >/dev/null 2>/dev/null
# kill veracrypt to avoid having the veracrypt window display which drive/volume was selected
kill $(pidof veracrypt)
# then wipe ram 3 times
# apt install stress
# below change 128G to the number of Gigabytes in your system ! put 16 if you only have 16Gb of ram!
stress -m 1 --vm-bytes 128G -t 10
stress -m 1 --vm-bytes 128G -t 10
stress -m 1 --vm-bytes 128G -t 10
</code></pre>
<p>you can run it like so:</p>
<pre><codeclass="nim">
chmod +x ./emergencyshutdown.sh
./emergencyshutdown.sh
</code></pre>
<p>Next we need to be able to send an email from the server to the administrator to notify him that an emergency shutdown just happened.</p>
<imgsrc="17.png"class="imgRz">
<p>We'll be using ssmtp from the homeserver itself , it's going to connect to a remote mail server to send the mail we want. (check <ahref="../mailprivate/index.html">this tutorial</a> if you want to know how to setup your SMTP mail server):</p>
<pre><codeclass="nim">
[ Wonderland ] [ /dev/pts/1 ] [~]
→ apt install ssmtp
</code></pre>
<p>Then make sure the user exists on the smtp server:</p>
<pre><codeclass="nim">
root@mail-nihilism:~# useradd -G mail -m surveillance
#remove VM and undefine it: (make sure that the VM name is exact!!!)
sudo virsh -c qemu:///system destroy debian12-VM
sudo virsh -c qemu:///system undefine debian12-VM
#make sure that there is also an innocent VM called debian12-VM in the outer partition (for plausible deniability)
#unmount veracrypt drives forcefully
sudo veracrypt -d -f
# then cleanup logs
sudo rm -rf /dev/shm/*
sudo rm -rf /var/log/*
sudo dmesg -c >/dev/null 2>/dev/null
# kill veracrypt to avoid having the veracrypt window display which drive/volume was selected
kill $(pidof veracrypt)
# then wipe ram 3 times
# apt install stress
# below change 128G to the number of Gigabytes in your system ! put 16 if you only have 16Gb of ram!
stress -m 1 --vm-bytes 128G -t 10
stress -m 1 --vm-bytes 128G -t 10
stress -m 1 --vm-bytes 128G -t 10
</pre></code>
</div>
</div><!-- /row -->
</div><!-- /container -->
</div><!-- /white -->
<divid="anon1">
<divclass="container">
<divclass="row">
<divclass="col-lg-8 col-lg-offset-2">
<h2><b>Surveillance Setup</b></h2></br></br>
<p>So now that we have our actions completed (emergency shutdown and sending a mail), we need to make sure that both are triggered whenever necessary, as shown below:</p>
<imgsrc="16.png"class="imgRz">
<p>To look out for any usb change on the homeserver, we have the following script:</p>
echo -en "Subject: USB CHANGE DETECTED ON $(hostname) \n\n USB CHANGE DETECTED on $(hostname) at $(date): \n\n LSUSB WAS: \n $defaultlsusb \n\n LSUSB NOW IS: \n $(lsusb)" | sudo ssmtp -vvv nihilist@nowhere.moe
if [ $(cat /tmp/maintenance) -eq 0 ]; # if no maintenance, look for usb changes
then
usbnow=$(lsusb | sha512sum)
if [ "$usbnow" = "$defaultusb" ]; #detect usb changes
then
echo "[+] No usb change..."
else
echo "[+] USB change detected, peforming action"
echo $defaultusb
echo $usbnow
action_for_unauthorized_usb_change
#sleep 3600
sleep 10
defaultlsusb=$(lsusb)
defaultusb=$(lsusb | sha512sum)
fi
else #maintenance ongoing, then not checking for usb changes
echo "[+] Maintenance mode, not checking for usb changes..."
fi
sleep 1
done
</code></pre>
<p>Then to detect any movement in the room we setup motion, this will make it possible for a cheap usb webcam to take pictures upon detecting movement in the room where the homeserver is:</p>
<pre><codeclass="nim">
[ Wonderland ] [ /dev/pts/5 ] [~]
→ apt-get install v4l-utils motion -y
[ Wonderland ] [ /dev/pts/5 ] [~]
→ cat /etc/motion/motion.conf
# Rename this distribution example file to motion.conf
<p>From there you can see if the webcam works on port 9091:</p>
<imgsrc="1.png"class="imgRz">
<p>Then we can see that motion to saves pictures once per second once it detects movement in /tmp/room:</p>
<pre><codeclass="nim">
Every 1.0s: ls -l /tmp/room wonderland: Thu Mar 28 17:07:51 2024
total 368
-rw-r--r-- 1 motion motion 52251 Mar 28 17:07 20240328170745-01.jpg
-rw-r--r-- 1 motion motion 52129 Mar 28 17:07 20240328170746-00.jpg
-rw-r--r-- 1 motion motion 52471 Mar 28 17:07 20240328170746-01.jpg
-rw-r--r-- 1 motion motion 37158 Mar 28 17:07 20240328170747-00.jpg
-rw-r--r-- 1 motion motion 33439 Mar 28 17:07 20240328170747-01.jpg
-rw-r--r-- 1 motion motion 22586 Mar 28 17:07 20240328170748-00.jpg
-rw-r--r-- 1 motion motion 19099 Mar 28 17:07 20240328170748-01.jpg
-rw-r--r-- 1 motion motion 18205 Mar 28 17:07 20240328170749-00.jpg
-rw-r--r-- 1 motion motion 19284 Mar 28 17:07 20240328170749-01.jpg
-rw-r--r-- 1 motion motion 49770 Mar 28 17:07 20240328170750-00.jpg
</code></pre>
<p>Then we have this script to check if there are any new files in /tmp/room/ and if there are then we put them in a zipfile before SEND it via mail to the admin:</p>
echo -e "Subject: MOVEMENT DETECTED IN ROOM OF $(hostname) \n\n MOVEMENT DETECTED IN ROOM OF $(hostname) AT $(date):"| (cat - && uuencode /tmp/images.zip images.zip) | sudo ssmtp -vvv nihilist@nowhere.moe
if [ $(cat /tmp/maintenance) -eq 0 ]; # if no room maintenance, look for room changes
then
roomnow=$(ls /tmp/room | sha512sum)
if [ "$roomnow" = "$defaultroom" ]; #detect usb changes
then
echo "[+] No Room Movement..."
else
echo "[+] Room movement detected, peforming action"
echo $defaulroom
echo $roomnow
action_for_unauthorized_room_motion
#sleep 3600
sleep 10
defaultroom=$(ls /tmp/room | sha512sum)
find /tmp/room/ -name *.jpg > /tmp/oldfiles
fi
else #maintenance ongoing, then not checking for room movements
echo "[+] Maintenance mode, not checking for room movements..."
fi
sleep 1
done
</code></pre>
<p>We also make a systemd service to automatically launch/relaunch the checkusb.sh script and checkmovement_room script, and also to make sure they auto start when the server boots up:</p>
Apr 01 10:31:40 wonderland sshd[3103410]: Server listening on 0.0.0.0 port 22.
Apr 01 10:31:40 wonderland sshd[3103410]: debug1: Bind to port 22 on ::.
Apr 01 10:31:40 wonderland sshd[3103410]: Server listening on :: port 22.
</pre></code>
<p>Now like this, the trap ssh service on port 22 is going to force the execution of only our emergency shutdown script if any user manages to login. Meaning the only way to get in is through the sshd2 port on port 2222 after doing the port knocking procedure:</p>
<pre><codeclass="nim">
#testing to login on port 22 shows that the emergencyshutdown script is triggered:
[ mainpc ] [ /dev/pts/7 ] [~]
→ ssh root@192.168.0.100 -i ~/.ssh/torified
Enter passphrase for key '/home/nihilist/.ssh/torified':
<b>Unauthorized, reporting incident to administrator, and performing emergency shutdown </b>
Connection to 192.168.0.100 closed.
#trying to evade the forced command execution fails:
[ mainpc ] [ /dev/pts/7 ] [~]
→ ssh root@192.168.0.100 -i ~/.ssh/torified bash
Enter passphrase for key '/home/nihilist/.ssh/torified':
<b>Unauthorized, reporting incident to administrator, and performing emergency shutdown</b>
#so in order to login we need to open the secret sshd2 port with the specific port knocking procedure as follows:
[ mainpc ] [ /dev/pts/7 ] [~]
→ for x in 7000 8000 9000; do nmap -Pn --max-retries 0 -p $x 192.168.0.100; done >/dev/null
#now that the port knocking is completed, we can login via ssh on port 2222:
ssh: connect to host 192.168.0.100 port 2222: Connection refused
</pre></code>
<p>Next, to make sure that failed ssh login attempts get banned with fail2ban, we make sure that sshd outputs to auth.log, that way we ward off any potential ssh bruteforce attack, you can check out how to setup fail2ban with <ahref="../fail2banssh/index.html">this tutorial</a> to know how to do it.</p>
</div>
</div><!-- /row -->
</div><!-- /container -->
</div><!-- /white -->
<divid="anon2">
<divclass="container">
<divclass="row">
<divclass="col-lg-8 col-lg-offset-2">
<h2><b>Clientside precautions</b></h2></br></br>
<p>So here we want to mke a bash/zsh command function to simplify the opening and closing of the secret ssh port. You can use the one i made below for the same:</p>
port knocking 192.168.0.100 with sequence 9000 8000 7000
</pre></code>
<p>That way, we make sure that we do not hardcode the port sequence to open/close the secret ssh port, and we also make sure that the secret ssh port is not revealed. This means that from now on you will need to remember the port sequence to open the secret ssh port, and also the secret ssh port itself. Without those, you will be unable to login to the server.</p>
<p>Now however the risk is that you'd reveal the port sequence and ssh port from the bash or zsh history file: </p>
<p>so let's make sure that there is a cronjob that runs on the clientside to sed out the lines containing ssh, scp, rsync, nmap and secretssh on the /home/*/.zsh_history files:</p>
<pre><codeclass="nim">
[ mainpc ] [ /dev/pts/10 ] [~/Nextcloud/blog]
→ sed -i s'/.*nmap.*//gi' /home/*/.zsh_history /home/*/.bash_history /root/.zsh_history /root/.bash_history
<p>And that's it! now we have a secure way of accessing the server, without revealing the port knocking sequence nor the secret ssh port.</p>
</div>
</div><!-- /row -->
</div><!-- /container -->
</div><!-- /white -->
<divid="anon1">
<divclass="container">
<divclass="row">
<divclass="col-lg-8 col-lg-offset-2">
<h2><b>UPS setup in case of a power outage (WIP)</b></h2></br></br>
<p>TODO: Then we make sure that when the power goes out, the UPS gives the signal for the homeserver to do the emergency shutdown script before shutting down, in order to hide the content of the server, before gracefully shutting down. </p>
<pre><codeclass="nim">
</pre></code>
<p>Next step is to look at how to make sure the .onion service keeps running even after shutting down the entire homeserver, to make sure that it is impossible to prove that you are maintaining the sensitive service. We'll go into it with how Endgame V3 can be setup in a future tutorial.</p>