Analyse
En effectuant une pré-analyse sur le fichier “input”, un élément me saute aux yeux :
$ xxd input | head -n20
00000000: 5f22 765e 0000 0000 0624 0b00 0000 0000 _"v^.....$......
00000010: 0400 0400 1c00 0000 5f22 765e 0000 0000 ........_"v^....
00000020: 0624 0b00 0000 0000 0100 1c00 0000 0000 .$..............
00000030: 5f22 765e 0000 0000 0624 0b00 0000 0000 _"v^.....$......
00000040: 0000 0000 0000 0000 6622 765e 0000 0000 ........f"v^....
00000050: 1ff3 0e00 0000 0000 0400 0400 1700 0000 ................
00000060: 6622 765e 0000 0000 1ff3 0e00 0000 0000 f"v^............
00000070: 0100 1700 0100 0000 6622 765e 0000 0000 ........f"v^....
00000080: 1ff3 0e00 0000 0000 0000 0000 0000 0000 ................
00000090: 6722 765e 0000 0000 5089 0000 0000 0000 g"v^....P.......
000000a0: 0400 0400 2000 0000 6722 765e 0000 0000 .... ...g"v^....
000000b0: 5089 0000 0000 0000 0100 2000 0100 0000 P......... .....
000000c0: 6722 765e 0000 0000 5089 0000 0000 0000 g"v^....P.......
000000d0: 0000 0000 0000 0000 6722 765e 0000 0000 ........g"v^....
000000e0: fcc0 0000 0000 0000 0400 0400 1700 0000 ................
000000f0: 6722 765e 0000 0000 fcc0 0000 0000 0000 g"v^............
00000100: 0100 1700 0000 0000 6722 765e 0000 0000 ........g"v^....
00000110: fcc0 0000 0000 0000 0000 0000 0000 0000 ................
00000120: 6722 765e 0000 0000 b705 0200 0000 0000 g"v^............
00000130: 0400 0400 2000 0000 6722 765e 0000 0000 .... ...g"v^....
(..)
On y voit un pattern de données.
Ma première approche est de me dire que 0x5f
et 0x22
sont des touches (keycodes).
En prenant en compte la liste des input-event du noyau Linux : https://github.com/torvalds/linux/blob/master/include/uapi/linux/input-event-codes.h Je ne vois pas vraiment de correspondance, j’écarte en premier lieu cette solution.
J’étudie plus en profondeur le pattern et je me dis que le 5f22
apparait souvent, puis disparait pour laisser sa place à un autre pattern.
Pour le coup, je me dis que cela ne peut être qu’une structure de données, mais laquelle ?
Je tourne autour du pot, car je ne vois quel programme aurait pu générer ce genre d’input. Jusqu’au moment où mes yeux reviennent sur le texte de l’auteur : " mais aucun programme ne semble avoir été installé " Chaque mot étant pesé, je me tourne vers une autre approche, si aucun programme n’a été installé, c’est qu’il est de base dans le système. Et qu’est-ce qui est de base dans un système ? le noyau ! Si aucun programme n’a été utile pour générer ce genre d’output, c’est qu’il doit provenir d’un périphérique.
Je me tourne alors vers /dev/input
, je scrute.
Et tente (après quelques essais sur d’autres devices dans /dev/input) un petit :
$ sudo xxd /dev/input/by-path/platform-i8042-serio-0-event-kbd
00000000: abd0 5267 0000 0000 6896 0600 0000 0000 ..Rg....h.......
00000010: 0400 0400 1c00 0000 abd0 5267 0000 0000 ..........Rg....
00000020: 6896 0600 0000 0000 0100 1c00 0000 0000 h...............
00000030: abd0 5267 0000 0000 6896 0600 0000 0000 ..Rg....h.......
00000040: 0000 0000 0000 0000 add0 5267 0000 0000 ..........Rg....
00000050: f628 0500 0000 0000 0400 0400 1c00 0000 .(..............
00000060: add0 5267 0000 0000 f628 0500 0000 0000 ..Rg.....(......
00000070: 0100 1c00 0100 0000 add0 5267 0000 0000 ..........Rg....
00000080: f628 0500 0000 0000 0000 0000 0000 0000 .(..............
00000090: add0 5267 0000 0000 829e 0600 0000 0000 ..Rg............
000000a0: 0400 0400 1c00 0000 add0 5267 0000 0000 ..........Rg....
000000b0: 829e 0600 0000 0000 0100 1c00 0000 0000 ................
000000c0: add0 5267 0000 0000 829e 0600 0000 0000 ..Rg............
000000d0: 0000 0000 0000 0000 aed0 5267 0000 0000 ..........Rg....
000000e0: 3f53 0900 0000 0000 0400 0400 1c00 0000 ?S..............
000000f0: aed0 5267 0000 0000 3f53 0900 0000 0000 ..Rg....?S......
00000100: 0100 1c00 0100 0000 aed0 5267 0000 0000 ..........Rg....
00000110: 3f53 0900 0000 0000 0000 0000 0000 0000 ?S..............
La structure de données est étrangement familière ! Sauf les débuts… et là, j’ai compris, les premiers octets sont un timestamp. Confirmation en continuant de jouer avec le clavier, les valeurs semblent s’incrémenter.
Je cherche alors à déterminer la structure de données que je trouve dans une obscure doc kernel :
struct input_event {
struct timeval time (16 octets)
unsigned short type; ( 2 octets)
unsigned short code; ( 2 octets)
unsigned int value; ( 4 octets)
};
Effectivement, si j’analyse les 24 octets, je retrouve des données qui sont parfaitement alignées.
Je cherche une solution pour pouvoir manipuler une structure de données de ce type, je me tourne vers libinput, evtest, entre autres, mais aucun ne me convient.
Je me tourne alors vers une solution : coder mon propre parser input (en Python) :
with open(sys.argv[1], "rb") as file:
while True:
time = file.read(16)
if not time:
break
event = file.read(2)
code = file.read(2)
value = file.read(4)
print(f"{time.hex()} - {event.hex()} - {code.hex()} - {value.hex()}")
Et si je lance l’analyse :
$ ./analyze.py ./input | head -n10
5f22765e0000000006240b0000000000 - 0400 - 0400 - 1c000000
5f22765e0000000006240b0000000000 - 0100 - 1c00 - 00000000
5f22765e0000000006240b0000000000 - 0000 - 0000 - 00000000
6622765e000000001ff30e0000000000 - 0400 - 0400 - 17000000
6622765e000000001ff30e0000000000 - 0100 - 1700 - 01000000
6622765e000000001ff30e0000000000 - 0000 - 0000 - 00000000
6722765e000000005089000000000000 - 0400 - 0400 - 20000000
6722765e000000005089000000000000 - 0100 - 2000 - 01000000
6722765e000000005089000000000000 - 0000 - 0000 - 00000000
6722765e00000000fcc0000000000000 - 0400 - 0400 - 17000000
(...)
Quand je prends mon tableau des input-events-codes (https://github.com/torvalds/linux/blob/master/include/uapi/linux/input-event-codes.h), j’observe que les données… ne correspondent pas. Petit grattage de tête… jusqu’à comprendre que la structure de données est encodée en little-endian. Petite modification de notre programme pour prendre en compte :
with open(sys.argv[1], "rb") as file:
while True:
time = file.read(16)
if not time:
break
event = int.from_bytes(file.read(2), "little")
code = int.from_bytes(file.read(2), "little")
value = int.from_bytes(file.read(4), "little")
print(f"{time.hex()} - {event:>2d} - {code:>2d} - {value}")
$ ./analyze.py ./input | head -n20
5f22765e0000000006240b0000000000 - 4 - 4 - 28
5f22765e0000000006240b0000000000 - 1 - 28 - 0
5f22765e0000000006240b0000000000 - 0 - 0 - 0
6622765e000000001ff30e0000000000 - 4 - 4 - 23
6622765e000000001ff30e0000000000 - 1 - 23 - 1
6622765e000000001ff30e0000000000 - 0 - 0 - 0
6722765e000000005089000000000000 - 4 - 4 - 32
6722765e000000005089000000000000 - 1 - 32 - 1
6722765e000000005089000000000000 - 0 - 0 - 0
6722765e00000000fcc0000000000000 - 4 - 4 - 23
6722765e00000000fcc0000000000000 - 1 - 23 - 0
6722765e00000000fcc0000000000000 - 0 - 0 - 0
6722765e00000000b705020000000000 - 4 - 4 - 32
6722765e00000000b705020000000000 - 1 - 32 - 0
6722765e00000000b705020000000000 - 0 - 0 - 0
6722765e00000000abd00d0000000000 - 4 - 4 - 28
6722765e00000000abd00d0000000000 - 1 - 28 - 1
6722765e00000000abd00d0000000000 - 0 - 0 - 0
6822765e0000000073bf000000000000 - 4 - 4 - 28
6822765e0000000073bf000000000000 - 1 - 28 - 0
De là, on voit que nos events (colonne 2) correspondent et nos codes (colonne 3) aussi. Vu le flot de données, on ne va pas analyser un par un les différents codes, on va réimplémenter rapidement une table de correspondance :
# les events
EV_KEYS = {
0: "EV_SYN",
1: "EV_KEY",
2: "EV_REL",
3: "EV_ABS",
4: "EV_MSC",
5: "EV_SW",
}
# seulement EV_KEY et seulement ceux utilisé dans "input"
CODES = {
0 : "KEY_RESERVED",
1 : "KEY_ESC",
2 : "KEY_1",
3 : "KEY_2",
4 : "KEY_3",
5 : "KEY_4",
6 : "KEY_5",
7 : "KEY_6",
8 : "KEY_7",
9 : "KEY_8",
10 : "KEY_9",
11 : "KEY_0",
12 : "KEY_MINUS",
13 : "KEY_EQUAL",
14 : "KEY_BACKSPACE",
15 : "KEY_TAB",
16 : "KEY_Q",
17 : "KEY_W",
18 : "KEY_E",
19 : "KEY_R",
20 : "KEY_T",
21 : "KEY_Y",
22 : "KEY_U",
23 : "KEY_I",
24 : "KEY_O",
25 : "KEY_P",
26 : "KEY_LEFTBRACE",
27 : "KEY_RIGHTBRACE",
28 : "KEY_ENTER",
29 : "KEY_LEFTCTRL",
30 : "KEY_A",
31 : "KEY_S",
32 : "KEY_D",
33 : "KEY_F",
34 : "KEY_G",
35 : "KEY_H",
36 : "KEY_J",
37 : "KEY_K",
38 : "KEY_L",
39 : "KEY_SEMICOLON",
40 : "KEY_APOSTROPHE",
41 : "KEY_GRAVE",
42 : "KEY_LEFTSHIFT",
43 : "KEY_BACKSLASH",
44 : "KEY_Z",
45 : "KEY_X",
46 : "KEY_C",
47 : "KEY_V",
48 : "KEY_B",
49 : "KEY_N",
50 : "KEY_M",
51 : "KEY_COMMA",
52 : "KEY_DOT",
53 : "KEY_SLASH",
54 : "KEY_RIGHTSHIFT",
55 : "KEY_KPASTERISK",
56 : "KEY_LEFTALT",
57 : "KEY_SPACE",
58 : "KEY_CAPSLOCK",
59 : "KEY_F1",
# (...)
74 : "KEY_KPMINUS",
# (...)
100 : "KEY_RIGHTALT",
# (...)
108 : "KEY_DOWN",
# (...)
247 : "KEY_RFKILL",
248 : "KEY_MICMUTE",
}
with open(sys.argv[1], "rb") as file:
while True:
time = file.read(16)
if not time:
break
event = int.from_bytes(file.read(2), "little")
code = int.from_bytes(file.read(2), "little")
value = int.from_bytes(file.read(4), "little")
# on filtre que sur EV_KEY car on n'a que la liste des "KEY" dans CODES
if event == 1 and value == 1:
print(f"{time.hex()} - {EV_KEYS.get(event)}({event}) - {CODES.get(code, str(code)):14s} - {value}")
Et en démarrant le programme :
$ ./analyze.py ./input | head -n25
6622765e000000001ff30e0000000000 - EV_KEY(1) - KEY_I - 1
6722765e000000005089000000000000 - EV_KEY(1) - KEY_D - 1
6722765e00000000abd00d0000000000 - EV_KEY(1) - KEY_ENTER - 1
6b22765e00000000be9c040000000000 - EV_KEY(1) - KEY_G - 1
6b22765e000000009c56060000000000 - EV_KEY(1) - KEY_R - 1
6b22765e000000000ba4080000000000 - EV_KEY(1) - KEY_O - 1
6d22765e000000005ab7040000000000 - EV_KEY(1) - KEY_U - 1
6e22765e00000000b5b5030000000000 - EV_KEY(1) - KEY_P - 1
6e22765e0000000020df040000000000 - EV_KEY(1) - KEY_S - 1
6e22765e0000000000dd0a0000000000 - EV_KEY(1) - KEY_ENTER - 1
7122765e0000000027740c0000000000 - EV_KEY(1) - KEY_P - 1
7222765e000000004120010000000000 - EV_KEY(1) - KEY_Z - 1
7222765e00000000608a030000000000 - EV_KEY(1) - KEY_D - 1
7222765e000000009b530c0000000000 - EV_KEY(1) - KEY_ENTER - 1
7322765e000000005aff0e0000000000 - EV_KEY(1) - KEY_Z - 1
7422765e00000000ee08030000000000 - EV_KEY(1) - KEY_ENTER - 1
7c22765e00000000f0e3040000000000 - EV_KEY(1) - KEY_P - 1
7c22765e00000000b7b0080000000000 - EV_KEY(1) - KEY_I - 1
7c22765e0000000039710c0000000000 - EV_KEY(1) - KEY_N - 1
7d22765e0000000051b1000000000000 - EV_KEY(1) - KEY_G - 1
7d22765e00000000ec44020000000000 - EV_KEY(1) - KEY_SPACE - 1
7d22765e0000000008f50d0000000000 - EV_KEY(1) - KEY_RIGHTSHIFT - 1
7e22765e00000000b40e060000000000 - EV_KEY(1) - KEY_2 - 1
7f22765e00000000bd51090000000000 - EV_KEY(1) - KEY_9 - 1
7f22765e00000000d4e70a0000000000 - EV_KEY(1) - KEY_8 - 1
On vient d’ouvrir le premier coffre-fort :-) Si on regarde et interprète le code (attention clavier US) :
$ id
$ groups
$ pwd
$ ping 298.125.256.42
$ sudo apt update
etc...
Analyse du parsing event
Dans le flot d’informations inutiles, je constate que l’auteur tape GPG avec des arguments puis enter :
7123765e000000009ed3020000000000 - EV_KEY(1) - KEY_G - 1
7123765e00000000b5890c0000000000 - EV_KEY(1) - KEY_P - 1
7323765e0000000095c00c0000000000 - EV_KEY(1) - KEY_G - 1
7923765e000000004ac40d0000000000 - EV_KEY(1) - KEY_SPACE - 1
7b23765e000000006158030000000000 - EV_KEY(1) - KEY_KPMINUS - 1
7b23765e00000000b66e080000000000 - EV_KEY(1) - KEY_KPMINUS - 1
7d23765e0000000062a0020000000000 - EV_KEY(1) - KEY_O - 1
7d23765e0000000077730c0000000000 - EV_KEY(1) - KEY_U - 1
7e23765e00000000480c010000000000 - EV_KEY(1) - KEY_T - 1
7f23765e00000000d6f7070000000000 - EV_KEY(1) - KEY_P - 1
8023765e000000008d8f010000000000 - EV_KEY(1) - KEY_U - 1
8023765e000000002746090000000000 - EV_KEY(1) - KEY_T - 1
8023765e00000000d6b50a0000000000 - EV_KEY(1) - KEY_SPACE - 1
8123765e00000000fa23060000000000 - EV_KEY(1) - KEY_F - 1
8123765e00000000942b080000000000 - EV_KEY(1) - KEY_L - 1
8123765e0000000061360a0000000000 - EV_KEY(1) - KEY_Q - 1
8223765e000000009464030000000000 - EV_KEY(1) - KEY_G - 1
8323765e0000000097b0040000000000 - EV_KEY(1) - KEY_SPACE - 1
8523765e000000008090080000000000 - EV_KEY(1) - KEY_KPMINUS - 1
8523765e00000000c9fa0a0000000000 - EV_KEY(1) - KEY_KPMINUS - 1
8723765e000000001882040000000000 - EV_KEY(1) - KEY_D - 1
8723765e00000000d824060000000000 - EV_KEY(1) - KEY_E - 1
8723765e00000000b34c0a0000000000 - EV_KEY(1) - KEY_C - 1
8723765e0000000073fc0b0000000000 - EV_KEY(1) - KEY_R - 1
8823765e0000000062d4010000000000 - EV_KEY(1) - KEY_Y - 1
8823765e00000000a61d0b0000000000 - EV_KEY(1) - KEY_P - 1
8823765e0000000001860d0000000000 - EV_KEY(1) - KEY_T - 1
8a23765e00000000d9ef040000000000 - EV_KEY(1) - KEY_SPACE - 1
9523765e000000009853050000000000 - EV_KEY(1) - KEY_F - 1
9623765e00000000590e010000000000 - EV_KEY(1) - KEY_L - 1
9623765e000000003bd6020000000000 - EV_KEY(1) - KEY_Q - 1
9723765e00000000bca7000000000000 - EV_KEY(1) - KEY_G - 1
9723765e000000007633050000000000 - EV_KEY(1) - KEY_RIGHTSHIFT - 1
9723765e00000000cbfa070000000000 - EV_KEY(1) - KEY_COMMA - 1
9b23765e0000000082dc080000000000 - EV_KEY(1) - KEY_G - 1
9b23765e00000000323c0c0000000000 - EV_KEY(1) - KEY_P - 1
9b23765e0000000079840d0000000000 - EV_KEY(1) - KEY_G - 1
a923765e00000000ff89030000000000 - EV_KEY(1) - KEY_ENTER - 1
ad23765e00000000049c070000000000 - EV_KEY(1) - KEY_CAPSLOCK - 1
ad23765e00000000b60d0d0000000000 - EV_KEY(1) - KEY_D - 1
ad23765e00000000dc410e0000000000 - EV_KEY(1) - KEY_CAPSLOCK - 1
ae23765e000000006c5f020000000000 - EV_KEY(1) - KEY_E - 1
ae23765e0000000018e6050000000000 - EV_KEY(1) - KEY_S - 1
af23765e000000009ecb010000000000 - EV_KEY(1) - KEY_T - 1
af23765e000000007e760e0000000000 - EV_KEY(1) - KEY_I - 1
b023765e00000000610b020000000000 - EV_KEY(1) - KEY_N - 1
b023765e00000000eb93030000000000 - EV_KEY(1) - KEY_Q - 1
b023765e000000002e7f070000000000 - EV_KEY(1) - KEY_R - 1
b023765e00000000ad6a090000000000 - EV_KEY(1) - KEY_I - 1
b023765e00000000dd6b090000000000 - EV_KEY(1) - KEY_O - 1
b023765e000000006cb40b0000000000 - EV_KEY(1) - KEY_T - 1
b223765e00000000a4b5070000000000 - EV_KEY(1) - KEY_BACKSPACE - 1
b223765e0000000085410c0000000000 - EV_KEY(1) - KEY_BACKSPACE - 1
b323765e000000007f58030000000000 - EV_KEY(1) - KEY_BACKSPACE - 1
b323765e0000000037530b0000000000 - EV_KEY(1) - KEY_BACKSPACE - 1
b523765e000000009ebf020000000000 - EV_KEY(1) - KEY_T - 1
b523765e000000009894040000000000 - EV_KEY(1) - KEY_I - 1
b523765e000000006623080000000000 - EV_KEY(1) - KEY_O - 1
b523765e000000008ea60b0000000000 - EV_KEY(1) - KEY_N - 1
b723765e000000002b38030000000000 - EV_KEY(1) - KEY_8 - 1
ba23765e0000000061940b0000000000 - EV_KEY(1) - KEY_CAPSLOCK - 1
ba23765e00000000737e0e0000000000 - EV_KEY(1) - KEY_Q - 1
bb23765e00000000b484000000000000 - EV_KEY(1) - KEY_CAPSLOCK - 1
bb23765e000000001147020000000000 - EV_KEY(1) - KEY_U - 1
bb23765e000000001293040000000000 - EV_KEY(1) - KEY_T - 1
bb23765e00000000e0dc060000000000 - EV_KEY(1) - KEY_I - 1
bb23765e000000003365080000000000 - EV_KEY(1) - KEY_R - 1
bb23765e000000008a600d0000000000 - EV_KEY(1) - KEY_E - 1
bd23765e00000000f51f0a0000000000 - EV_KEY(1) - KEY_BACKSPACE - 1
bd23765e000000002f760d0000000000 - EV_KEY(1) - KEY_BACKSPACE - 1
be23765e000000008746010000000000 - EV_KEY(1) - KEY_BACKSPACE - 1
be23765e0000000034150f0000000000 - EV_KEY(1) - KEY_R - 1
bf23765e00000000a009020000000000 - EV_KEY(1) - KEY_I - 1
bf23765e0000000078ce030000000000 - EV_KEY(1) - KEY_C - 1
bf23765e000000003e5b060000000000 - EV_KEY(1) - KEY_H - 1
bf23765e00000000eda4080000000000 - EV_KEY(1) - KEY_E - 1
c223765e000000003cc7010000000000 - EV_KEY(1) - KEY_8 - 1
c223765e00000000a6dc0e0000000000 - EV_KEY(1) - KEY_RIGHTALT - 1
c323765e000000002df4030000000000 - EV_KEY(1) - KEY_3 - 1
c323765e00000000271e0a0000000000 - EV_KEY(1) - KEY_CAPSLOCK - 1
c423765e000000001fb6000000000000 - EV_KEY(1) - KEY_T - 1
c423765e00000000b1e6040000000000 - EV_KEY(1) - KEY_E - 1
c523765e00000000fdca060000000000 - EV_KEY(1) - KEY_Q - 1
c523765e0000000041210c0000000000 - EV_KEY(1) - KEY_SEMICOLON - 1
c623765e000000003c6e090000000000 - EV_KEY(1) - KEY_F - 1
c623765e00000000aced0a0000000000 - EV_KEY(1) - KEY_R - 1
c923765e000000008e12080000000000 - EV_KEY(1) - KEY_CAPSLOCK - 1
d623765e00000000a3cb0c0000000000 - EV_KEY(1) - KEY_ENTER - 1
Dans ce flot de touche, on constate une chaîne de caractère qui sortent de l’ordinaire.
Si on interprète les CAPSLOCK
, les RIGHTALT
et les KEY_*
au format US, on peut retrouver le mot de passe :
Destination_Autriche_#TEAMFR
On lance la commande GPG pour décrypter :
gpg -d flag.gpg
> Destination_Autriche_#TEAMFR
gpg: AES256.CFB encrypted data
gpg: encrypted with 1 passphrase
FCSC{0bec21052ae86baf149eb97ce52de0fec1d6b8e1ed827fe026f6197be07419c3}
Et voilà !