Google CTF 2018 – Feel It

We are given a pcap file, so let’s open it up in Wireshark:

As can be seen, the entire pcap consists of USB packets. The overall structure of the file is something like:

  1. At the beginning, the host asks a USB device for its descriptors (i.e. configuration information).
  2. Then, the host repeatedly sends SET_REPORT requests to the device using the USB HID protocol. HID is one of many USB classes, meant for use by keyboards, mice, and other similar devices.
  3. There are also a lot of URB_INTERRUPT packets. Note that they are sent from a different address (1.9.2 instead of 1.9.0), so I assume that it’s coming from a different device, though I’m not entirely sure what. When working on this challenge, we focused on the USB HID packets, and URB_INTERRUPT packets ended up not being important so we didn’t look any more into them.

Starting with the USB descriptors, there are 4 interesting string descriptors. The first one indicates that the language is English, and the other three contain the messages “Manufacturer is confidential”, “and so is product string”, and “not to mention serial number”. What do these messages mean? Looking at the device descriptor (in packet #2) gives us the answer:

As we can see, the manufacturer, product, and serial number are simply 1, 2, and 3, which I imagine are not real values. However, it looks like we still have some information about the device, namely the idVendor and idProduct. Putting idvendor=c251 idproduct=1126 into Google takes us to this link, which is part of brltty, a program that helps display Unix console text on refreshable Braille displays. This means that the USB device is likely a Braille display (and the challenge title makes a lot of sense now!).

We’re now pretty sure that the SET_REPORT requests contain data that somehow indicates which dots are raised on the Braille display. So now, all we need to do is figure out how the data corresponds to Braille dots.

Unfortunately, this turned out to be a lot harder than we first thought. Googling things like USB Braille specification gave us a bunch of news articles about a new USB Braille standard that was supposedly released a month ago, but we couldn’t find the specifications anywhere (and presumably this device is way older than a month anyways). We also couldn’t find any helpful information on the official USB website which was kind of unfortunate.

At some point, we decided to look at the brltty codebase, thinking that we could find the driver for our Braille display and read the code to figure out how Braille data was represented. From some searching, we found out that the vendor and product number corresponded to the Eurobraille Esys device, and got the corresponding driver code, but none of us were able to understand enough of it for it to be of any use.

At the same time, some of us tried to reverse engineer how to convert packet data to Braille by just looking at the data directly and trying to make the “CTF” letters. The first few SET_REPORT packets had the following hex data:

02005442536a07111b0a1e480e1311070700187b2bc00000000000...
02005442536a07111b0a1e480e1311070700187b2b49c000000000...
02005442536a07111b0a1e480e1311070700187b2b495ec0000000...
02005442536a07111b0a1e480e1311070700187b2b495e4bc00000...
02005442536a07111b0a1e480e1311070700187b2b495e4b2ac000...
02005442536a07111b0a1e480e1311070700187b2b495e4bea0000...

(we left out a lot more 0s at the ends)

As you can see, between consecutive packets, the data barely changes, while gradually growing over time. This feels like it could correspond to dots on a Braille display popping up from left to right. Assuming that this is the case, the final SET_REPORT packet should contain the whole flag represented in Braille somehow:

02005442536a07111b0a1e480e1311070700187b2b00495e4b2a1302191138011d19380e1212053bc0000...

A couple of clarifications, to be precise:

  • The SET_REPORT packets actually alternate between packets containing this “0200544253…” data that gradually grows in size and packets of the form “0000000…55555…”. We ignored all packets of the second form because they were all identical and clearly didn’t contain useful information.
  • The above packet is actually the second-to-last SET_REPORT packet, but the last one is much shorter and doesn’t begin with the same 20 bytes as the others, so we also ignored that one.

We looked the bits of the “0200544253…” strings and tried manipulating them somehow to get valid Braille characters but weren’t having much luck.

However, while combing through the brltty codebase, we found this comment that explained how the dots on an 8-dot Braille display matched to bits (apparently this is fairly standard). If each 8-dot Braille character directly matched to a byte in the packet data in this manner, then we’d be able to decode it. Of course, it’s possible that the data might be compressed or encrypted in some way before being sent over the network, but in the end our wishful thinking worked out and the flag was indeed directly encoded in this manner.

Here is the above hex string represented in binary, ending with the c0:

00000010 00000000 01010100 01000010 01010011 01101010 00000111 00010001 00011011 00001010 
00011110 01001000 00001110 00010011 00010001 00000111 00000111 00000000 00011000 01111011 
00101011 00000000 01001001 01011110 01001011 00101010 00010011 00000010 00011001 00010001 
00111000 00000001 00011101 00011001 00111000 00001110 00010010 00010010 00000101 00111011 
11000000 

In 8-dot Braille, the character “C” is generally written as the normal 6-dot Braille “C”, with the lower-left dot also filled in to indicate capitalization. That is, it looks like

O O
- -
- - 
O -

where O’s represent raised dots. Combined with the dots to bits mapping given above

0 3
1 4
2 5
6 7

means that “C” maps to 01001001. And indeed, there is an 01001001 in the middle of the binary string:

...
01001001 01011110 01001011 00101010
...

We can also attempt to hand-decode the byte 01011110 after the “C”:

            0 1
01011110 -> 1 1 -> "T"
            1 0
            1 0         

This is indeed a capital “T”! We can continue decoding the rest of the characters* to get the flag, CTF{h1de_and_s33k}.

*During the actual CTF, we had a little trouble with the decoding because we couldn’t find a standardized canonical 8-dot Braille alphabet other than with the letters. So we actually ended up extracting CTF{h?de?and? before guessing that the flag would be some variation on “hide and seek”. Our first guess, CTF{h1de_and_seek}, was not correct, but we then replaced the “e”s with “3”s and that worked. 😛

 

One thought on “Google CTF 2018 – Feel It

Leave a reply to Arif Zuhairi (@AreRex14) Cancel reply