14

On a given USB port, I only want to accept USB Mass Storage capabilities, and nothing else. No HID devices, no Bluetooth adapters, no RS232 converters, nothing. Is there a way to do this e.g. using udev? I'm aware that I can write a custom udev rule for including a driver for a given device, or a given port, but can I somehow exclude all other drivers? I'm trying to only allow one class of devices, namely USB Mass Storage; there are a myriad different device models in this class, and I don't know which are going to connect to the port (clients will bring their own, there is no way for me to affect this).

Threats from reprogrammed USB firmware are only going to get worse in the foreseeable future. I'm trying to mitigate them for this use case: I have a computer with internally connected USB peripherals (network card, specialized peripherals, keyboard) and one public-facing USB port, which is only supposed to be used for file transfer. So, I can't blacklist the other USB modules altogether; but I'd like to "sanitize" that particular port, so that plugging in a different device type would do nothing.

The case is physically locked, so that only this one specific USB port is accessible from outside, and meddling with the case or splicing into the keyboard cable should be suspicious enough to trigger physical security response; moreover, I don't expect most of the users to be actively malicious, but I expect the number of unwitting carriers of re-flashed USB drives to increase (as it was with floppy boot sector infections of yore). As far as security goes, it does not really matter whether the user brings the "weaponized" USB disk with malicious intent, or just doesn't know that it's "infected".

I am aware that perfect security is infeasible here, and allowing user to interact with the system in any way is risky - but alas, I need to balance security against usability: the computer needs to be client-usable. Also, I'm not trying to defend against a targetted, determined attacker with this; rather, I'm using this as one of mitigation techniques, so that the system is not low-hanging-fruit.

  • (I'm aware that some USB storage devices are internally a USB hub with the mass storage hanging off it, but I'm trying to build the minimal case first) – Piskvor left the building Oct 02 '14 at 15:19
  • 2
    Um. I'm just learning stuff, so not sure if this may help , but see if this is relevant: "http://www.linux-usb.org/FAQ.html" Specifically , scroll to "Why do I only see one device from my multipurpose storage device" question, and there's line " If you do not want to do this for all SCSI devices then you can tell the kernel to scan for a specific device using;

    echo >/proc/scsi/scsi "scsi add-single-device 0 0 0 1" "

    – Sergiy Kolodyazhnyy Oct 06 '14 at 08:46
  • @Xieerqi: That's an interesting document, thank you; unfortunately, that does not solve my problem: I don't really care how many storage devices are connected; all I want is "anything connected that does not identify as a storage device is ignored by the system." – Piskvor left the building Oct 06 '14 at 09:13
  • 1
    You only want a specific port to be locked to USB Mass Storage? – Kaz Wolfe Oct 06 '14 at 09:41
  • 3
    "With physical access, the battle for security is lost." ~Every Security.SE user ever. – Kaz Wolfe Oct 06 '14 at 10:17
  • 1
    @Piskvor I'm not sure but you might have better luck finding an answer at http://security.stackexchange.com/ or one of the other more advanced / professional-oriented sites. – Brian Z Oct 06 '14 at 12:32
  • 2
    Maybe this can help: http://www.irongeek.com/i.php?page=security%2Fplug-and-prey-malicious-usb-devices , especially the 3.2 Locking down Linux using UDEV section. – alci Oct 06 '14 at 12:32
  • 1
    @Whaaaaaat: As for physical access - I agree completely. The other ports are not physically accessible to the users, and disassembling the case (or the keyboard, in order to splice into the USB cable) should be suspicious enough to get the user evicted from the premises in short order. – Piskvor left the building Oct 06 '14 at 12:57
  • @alci: That looks promising, thank you! Will look into this. – Piskvor left the building Oct 06 '14 at 13:02
  • @BrianZ: I'll see how well the bounty goes; I've already received several useful pointers here in the comments. I have indeed been choosing between here and SE for this question. – Piskvor left the building Oct 06 '14 at 13:09
  • No matter what, a user can take over. Reboot into a LiveUSB. Udev protection gone. Screw with BIOS. Use DBAN on the hard drive. You are never safe if the user has access to any buttons. – Kaz Wolfe Oct 07 '14 at 09:07
  • 1
    @Whaaaaaat: I'm not looking for a 100% gold-plated solution: I'm fully aware that the only safe computer is switched off, in a block of concrete, at the bottom of Mariana Trench. A determined enough attacker can get through any defense; it doesn't follow that I should leave the system unsecured against a drive-by attack on LHF. – Piskvor left the building Oct 07 '14 at 09:33
  • 1
    @Whaaaaaat: I have gone through the usual checklist - password-protected BIOS, only allow boot from the internal CD-ROM (read-only by design, not physically accessible), minimal user rights, disable CAD and Magic SysRQ, etc.; none of that guarantees perfect security, but each of it helps. – Piskvor left the building Oct 07 '14 at 09:45

1 Answers1

17

