Pocztówka od KrzaQ

4 minute read Published:

foxtrot_charlie znów napis… a nie napisał posta - Gynvael Coldwind

0x0. Nieoczekiwana przesyłka czyli jak trafiłem do dzienniczka

Czasami śledząc losy takich agentów jak popularny Thomas, zastanawiam się jak (nie)wiele potrzeba, aby trafić na watchlistę trzyliterowej firmy sponsorowanej z budżetu państwa. Jak bardzo musiałbym się postarać? W podobnym zamyśleniu trwałem w momencie wyciągania poczty, ze skrzynki, pewnego środowego poranka. I w tym momencie w moje ręce wpadła pocztówka. Ot taka zaadresowana do @foxtrot_charlie.

Świetnie pomyślałem spoglądając na tę część, która z reguły zawiera kilka miłych dla adresata słów, teraz to już na pewno taka firma doda sobie mnie do znajomych.

Backend of the postcard

0x1. Biuro Szyfrów i Spraw Beznadziejnych

No dobra, skoro orzeł wylądował to wypadałoby coś z tym fantem zrobić. Trochę z obawą zająłem się rozwiązywaniem tej łamigłówki, no bo jakby mi nie wyszło… to jak to tak… cough niehackersko cough! Wiem! Powiem, że mi się zgubiła… Nie, słabe to, muszę rozwiązać. No nic, ** failure is not an option ** jak to mówią.

1. Przepisujemy!

Powiedzmy sobie szczerze, operowanie bitami znacznie szybciej przychodzi naszym kwarcowym kumplom niż białkowcom. Po zdigitalizowaniu otrzymujemy coś takiego:


51 63 70 62   63 61 78 6c   63 20 6e 6d
78 62 70 6d   75 67 63 6c   67 79 20 78
20 4c 6d 75   63 65 6d 20   48 6d 70 69
73 21 0a 0a   69 6f 0a

2. Łamiemy!

Na pierwszy rzut oka wszystkie wartości są zawierają się w przedziale ASCII, więc może szyfrowanie nie jest aż takie trudne? Sprawdźmy.

Krótki helper w Pythonie i otrzymujemy:

Qcpbcaxlc nmxbpmugclgy x Lmucem Hmpis!

io

Chyba jest dobrze… Mamy coś co przypomina wyrażenie zgodne z jakimś językiem, wszystkie znaki należą do ASCII, nie mamy żadnych krzaczków (KrzaQ ;D). Czyli pewnie zastosowano jakiś szyfr podstawieniowy. To jest moment w którym powiedziałem sobie to “io” to będzie podpis kq. Stąd droga do rozwiązania łamigłówki już krótka. Za każdy znak podstawiamy jego odpowiednik “oddalony” w alfabecie o określony offset. Dla każdego przesunięcia printujemy rozwiązanie. Jedno z nich wygląda następująco:

Serdeczne pozdrowienia z Nowego Jorku!

kq

BINGO! Chciałem tylko nadmienić, że mój zmysł łowcy od razu podpowiedział mi, że io -> kq, #aniemowilem?

Dziękuję serdecznie za śliczną pocztówkę KrzaQ! Publicznie zobowiązuję się do zajęcia Ci chwili podobną wiadomością!

To jeszcze panorama Nowego Jorku:

frontend of the postcard

Epilog

Zgodnie z założeniami tego typu postów, mają one nieść ze sobą pewną wartość merytoryczną. Dlatego przedstawiam krótki kod, którym rozwiązałem to zadanie. Oczywiście da się to zrobić ładniej, krócej, szybciej, ale tak jak w przypadku CTFów: what works works.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#!/usr/bin/env python3
import string
import codecs


def rot(i, text):
    # prosta i przyjemna w użyciu funkcja maketrans zamieni nam odpowiadające
    # literki (operuje na bajtach stąd .encode())
    rotI = bytes.maketrans(
        string.ascii_letters.encode(),
        string.ascii_letters[i:].encode() + string.ascii_letters[:i].encode()
    )

    # zwracamy "przetłumaczoną" wiadomość zdekodowaną do ascii
    return codecs.decode(text.encode().translate(rotI))


def main():
    ''' 
    zawartość postcard.txt:
        51 63 70 62   63 61 78 6c   63 20 6E 6D
        78 62 70 6D   75 67 63 6c   67 79 20 78
        20 4c 6d 75   63 65 6d 20   48 6d 70 69
        73 21 0a 0a   69 6f 0a
    '''
    # wczytujemy kartkę z pliku postcard.txt i zamieniamy na ascii
    ptcard = ''.join([chr(int(x, 16)) for x in open(
        './postcard.txt').read().replace('\n', ' ')
        .replace('   ', ' ').strip().split(" ")])

    '''
    Wynikiem działania jest:
    Qcpbcaxlc nmxbpmugclgy x Lmucem Hmpis!

    io
    '''
    # teraz najważniejsza część, czyli dla każdego przesunięcia (rotX)
    # tworzymy odpowiadający mu string

    for i in range(len(string.ascii_letters)):
        print(rot(i, ptcard))

if __name__ == '__main__':
    main()

Przy okazji postanowiłem pobawić się i stworzyć niezbyt czytelnego onelinera, który również rozwiąże łamigłówkę:

import string; print(list(map(lambda i: '{} --->  '.format(i) + ''.join( [x.upper() if w.isupper() else (x) for x, w in zip([chr(ord('a')+(ord(g) + i)%len(string.ascii_lowercase)) if g in string.ascii_lowercase else g for g in 'Qcpbcaxlc nmxbpmugclgy x Lmucem Hmpis!\n\nio'.lower()], list('Qcpbcaxlc nmxbpmugclgy x Lmucem Hmpis!\n\nio'))]), range(len(string.ascii_lowercase))))[9])

foxtrot_charlie