Reverse engineering, Windows internals, x86 magic, low level programming, and everything else I feel like writing about.

CrySyS SecChallenge 2020: 30 FPS

Destroying the Death Star 1/5

You and your squad volunteered for the mission to end the havoc of the Empire.

Your task is to sabotage the infamous Death Star.

Now you are at the Death Star and started docking, however, there is a display on the side of the docking station which asks for a password, which you don’t know. Below the display there is an suspicious controller hanging on a wire, which is being used to type the password. You tap into the wires, start a packet capture and start pressing the buttons. It looks like they are using a USB protocol. Jack pot. You plant a small package capturer beneath the display and hide nearby waiting for someone to enter the correct password. Suddenly, a wild ship appears, and after spending some time at the control panel, it enters the Death Star. After you check the device, you see that you have successfully captured a bunch of packages. If you can reverse it back, you will find the password. Hurry up, before the Empire blow up your ship! Good luck rebels, may the Force be with you.

Categories

Hardware

Files

Solution

Start off with opening the packet capture in Wireshark. Looking at the packets we can see most of the communication is between host and 1.9.1. We can also see a few GET DESCRIPTOR responses, which should tell us what device we’re dealing with. Here’s the response belonging to 1.9.0:

DEVICE DESCRIPTOR
    bLength: 18
    bDescriptorType: 0x01 (DEVICE)
    bcdUSB: 0x0200
    bDeviceClass: Vendor Specific (0xff)
    bDeviceSubClass: 71
    bDeviceProtocol: 208
    bMaxPacketSize0: 64
    idVendor: Microsoft Corp. (0x045e)
    idProduct: Xbox One Controller (Firmware 2015) (0x02dd)
    bcdDevice: 0x0203
    iManufacturer: 1
    iProduct: 2
    iSerialNumber: 3
    bNumConfigurations: 1

Great, so we’re dealing with an Xbox One controller. Let’s filter the incoming messages that look like inputs (this is a guess based on count of packets having this size): frame.len == 82 && usb.src == "1.9.1" and save the visible packets’ raw bytes as a C source for further processing. After a bit of fiddling around with regexes in Notepad++, I ended up with the following declaration, with content as you’d expect:

const unsigned char packets[][82] = {

Now it’s time to figure out the inputs. A quick search results in this documentation for the controller’s protocol. The data in our packets start at 0x40, and the data for the buttons we care about is two bytes, located at offset 0x4 and 0x5.

uint16_t get_buttons_for_packet(const unsigned char* p)
{
  return uint16_t(p[0x44]) << 8 | p[0x45];
}

Considering this is a CTF challenge we can make the assumption that only zero or one button is pressed simultanously. This will simplify our code for processing it a bit. Let’s encode the keypad we see as a map:

keypad

const char* map[] = {
  "ABCDEFGHIJ",
  "KLMNOPQRST",
  "UVWXYZabcd",
  "efghijklmn",
  "opqrstuvwx",
  "yz01234567",
  "89.,-_:;{}"
};

Now all we need to do is to emulate the keys’ movements on the key map, and print the selected character on pressing A:

int main()
{
  auto x = 0u, y = 0u;
  for(const auto packet : packets)
    if(const auto buttons = get_buttons_for_packet(packet))
      switch (buttons)
      {
      case 1:
        y--;
        break;
      case 2:
        y++;
        break;
      case 4:
        x--;
        break;
      case 8:
        x++;
        break;
      case 0x1000:
        printf("%c", map[y][x]);
        break;
      default:
        assert(false);
        break;
      }
  return 0;
}

Output:

cd20{R_R_R_R_R_R_A_A_D_D_L_L_L_L_A_R_R_R_U_A}