It seems to work for me in Ubuntu 14.04 with 2 flash keys & android phone as storage and usb network adapter & webcam as other type. (I couldn't test placing a usb hub)

  1. Check USB port (which is a parent device for the plugged device)

    $ udevadm info --name=/dev/sdc --attribute-walk
    
      looking at parent device '/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0':
        KERNELS=="2-1.2:1.0"
        SUBSYSTEMS=="usb"
        DRIVERS=="usb-storage"
        ATTRS{bInterfaceClass}=="08"
        ATTRS{bInterfaceSubClass}=="06"
        ATTRS{bInterfaceProtocol}=="50"
        ATTRS{bNumEndpoints}=="02"
        ATTRS{supports_autosuspend}=="1"
        ATTRS{bAlternateSetting}==" 0"
        ATTRS{bInterfaceNumber}=="00"
    
  2. Create udev rule, matching usb port kernel name without usb-storage driver

    /etc/udev/rules.d/90-remove-non-storage.rules

    Allow any device that have storage as 1st interface (Composite devices allowed)

    KERNELS=="2-1.2:1.0", DRIVERS!="usb-storage", RUN+="/bin/sh -c 'echo 1 > /sys/bus/usb/drivers/hub/2-1\:1.0/port2/device/remove'"
    

    Block any device that have a non storage interface (Composite devices denied)

    Actually, Phone gets mounted as modem to /dev/ttyACM0 as KERNELS=="2-1.2:1.1". This will not let phones (composite devices) to be mount only simple storage devices will.

    KERNELS=="2-1.2:1.[0-9]*", DRIVERS!="usb-storage", RUN+="/bin/sh -c 'echo 1 > /sys/bus/usb/drivers/hub/2-1\:1.0/port2/device/remove'"
    

    Block only interfaces that are not storage (Composite devices allowed as storage only)

    After some search about a way to disable only non allowed interfaces. Driver unbinding seems to work. My phone could only be used as storage, It does not create /dev/ttyACM0.

    KERNELS=="2-1.2:1.[0-9]*", DRIVERS!="usb-storage", RUN+="/bin/sh -c 'echo -n %k >/sys%p/driver/unbind'"
    
  3. Reload udev rules

    udevadm control --reload-rules
    

References:

user.dz
  • 48,105
  • Beat me to it... – Kaz Wolfe Oct 06 '14 at 15:52
  • @Whaaaaaat, yep authorized does work too, but remove is a cleaner way as user gonna unplug it anyway. I have checked unmounted drives, they still have usb-storage as driver for a parent node. I agree, there are some cases where usb-storage may not be used. BTW, :) u consume too many comments (2min, 1min), you could edit 1st one (in <5min) – user.dz Oct 06 '14 at 16:41
  • @Whaaaaaat: When I was testing this, the usb-storage driver got loaded right before the block device(s) were created; mounting them was not necessary. – Piskvor left the building Oct 07 '14 at 08:56
  • @Piskvor Did this answer your question? If so, you should mark this as the correct answer and award your bounty. – John Scott Oct 09 '14 at 16:44
  • 1
    This is a good approach, but why name the script 90-? Udev rules are processed in lexical order, so the lower number script gets executed first. Would you not want to run your remove script before anything else happens with the devices that you want to block? – user643011 Dec 09 '22 at 20:52
  • 1
    Can you think of any race conditions where the plugged-in device could exploit a bug in the kernel before it gets removed by the udev script? What exactly happens from the point you plug in a device until the script runs? Could you add more sources to your answer? – user643011 Dec 09 '22 at 20:56
  • 1
    @user643011 Yes, a lower number seems more convenient for this case, I just used to work late scripts. Udev is slow in that sense and may miss event while booting. Instead of blacklist, it may be best for security thinking of whitelist. Every thing blocked till allowed by udev. Seems not easy but could be tested in a virtual box, see this post: https://unix.stackexchange.com/a/412227/12209 – user.dz Dec 09 '22 at 22:05
  • @user.dz Exactly what I was looking for! Much appreciated, thanks for the quick reply! – user643011 Dec 09 '22 at 22:29
  • 1
    One of the comments in the issue that you linked brought me to a project that apparently builds on the safe USB authorization approach in the kernel und comes with an easier to use configuration language than using udev rules directly: https://usbguard.github.io/ – user643011 Dec 09 '22 at 22:42
  • 1
    One more note: I tried your udev rules above, and it worked well. It reliably blocks keyboards, mouses, webcams, NICs, etc. and even USB hubs. But I had to add two udev rules per physical USB port because USB1.1/2/3.0 devices require different kernel-strings on the same physical port. E.g. one physical port required me to add both KERNELS=="1-4:1.[0-9]*" ..... /hub/1-0\:1.0/usb1-port4/device/remove'" and KERNELS=="2-5:1.[0-9]*" ..... /hub/2-0\:1.0/usb2-port5/device/remove'" – user643011 Dec 10 '22 at 08:50
  • 1
    @user643011 Interesting, I didn't notice that. Most probably I was using only USB2 devices. – user.dz Dec 10 '22 at 19:04