Novena-as-a-Keyboard

There's just something magical about watching text flow along a screen. It's the basis for Defcon boards made popular by the movie Wargames, and is popular in the background of most movies. It's rare that such a display happens in the real world, but I actually had the occasion to need to try as many as 65,000 possible passphrases on a system that only had USB input. This gave me an idea. Maybe it would be possible to have Novena emulate a USB keyboard to type in all possible passphrases.

When we were designing the Novena block diagram, we were trying to decide how the USB topology would look. The i.MX6 provides two USB hosts, but one can also be turned into a USB device. USB hubs add latency, so it's a good idea to minimize their use as much as possible. Certainly you want to put ports for high-bandwidth devices such as cameras closer to the CPU. I thought it would be useful to have Novena act as a USB device, and so I campaigned to put in a second hub for some of the external ports, and use the second USB host block in Device mode.

This is how the Novena shipped, and we actually didn't do much with it at first. Drivers to allow the USB controller to run in Device mode didn't make it into the kernel until about a year ago, and even then it was limited in its use. The only modules available were very limited, and you could only use one at a time. We could run it as a USB audio device, but only at specific frequencies, and not very reliably. A special g_multi device was available, but in general it wasn't very useful.

Now we have libcomposite and configfs in the kernel. This combination allows for creating multiple USB devices on the fly by simply creating directories and loading data into files on a special pseudo-filesystem. No more worrying about which modules are currently loaded, and no more instability when loading and unloading modules. The configuration process is more complicated than simply running "modprobe", but it offers much more flexibility in return.

Back to my passphrase problem. The passphrase was protected by a crc16, which means that there are up to 65,536 possible values to try. They seemed to use nonstandard polynomials and nonstandard XOR values, so reversing that could be nontrivial. After putting a few passphrases in by hand, I noticed there didn't seem to be any backoff time, and so I thought about trying all combinations. Each passphrase takes about half a second to verify, and depending on the speed of input, a passphrase can be entered in about half a second. That means one passphrase per second. At 65,536 possibilities, it takes just above 18 hours to try all combinations. Perfect, especially since it can run autonomously.

The first order of business is to generate all possible passphrases. This involved modifying the generator to generate passphrases with each possible crc16 value and write each one out to a text file with one per line. Something as simple as:

for crc in range(65536):
    print(generate_passphrase(crc) + "\n")

Next, I needed to get Novena to actually act as a USB keyboard. This is the tricky bit, owing to a lack of available documentation. I put together a simple shell script that creates the directories and creates the requisite files, based on information I gathered from around the Internet:

#!/bin/sh

configfs="/sys/kernel/config/usb_gadget"
g=g1
c=c.1
d="${configfs}/${g}"
func=hid.usb0

do_stop() {
        echo "" > "${d}/UDC"

        rm "${d}/configs/${c}/${func}"

        rmdir ${d}/strings/0x409/

        rmdir "${d}/configs/${c}/strings/0x409"
        rmdir "${d}/configs/${c}"
        rmdir "${d}/functions/${func}"
        rmdir "${d}"
}

do_start() {
        mkdir "${d}"
        echo 0x05e2 > "${d}/idVendor"
        echo 0x0613 > "${d}/idProduct"
        echo 8 > "${d}/bMaxPacketSize0"

        mkdir "${d}/strings/0x409"
        echo "12345" > "${d}/strings/0x409/serialnumber"
        echo "kosagi" > "${d}/strings/0x409/manufacturer"
        echo "Software keyboard" > "${d}/strings/0x409/product"

        mkdir "${d}/configs/${c}"

        mkdir "${d}/configs/${c}/strings/0x409"
        echo "config 1" > "${d}/configs/${c}/strings/0x409/configuration"
        echo 120 > "${d}/configs/${c}/MaxPower"

        mkdir "${d}/functions/${func}"
        echo "1" > "${d}/functions/${func}/protocol"
        echo "1" > "${d}/functions/${func}/subclass"
        echo "8" > "${d}/functions/${func}/report_length"
        /bin/bash -c 'echo -ne \\x05\\x01\\x09\\x06\\xa1\\x01\\x05\\x07\\x19\\xe0\\x29\\xe7\\x15\\x00\\x25\\x01\\x75\\x01\\x95\\x08\\x81\\x02\\x95\\x01\\x75\\x08\\x81\\x03\\x95\\x05\\x75\\x01\\x05\\x08\\x19\\x01\\x29\\x05\\x91\\x02\\x95\\x01\\x75\\x03\\x91\\x03\\x95\\x06\\x75\\x08\\x15\\x00\\x25\\x65\\x05\\x07\\x19\\x00\\x29\\x65\\x81\\x00\\xc0' > "${d}/functions/${func}/report_desc"

        ln -s "${d}/functions/${func}" "${d}/configs/${c}"

        udc=$(ls -1 /sys/class/udc/)
        echo "${udc}" > "${d}/UDC"
}

if [ "$1" = "start" ]
then
        do_start
elif [ "$1" = "stop" ]
then
        do_stop
else
        echo "Usage: $0 (stop | start)"
fi

Load this into a file, mark it executable, and run "start". It should complete without errors, load the HID module, and create a device node under /dev/hidg0. This file is a representation of USB endpoint 0: Write to it to send packets out the wire, read from it to get data from the wire, all using logical endpoint 0.

USB HID has some special features. Gamers are familiar with 6RKO -- 6-Key RollOver, which describes the fact that a standard USB keyboard can only support six keys at a time, due to the fact that HID devices use 8-byte packets and the first two bytes are dedicated to special keys such as shift and control.

With the pipe set up, there are two more pieces we need. The first is a way to format USB HID packets, translating from ASCII to the HID mapping. The second is a way to time these packets, and to try each passphrase in turn.

For the first program, we can use tester code from the Linux kernel Documentation/ directory. The usb documentation includes gadgethid.txt, which contains a program we can compile and use wholesale. Write it to a file, compile it with "gcc gadgethid.c -o gadget_hid".

For the second, I decided to use my default prototyping language: Perl. I created a program that reads values line-by-line, splits each line up, emits one character, pauses the appropriate amount of time, emits a "Return" key at the end of a passphrase, and pauses between passphrases.

Then, connect the USB cable to the target device, feed the Perl program into gadget_hid, and go take a very long coffee break.

The system ran for about ten hours and 30,000 passphrases before running across a working one. Since it's a feed-forward system, it just kept plugging in potential passphrases even after it had found a working one.

But it worked, and now I have another tool in the box. I can now hook Novena up to any machine and have it act as a keyboard. With a small change, I can have it act as a joystick, or as a mouse. This approach can also be adapted to other USB devices, but that's a problem for another day.