After a long time discussing the concept of USB sticks with internal AV engines, I’ve decided to create a quick and dirty PoC. Thus, this post shows how to utilize a USB Armory MK II and ClamAV as a self-scanning USB stick. The Summary: It failed successfully!

The Aim

A magic USB stick, with an LED that blinks when a device pushes malware onto the device.

The Setup

Prepare USB Amory

Head over to the USB Armory GitHub page and prepare it with the latest Debian image.

Expand Memory

Check here how to extend the partition on the SD card.

Configure USB Armory

Create a SWAP File

We sadly don’t have quite enough RAM to run ClamAV.

fallocate -l 1G /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile

Add /swapfile swap swap defaults 0 0 to /etc/fstab

Create an image for USB Storage emulation

Create the image file

dd if=/dev/zero of=usb.img bs=4096 count=81920

Use fdisk to create a partition table (o) and create a new partition (p). Then set the type (t) to FAT32 (c) and write the changes (w).

mkfs.fat usb.img

I created the file on my host machine and pushed it to the USB Armory via scp. Park the file in /opt/

Enable USB Storage Mode

Head to the USB Armory docs to enable USB Storage Mode. For the PoC I stuck to the CDC and Mass Storage emulation. Set g_multi in /etc/modules and options g_multi use_eem=0 dev_addr=aa:bb:cc:dd:ee:f1 host_addr=aa:bb:cc:dd:ee:f2 file=/opt/usb.img in /etc/modprobe.d/usbarmory.conf.

Setup ClamAV

Install with apt install clamav, then restart the USB Armory. Then copy the signatures from the ClamAV website to the USB Armory via scp. You will need

I got rate limited/banned by cloudflare during my initial tests, so careful! The allowed rate is described in the following block.

Error Message
FreshClam previously received error code 429 or 403 from the ClamAV Content Delivery Network (CDN).
 This means that you have been rate limited or blocked by the CDN.
  1. Verify that you're running a supported ClamAV version.
     See https://docs.clamav.net/faq/faq-eol.html for details.
  2. Run FreshClam no more than once an hour to check for updates.
     FreshClam should check DNS first to see if an update is needed.
  3. If you have more than 10 hosts on your network attempting to download,
     it is recommended that you set up a private mirror on your network using
     cvdupdate (https://pypi.org/project/cvdupdate/) to save bandwidth on the
     CDN and your own network.
  4. Please do not open a ticket asking for an exemption from the rate limit,
     it will not be granted

Copy them to /var/lib/clamav/

cp *.cvd /var/lib/clamav/
chown clamav:clamav /var/lib/clamav/*.cvd
chmod 644 /var/lib/clamav/*.cvd

Do a test scan running clamscan -r /opt/usb_scan. Or rather start it and get a pizza. The Loading step took me 7m 30s, the Compiling step took 15m 53s and then it took a while longer to finish.

Why not freshclam?

freshclam will require a significant amount of RAM when verifying the database download. This will fail on the USB Armory.

Scanning

Create a copy of usb.img (cp /opt/usb.img /opt/usb_scan.img), mount (mkdir /opt/usb_scan, mount /opt/usb_scan.img /opt/usb_scan) it at and perform the scan (clamscan -v -r /opt/usb_scan/).

Stats

I’m running

root@usbarmory:/# clamscan --version
ClamAV 1.0.3/27193/Thu Feb 22 18:25:15 2024
root@usbarmory:/# uname -r
6.6.17-0

In g_multi mode

  • Loading hits a wall at around 1.8M and then freezes for a while
  • Compiling feels frozen the whole time

First run

----------- SCAN SUMMARY -----------
Known viruses: 8685829
Engine version: 1.0.3
Scanned directories: 1
Scanned files: 0
Infected files: 0
Data scanned: 0.00 MB
Data read: 0.00 MB (ratio 0.00:1)
Time: 2625.456 sec (43 m 45 s)
Start Date: 2024:02:22 19:02:39
End Date:   2024:02:22 19:46:25

Second run

----------- SCAN SUMMARY -----------
Known viruses: 8685829
Engine version: 1.0.3
Scanned directories: 1
Scanned files: 0
Infected files: 0
Data scanned: 0.00 MB
Data read: 0.00 MB (ratio 0.00:1)
Time: 2696.123 sec (44 m 56 s)
Start Date: 2024:02:22 19:48:43
End Date:   2024:02:22 20:33:39

In g_ether mode

I thought that maybe the USB storage functionality would have certain performance impact, so I switched it off for a second try.

  • Loading actually counts and doesn’t seem to freeze
  • Compiling still feels frozen

First run

----------- SCAN SUMMARY -----------
Known viruses: 8685829
Engine version: 1.0.3
Scanned directories: 1
Scanned files: 0
Infected files: 0
Data scanned: 0.00 MB
Data read: 0.00 MB (ratio 0.00:1)
Time: 2681.409 sec (44 m 41 s)
Start Date: 2024:02:22 21:26:26
End Date:   2024:02:22 22:11:07

Second run

----------- SCAN SUMMARY -----------
Known viruses: 8685829
Engine version: 1.0.3
Scanned directories: 1
Scanned files: 0
Infected files: 0
Data scanned: 0.00 MB
Data read: 0.00 MB (ratio 0.00:1)
Time: 2814.201 sec (46 m 54 s)
Start Date: 2024:02:22 22:21:03
End Date:   2024:02:22 23:07:57

Notes

  • Running freshclam will fail with the error down below, too little RAM
  • Running clamav without swap will fail with Killedg: 54s, ETA: 3m 15s [====> ] 1.88M/8.70M sigs, too little RAM
  • Various posts on the net recommend running clamav only on machines with more that 1,5GB or rather 2GB of RAM
Error Message
--------------------------------------
freshclam daemon 1.0.3 (OS: Linux, ARCH: armv8l, CPU: armv8l)
ClamAV update process started at Thu Feb 22 18:38:34 2024
daily database available for download (remote version: 27193)
Testing database: '/var/lib/clamav/tmp.ce83264c8b/clamav-9f520a510385018f271dbc5519d752b7.tmp-daily.cvd' ...
ERROR: Database load killed by signal 9
ERROR: Database test FAILED.
ERROR: Unexpected error when attempting to update daily: Test failed
ERROR: Database update process failed: Test failed
ERROR: Update failed.
--------------------------------------

Result

Well, successful, but not the result I had hoped for. 45minutes for a scan simply is too slow, far too slow. Still, the issue doesn’t seem to be the actual scan, but preparing the signature map, which might be patachable into only running once and not on every scan. Using clamd might also be an approach, but it would still load the database on every power cycle of the USB stick, thus on every use.
What was the actual use case? The initial idea was to have a USB stick, which is used for extracting log files from potentially infected systems (old stuff), that will simply show if it gets infected by the system it’s connected to.
Failed? The initial test used the full ClamAV database, the use case though aims at old stuff, let’s say Windows XP etc.. Coming from here the list of potential malware is certainly limited and might be a basis for optimzation.
Alternatives? Knowing the potentially infected systems and knowing which logfiles should be pushed to the USB stick, it’s easy to do a little pattern matching/anomaly detection/sanity checks. Does the Host create an autorun.inf?. Coming from here it can be pretty easy to identify whether the host does what it’s expected to do or has learned a new trick. Also explicit hash or name based black- and white-listing can be implemented trivially.
Next Step Find a Raspberry Pi 4B and use that to emulate a USB Mass Storage device for another benchmark.