I've heard talk about a simple method of communication, but I'm not sure what it is. Luckily I found some traffic of the said communication, but I'm too noob to figure it out. Please help me crack this enigma of a transmission.
Find the correct sourcecode which leads to the key mappings.
Decrypt the l2cap packets.
Stonks
Analysis
Viewing the packets captured, we mostly see garbage data that are not relevant to solving the challenge.
We can see that the first few packets are mostly configuration packets and can put these aside first.
Followed by these long chain of USB_INTERRUPT packets, which we can also put aside and come back to them later if needed.
Scrolling down, we suddenly see a bunch of L2CAP packets.
We can filter to see only these packets by entering btl2cap in the filter bar.
Searching online, we can see that L2CAP packets are used for Bluetooth communication.
This indicates that there may be potential data that we can decode in these packets, so let's spend some time looking into it.
Expanding the Bluetooth L2CAP Protocol field in Wireshark, we can see that every single L2CAP packet has the payload starting with a1
This might be a header specific to this kind of Bluetooth device, so we can try looking it up.
Decoding L2CAP Packets
Searching for the keywords bluetooth l2cap a1 payload on Google, we come across a CTF writeup on a similar challenge.
Taken from the writeup:
Let's look at one of the payloads from the pcap: a10201ff0300
Comparing with the above code or image, we can decipher that the a1 byte is the value denoting this is a data packet. 02 is the ID for a mouse...
An example payload we have is a1010200000000000000. We have the a1 the same, but since the second byte they have is 02 which represents the ID for a mouse, our payload has 01, which should represent the ID for a keyboard.
There is also a reference to a source code provided by the author of the writeup:
Looking at the source code on lines 184-190 for our keyboard payload struct, we can see that our payload matches the struct.
struct hidrep_keyb_t
{
unsigned char btcode; // Fixed value for "Data Frame": 0xA1
unsigned char rep_id; // Will be set to REPORTID_KEYBD for "keyboard"
unsigned char modify; // Modifier keys (shift, alt, the like)
unsigned char key[8]; // Currently pressed keys, max 8 at once
} __attribute((packed));
I'm not sure why but the source code defines the ID for keyboard as 2 instead (lines 139-141):
// These numbers must also be used in the HID descriptor binary file
#define REPORTID_MOUSE 1
#define REPORTID_KEYBD 2
Looking back at the keyboard struct, we see that the 3rd byte represents the modifier keys, then the rest of the bytes represents the keys pressed.
Scrolling down the source code to lines 826-956, we see that we have the modifier keys and values that represents the keys pressed:
// *** "Modifier" key events
case KEY_RIGHTMETA:
u <<= 1;
case KEY_RIGHTALT:
u <<= 1;
case KEY_RIGHTSHIFT:
u <<= 1;
case KEY_RIGHTCTRL:
u <<= 1;
case KEY_LEFTMETA:
u <<= 1;
case KEY_LEFTALT:
u <<= 1;
case KEY_LEFTSHIFT:
u <<= 1;
case KEY_LEFTCTRL:
evkeyb->btcode = 0xA1;
evkeyb->rep_id = REPORTID_KEYBD;
memcpy ( evkeyb->key, pressedkey, 8 );
modifierkeys &= ( 0xff - u );
if ( inevent->value >= 1 )
{
modifierkeys |= u;
}
evkeyb->modify = modifierkeys;
j = send ( sockdesc, evkeyb,
sizeof(struct hidrep_keyb_t),
MSG_NOSIGNAL );
if ( 1 > j )
{
return -1;
}
break;
// *** Regular key events
case KEY_KPDOT: ++u; // Keypad Dot ~ 99
case KEY_KP0: ++u; // code 98...
case KEY_KP9: ++u; // countdown...
case KEY_KP8: ++u;
case KEY_KP7: ++u;
case KEY_KP6: ++u;
case KEY_KP5: ++u;
case KEY_KP4: ++u;
case KEY_KP3: ++u;
case KEY_KP2: ++u;
case KEY_KP1: ++u;
case KEY_KPENTER: ++u;
case KEY_KPPLUS: ++u;
case KEY_KPMINUS: ++u;
case KEY_KPASTERISK: ++u;
case KEY_KPSLASH: ++u;
case KEY_NUMLOCK: ++u;
case KEY_UP: ++u;
case KEY_DOWN: ++u;
case KEY_LEFT: ++u;
case KEY_RIGHT: ++u;
case KEY_PAGEDOWN: ++u;
case KEY_END: ++u;
case KEY_DELETE: ++u;
case KEY_PAGEUP: ++u;
case KEY_HOME: ++u;
case KEY_INSERT: ++u;
++u; //[Pause] key
// - checked separately
case KEY_SCROLLLOCK: ++u;
case KEY_SYSRQ: ++u; //[printscr]
case KEY_F12: ++u; //F12=> code 69
case KEY_F11: ++u;
case KEY_F10: ++u;
case KEY_F9: ++u;
case KEY_F8: ++u;
case KEY_F7: ++u;
case KEY_F6: ++u;
case KEY_F5: ++u;
case KEY_F4: ++u;
case KEY_F3: ++u;
case KEY_F2: ++u;
case KEY_F1: ++u;
case KEY_CAPSLOCK: ++u;
case KEY_SLASH: ++u;
case KEY_DOT: ++u;
case KEY_COMMA: ++u;
case KEY_GRAVE: ++u;
case KEY_APOSTROPHE: ++u;
case KEY_SEMICOLON: ++u;
case KEY_102ND: ++u;
case KEY_BACKSLASH: ++u;
case KEY_RIGHTBRACE: ++u;
case KEY_LEFTBRACE: ++u;
case KEY_EQUAL: ++u;
case KEY_MINUS: ++u;
case KEY_SPACE: ++u;
case KEY_TAB: ++u;
case KEY_BACKSPACE: ++u;
case KEY_ESC: ++u;
case KEY_ENTER: ++u; //Return=> code 40
case KEY_0: ++u;
case KEY_9: ++u;
case KEY_8: ++u;
case KEY_7: ++u;
case KEY_6: ++u;
case KEY_5: ++u;
case KEY_4: ++u;
case KEY_3: ++u;
case KEY_2: ++u;
case KEY_1: ++u;
case KEY_Z: ++u;
case KEY_Y: ++u;
case KEY_X: ++u;
case KEY_W: ++u;
case KEY_V: ++u;
case KEY_U: ++u;
case KEY_T: ++u;
case KEY_S: ++u;
case KEY_R: ++u;
case KEY_Q: ++u;
case KEY_P: ++u;
case KEY_O: ++u;
case KEY_N: ++u;
case KEY_M: ++u;
case KEY_L: ++u;
case KEY_K: ++u;
case KEY_J: ++u;
case KEY_I: ++u;
case KEY_H: ++u;
case KEY_G: ++u;
case KEY_F: ++u;
case KEY_E: ++u;
case KEY_D: ++u;
case KEY_C: ++u;
case KEY_B: ++u;
case KEY_A: u +=3; // A => 4
We can automate this using a script to make things easier for us. We can do so using the pyshark library in Python.
Firstly, we need to extract the payload from the L2CAP packets.
extract.py
import pysharkf = pyshark.FileCapture('traffic.pcap', display_filter="btl2cap")keys = []for p in f: data = p['btl2cap'].payload.split(":")print(data)
We can then filter the packets where only our 2nd byte is equal to 01, and then check if the 3rd byte is 02 as having the third byte as 02 means a LEFT SHIFT is pressed (according to the source code).
We then see that the other bytes are all null except for the 4th byte, so we can guess that that is our value representing the key pressed.
So we can grab the 4th byte, check what character it represents (A=4, B=5, C=6 and so on...), and substitute it for the corresponding character, and check the 2nd byte to see if the LEFT SHIFT key is pressed.