From 5189bebff8c0f9ec36b76224baf19fcb780044c1 Mon Sep 17 00:00:00 2001 From: mli Date: Sun, 26 Jan 2020 23:35:29 +0100 Subject: [PATCH] Add hxp writeup --- writeups/mli/hxp-36c3-ctf.md | 210 ++++++++++++++++++++++++ writeups/mli/hxp-36c3-ctf/exploit.py | 182 ++++++++++++++++++++ writeups/mli/hxp-36c3-ctf/flagchars.txt | 40 +++++ 3 files changed, 432 insertions(+) create mode 100644 writeups/mli/hxp-36c3-ctf.md create mode 100644 writeups/mli/hxp-36c3-ctf/exploit.py create mode 100644 writeups/mli/hxp-36c3-ctf/flagchars.txt diff --git a/writeups/mli/hxp-36c3-ctf.md b/writeups/mli/hxp-36c3-ctf.md new file mode 100644 index 0000000..0fb207b --- /dev/null +++ b/writeups/mli/hxp-36c3-ctf.md @@ -0,0 +1,210 @@ +# 36c3 hxp ctf + +## Time spent + +In total I spent 7+9+1(challenges)+3+4+11 hours on this ctf __Total 35 hours__ + +3 hours writing this writeup + +4 hours reading challenge writeups after the ctf was over + +for preparation: a full workday reading and experimenting with writeups from last years 35c3 ctf + about 3 hours for the junior 35c3 ctf (ethereum challenges "entrance" and "future") = 11 hours + +## peerreviewed (ZAJ CRY BBY) + +Description: + +Because AES is so hard to understand, we went looking for something that is a little easier to follow. This paper (DOI: 10.1109/CCNC.2007.64, PDF) introduces an encrypted protocol that “prevents an attacker from recovering the transmitted information through operations on the encrypted vectors exchanged during the transmission procedure” - in fact, the paper even assures us that “the only identified way one can compromise the protocol security is by applying brute force attacks”. Since this is a peer-reviewed paper (by subject-matter experts, no less) we can be sure that our data is safe! My colleagues are still a bit concerned, though, so I am running this little test in order to prove that the protocol is secure. + +Download: + +* peerreviewed-320d68ba23764d17.tar.xz + * crypto.sage + * intercepted.txt + +### Time spent + +For this challenge, I spent 6 hours during the ctf and 1 hour afterwards reading the writeup. + +### Overview + +* I first downloaded/installed sagemath (+pycryptodome) +* We apparently have 3 instances of message communication inside of intercepted.txt + * block 1 + * `A -> B: yA = (5.8537179772742871378006829317359804640034149162093776176771e75, 2.0260990893806965307943860314007373888732002921518840941414e76)` + * `B -> A: yB = (4.0652782673020986683538918237010543408982543019306057179496e95, -5.0285426513783822097670201376390415563936061484095492229894e94)` + * `A -> B: yC = (4.3083595977562861674637990303350994431503203162436044206961e76, -3.1928247205608247346546681530367209122491766171698701927922e76)` + * block 2 + * `A -> B: yA = (3.9886651421460868244883042163468164077791762065632163641999e76, -1.2487387183806776412156661578750877275468860853692530589115e77)` + * `B -> A: yB = (2.5644531171906756537679808003501733255703504126650573479133e95, -1.6570541229598112699074962433815055733294177769576178678896e96)` + * `A -> B: yC = (4.5893614625576224795913782492661186902913334535507951850089e76, 6.8611881934867127810471276550294069221078140676008042683634e76)` + * block 3 + * `A -> B: yA = (-6.5089227887140404010136240391818701730482514213358150433451e75, 5.5620733870042242270647223203879530885369669489378834804472e76)` + * `B -> A: yB = (1.0017374772520318947002755790705886607122203555146923369286e95, 1.2876407911288161811207561236370324382394428286338064237471e96)` + * `A -> B: yC = (1.1418280384822587553925695577555385627605037374102719431688e77, -1.9966984339634055590175475802847306340483261373340080568330e76)` +* The intercepted.txt was probably generated by the "Self-test"-lines of `crypto.sage` inside `if __name__ == '__main__'` + * The plaintext message is split into blocks of 192 bytes +* The output blocks look independed to each other (regarding encryption) +* My goal was to make an equation to see what I need in order to recompute the plaintext + +according to the code in the test procedure of the original crypto.sage sourcecode + +key 1: `O1, Oc1, A1, Ac1` + +key 2: `O2, Oc2, A2, Ac2` + +transmission 1->2: `b * O1 * A1` = yA +transmission 2->1: `yA * O2 * A2` = yB +transmission 1->2: `yB * Oc1 * Ac1` = yC + +So, yA, yB, yC are public. We can also compute `O2 * A2` by `yB/yA` and `Oc1 * Ac1` by `yC/yB`. +I wrote this on some paper and tried to make sense of it for some time. I looked at the generate_key() function and thought how this could work in a matrix context. + +I skimmed through the paper to look whether there was some information about a possible exploit when all transmission content is listened to, but they just wrote that you cannot break this crypto except using "brute-force". Hmm, must be something else then (which the peerreviewers ;) did not get - matches the description at least). + +### Solution/Exploit + +I did not manage to solve the challenge before end of ctf. + +There are two writeups ( and ), the second one explains the problem pretty well. + +The absolute value b can be computed by `yC*yA / yB` (because `Oc1*Ac1` are basically inverses of `O1*A1` which all 4 get canceled out so that `yC*yA / yB = yB*b / yB = b`). b itself consists of the plaintext. The flag was `hxp{p33r_r3v13w3d_m4y_n0t_b3_1337_r3v13w3d}`. + +## catch-the-flag (MSC) + +Description: "Just catch me." + +Download: + +* catch the flag-9573fd8e2bb91485.tar.xz + * client.py + * Dockerfile + * flag_char.py + * game.py + * world_generator.py + * ... + +### Time spent + +I invested 9 hours into this challenge, most of it walking around manually and also changing my script each time after I found out a new aspect of the game (which I should have looked at beforehand yes, the `game.py` and other files were here for a reason. + +### Overview + +This challenge was about a console-based game, I imagined that it was similar to the old Nokia game Snake where the flag is the snake and I have to walk around and read the flag. The problem is that there are holes (pits) in the ground and when I fall into one I have to start over. + +* The game will print only 'i' when we stand on nothing special. +* 'iw' means that we are standing against a wall +* 'breeze' means there is a pit nearby +* 'smell' means there is the flag nearby + +I decided that I track my current position and paint a map from the responses I get because the pits don't seem to change (while the flag does run around the map). I wanted to first run around manually to find out as much as possible about the game. + +I kept track of the map using an array and printed it on each move): + +```json +[['i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'B', 'i', 'B', 'i', 'B', 'B', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', + ['i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'B', 'i', 'i', 'i', 'i', 'i', 'B', ' ', 'B', ' ', 'B', ' ', ' ', 'B', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', + ['i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'B', ' ', 'B', 'i', 'i', 'i', 'B', 'd', 'B', 'i', 'B', 'B', ' ', 'B', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', + ['i', 'B', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'B', 'i', 'i', 'i', 'i', 'i', 'B', 'i', 'i', 'i', 'i', 'B', 'i', 'i', 'i', 'B', 'i', 'i', 'i', 'i', 'i', 'i', 'B', + ['B', 'd', 'B', 'i', 'i', 'i', 'i', 'i', 'B', 'i', 'i', 'i', 'i', 'i', 'i', 'B', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'B', 'd', 'B', 'i', 'i', 'i', 'i', 'B', ' ', + ['i', 'B', 'd', 'B', 'i', 'i', 'i', 'B', ' ', 'B', 'i', 'i', 'B', 'i', 'B', ' ', 'B', 'i', 'i', 'B', 'i', 'i', 'i', 'B', 'i', 'i', 'i', 'i', 'i', 'i', 'B', ' ', + ['B', ' ', 'B', 'i', 'i', 'i', 'i', 'B', ' ', 'i', 'i', 'B', 'd', 'B', 'd', 'B', 'i', 'i', 'B', ' ', 'B', 'i', 'B', 'd', 'B', 'B', 'i', 'i', 'i', 'B', 'd', ' ', + ['i', 'B', 'd', 'B', 'i', 'i', 'B', ' ', 'B', 'i', 'i', 'B', 'd', ' ', 'B', 'i', 'i', 'i', ' ', 'B', 'i', 'i', 'i', 'B', 'B', 'i', 'i', 'i', 'B', ' ', ' ', 'B', + ['i', 'i', 'B', 'i', 'i', 'i', 'i', 'B', 'i', 'i', 'i', 'i', 'B', 'B', 'i', 'i', 'i', 'i', 'i', 'B', 'i', 'i', 'B', 'd', 'i', 'i', 'i', 'i', 'i', 'B', 'B', 'i', ['i', 'i', 'i', 'i', 'i', 'i', 'B', 'd', 'B', 'B', 'i', 'B', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'B', 'i', 'i', 'B', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', + ['i', 'i', 'i', 'i', 'i', 'B', 'd', 'B', 'B', ' ', 'B', 'i', 'B', 'i', 'i', 'i', 'i', 'i', 'i', 'B', ' ', 'B', 'i', 'B', 'i', 'i', 'i', 'i', 'i', 'i', 'B', 'i', + ['i', 'i', 'i', 'i', 'i', 'i', 'B', 'i', 'i', 'B', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'B', 'd', 'B', ' ', 'B', 'B', 'i', 'B', 'i', 'B', ' ', 'B', + ['i', 'i', 'i', 'i', 'i', 'B', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'B', 'i', 'B', 'B', 'd', 'B', ' ', 'B', ' ', 'B', 'i', + ['i', 'i', 'i', 'i', 'B', 'd', 'B', 'i', 'i', 'i', 'i', 'B', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'B', ' ', ' ', ' ', ' ', 'B', 'i', + ['i', 'i', 'i', 'i', 'i', 'B', 'i', 'i', 'i', 'i', 'B', 'd', 'B', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'B', ' ', ' ', ' ', 'B', 'i', + ['i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'B', 'i', 'i', 'B', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'B', 'd', 'B', 'i', 'i', + ['i', 'i', 'B', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'B', 'd', 'B', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'B', 'B', ' ', 'B', 'i', 'i', 'B', ['i', 'B', 'd', 'B', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'B', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'B', 'i', 'B', ' ', ' ', ' ', ' ', 'B', 'B', ' ', + ['i', 'i', 'B', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'B', 'i', 'i', 'B', 'i', ' ', ' ', ' ', ' ', ' ', 'B', + ['i', 'i', 'B', 'i', 'B', 'B', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'B', 'i', 'B', 'i', 'B', '?O, 'i', 'i', 'smell', 'i', 'i', 'i', 'B', ' + ['i', 'B', 'd', 'B', ' ', ' ', 'B', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'B', 'i', 'd', ' ', ' ', ' ', ' ', 'i', 'i', 'i', 'i', '4', 'smell', 'i', 'i', ' + ['B', ' ', 'B', ' ', ' ', ' ', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', ' ', ' ', 'B', 'i', ' ', ' ', 'i', 'i', 'i', 'i', 'i', ' ', ' ', ' ', + ['i', 'B', 'i', 'B', 'd', ' ', 'i', 'B', 'B', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', ' ', 'iO, 'BO, 'smell', 'i', 't', 'smell', 'i', 'smell', 'i', ' ', ' ' + ['i', 'i', 'i', 'i', 'B', 'B', 'i', 'd', 'B', 'i', 'i', 'i', 'i', 'i', 'B', 'd', ' ', 'i', ' ', 'i', 'B', 'i', 'B', 'i', 'smell', 'smell', 'i', ' ', ' ', ' ', 'B + ['i', 'i', 'i', 'i', 'i', 'i', 'B', 'n', 'B', 'a', 'i', 'i', 'i', 'B', 'i', 'i', 'i', 'B', 'smell', 'i', 'i', 'i', 'i', 'iO, 'B', 'd', 'd', ' ', ' ', ' ', ' ', ' + ['i', 'i', 'B', 'i', 'B', 'i', 'i', 'B', 'i', 'i', 'i', 'B', 'i', 'B', 'i', 'i', 'iO, 'smell', 'i', 'smell', 'smell', 'smell', 't', 'NoneO, 'B', 'i', 'smell', 's + ['i', 'i', ' ', ' ', ' ', 'i', ' ', 'i', 'i', 'n', 'smell', 'wO, 'BO, 'i', '?OO, '?OO, ' ', ' ', ' ', ' ', ' ', ' ', ' ', '?O, '?O, '?O, 'i', 'i', 'i', 'i', 'i', + ['i', 'B', 'i', 'B', 'i', 'i', 'i', 'B', 'i', 'smell', 'x', 'BO, 'd'] + ['i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'B', 'i', 'i', 'NoneO, 'B'] + ['i', 'i', 'i', 'i', 'i', 'i', 'i', 'smell', 'i', 'i', 'i', 'B', ' ', ' ', '?O, 'B', ' ', 'B'] + ['i', 'i', 'i', 'i', 'i', 'i', 'i', 'n', 'smell', '_', 'i', 'B', 'i', 'B', 'NoneO, 'BO, 'B', 'B', 'B', 'd'] + ['i', 'B', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'B'] + ['B', ' ', 'i', ' ', 'i', 'NoneO, 'B', 'i', 'B', 'd'] + ['B', 'B', 'i', 'i', 'i', 'B', ' ', 'B', 'i', 'B', 'B'] + ['d', 'B', 'i', 'i', 'i', 'BO, 'B', 'i', 'i', 'i', 'i', 'B'] + ['B', 'i', 'i', 'i', 'B', 'BO, 'd', 'B', 'i', 'i', 'i', 'B'] + ['i', 'i', 'i', 'B', 'd', 'B', 'B', 'i', 'i', 'i', 'B'] + ['i', 'i', 'i', 'i', 'B', 'i', 'i', 'i', 'i', 'B'] + ['i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'B'] + ['i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'B'] + ['i', 'i', 'i', 'i', 'i', 'i', 'i', 'i', 'B'] + ['i', 'i', 'i', 'i', 'i', 'i', 'i', 'B'] + ['i', 'i', 'i', 'i', 'i', 'i', 'B'] + ['B', 'i', 'i', 'i', 'i', 'B'] + ['d', 'B', 'B', 'i', 'i', 'B'] + ['d', 'B', 'd', 'B', 'B', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'i', 'i', 'i'] + ['B', 'B'] + ['B'] + ['i', 'B'] + ['i', 'i', 'B'] + ['|] +``` + +### Solution/Exploit + +Code which I used to connect/walk around/store state locally see + +I wrote all characters of the flag (well all which I encountered) to this file: , for example the output from one run was `[['f', 'h'], ['f', 'x'], ['f', 'n'], ['f', 'p'], ['f', 'n'], ['f', 'w']]` (we can see some of the characters of the actual flag `hxp{and_n0w_try_t0_c4tch_m3_w1th0ut_dy1ng}`). Unfortunately I did not manage to find out the order of the flag characters. + +I did not find out the whole flag. This is a good writeup: - this guy was more intelligent than me and dumped the map automatically using an A* path algorithm (to find all the pits). He used the algorithm also to walk to a tile he wanted to go after having the pits he had to avoid. It was then easy to find out that there are 42 flag characters running around in the map (count until `Congrats, you caught the whole flag.`). The writeup author also found out that the seed which fed the randomness determing the flag characters is time.time() - and ran the same game locally feeding this seed as input to find out the actual character positions. The flag turned out to be `hxp{and_n0w_try_t0_c4tch_m3_w1th0ut_dy1ng}`. + +## tetres2019 (REV) + +Description: + +* We made a tetris game but we’re not sure whether it’s actually winnable. Can you please check? +* If you execute on hardware that sucks (i.e. not GT740M, GTX970, GTX1080, RTX2060, UHD 620, UHD 630, or any AMD) or software that sucks (i.e. not OpenGL version 4.6) the program might complain that some GL function was not loaded. This is not necessarily a hard error. It’s a hard error if it crashes. :) +* But even hard errors are, unfortunately, totally your problem. +* Good luck. + +Download: + +* tetres2019-ed197a2f816f57ea.tar.xz + * tetris.exe + +### Time spent + +1 hour + +### Overview + +I could not start the game (`The driver doesn't support some functions and the game might crash.`) but tried to analyze it. + +* File `tetris.exe` returned: `tetris.exe: PE32 executable (console) Intel 80386, for MS Windows` + +But ghidra only can load some lines of assembler core. Accidentially I opened it using 7-Zip and could extract a file `peippo`. As Ch4s3r in Mattermost wrote, this looks like a packed binary. Unfortunately I also could not load this `peippo` file in Ghidra although trying different formats. + +* File `peippo` returned: `peippo: data` + +Binwalk does not return results. The first bytes of this file are + +```xxd +00000000: fcbe 6001 3000 bf16 d430 00b9 a000 0000 ..`.0....0...... +00000010: f3a4 ba38 1330 00b9 b110 3000 e838 0100 ...8.0....0..8.. +00000020: 00ff 3581 1030 00ff 357d 1030 0068 d0af ..5..0..5}.0.h.. +00000030: 4100 6800 0040 00e8 ad05 0000 83c4 10e9 A.h..@.......... +00000040: 0fc4 1000 4b45 524e 454c 3332 2e44 4c4c ....KERNEL32.DLL +00000050: 0000 004c 6f61 644c 6962 7261 7279 4100 ...LoadLibraryA. +00000060: 0000 4765 7450 726f 6341 6464 7265 7373 ..GetProcAddress +00000070: 0051 1000 0060 1000 0000 0000 0051 1000 .Q...`.......Q.. +00000080: 0060 1000 0000 0000 0071 1000 0000 0000 .`.......q...... +``` + +### Solution + +I did not solve it. + +Writeup: , the challenge could be solved by using a graphics debugger ( was used in this case) to trace the OpenGL calls. Apparently the player had to score 1337 points for the flag to be rendered. Flag: `hxp{300$_GPU_For_7etr1s_1n_2019}` diff --git a/writeups/mli/hxp-36c3-ctf/exploit.py b/writeups/mli/hxp-36c3-ctf/exploit.py new file mode 100644 index 0000000..1be23c3 --- /dev/null +++ b/writeups/mli/hxp-36c3-ctf/exploit.py @@ -0,0 +1,182 @@ +#!/usr/bin/env python3 + +from pwn import * +from itertools import chain +import pickle, readchar + +host = '78.47.17.200' +port = 7888 + +conn = remote(host, port) + +pos = (0, 0) # x, y +servermap = [[]] + +flag_chars = [] + +def print_map(printpos=None): + temp = servermap + if printpos: + while len(temp) <= printpos[1]: + temp.append(['?']) + while len(temp[printpos[1]]) <= printpos[0]: + temp[printpos[1]].append('?') + temp[printpos[1]][printpos[0]] = str(temp[printpos[1]][printpos[0]])+'O' + print(repr(temp) + .replace('], ', ']\n ') + .replace("breeze", "B") + #.replace("smell", "i") + .replace("'?'", "' '") + .replace("O'", "O") + .replace("iwO", "|") + ) + + +def read_map(): + global servermap + with open("servermap", 'rb') as f: + servermap = pickle.load(f) + + +def set_map_val(val): + while len(servermap) <= pos[1]: + servermap.append(['?']) + while len(servermap[pos[1]]) <= pos[0]: + servermap[pos[1]].append('?') + # print(f'setting {pos} {pos[1]} {pos[0]} to {val}') + servermap[pos[1]][pos[0]] = val + + +def get_current_position(): + if not conn.connected(): + return + line = (conn.recvuntil('\n', True).decode()) + x = line.split('\x00') + print(f'{pos}: {x}') + set_map_val(x[-1]) + if x[0] == 'f': + flag_chars.append(x) + return x + +def get_map_val(position=pos): + try: + return servermap[position[1]][position[0]] + except: + return '?' + +def move(c): + x = get_current_position() + try: + conn.sendline(c) + except: + print('sendline error') + return None + return x[-1] + + +def down(): + global pos + res = move('s') + if res not in ('iw', 'd'): + x, y = pos + pos = (x, y+1) + set_map_val(res) + return res + + +def up(): + global pos + res = move('w') + if res not in ('iw', 'd'): + x, y = pos + pos = (x, y-1) + return res + + +def right(): + global pos + res = move('d') + if res not in ('iw', 'd'): + x, y = pos + pos = (x+1, y) + return res + + +def left(): + global pos + res = move('a') + if res not in ('iw', 'd'): + x, y = pos + pos = (x-1, y) + return res + + +read_map() +print_map() + +resp = None + +if 0 == 1: + for i in range(33): + resp = down() + resp = right() + resp = right() + resp = right() + + resp = down() + resp = down() + resp = down() + resp = down() + + resp = left() + resp = left() + resp = left() + + while resp != 'd': + resp = down() + #resp = right() + +while resp != 'd': + print_map(pos) + c = readchar.readchar() + if c == 'w': + resp = up() + continue + if c == 'a': + resp = left() + continue + if c == 's': + resp = down() + continue + if c == 'd': + resp = right() + continue + if c == 'Ü': + break + print('??') + continue + + #x, y = pos + #if get_map_val((x, y+1)) in ('iw', 'B'): # B below + # resp = up() + # continue + #if get_map_val((x, y-1)) in ('iw', 'B'): # B above + # resp = down() + # continue + #if get_map_val((x+1, y)) in ('iw', 'B'): # B right + # resp = left() + # continue + #if get_map_val((x-1, y)) in ('iw', 'B'): # B left + # resp = right() + # continue + #print('no B in sight - go right') + #resp = right() + +with open("servermap", "wb") as f: + pickle.dump(servermap, f) + +print_map() + + +with open("flagchars.txt", "a") as myfile: + myfile.write(repr(flag_chars)+'\r\n') diff --git a/writeups/mli/hxp-36c3-ctf/flagchars.txt b/writeups/mli/hxp-36c3-ctf/flagchars.txt new file mode 100644 index 0000000..3965d13 --- /dev/null +++ b/writeups/mli/hxp-36c3-ctf/flagchars.txt @@ -0,0 +1,40 @@ +[['f', 'd']] +[['f', '{'], ['f', 'x'], ['f', 'n']] +[['f', '_']] +[['f', 'm']] +[['f', 'h']] +[] +[['f', 'w'], ['f', 't']] +[['f', 'p'], ['f', 'd']] +[] +[] +[['f', '4'], ['f', 't'], ['f', '_']] +[['f', 'm']] +[['f', 'h']] +[] +[] +[] +[['f', '_'], ['f', 't'], ['f', 'm']] +[['f', '{'], ['f', '0'], ['f', 'r']] +[['f', '{'], ['f', 'd']] +[['f', '_'], ['f', 't']] +[['f', '{'], ['f', 'a']] +[] +[['f', '1'], ['f', 'u']] +[['f', 'h'], ['f', 'x']] +[] +[] +[] +[] +[] +[] +[] +[['f', '4']] +[] +[] +[] +[] +[['f', '_'], ['f', '0']] +[['f', 'h'], ['f', 'x'], ['f', 'n'], ['f', 'p'], ['f', 'n'], ['f', 'w']] +[['f', 'n'], ['f', 'a'], ['f', 'r'], ['f', '_'], ['f', 'y'], ['f', '4']] +[] -- 2.43.0