Rozwiązanie CrackMe Honeynet Project Workshop

Data publikacji: 07/04/2014, Łukasz Siewierski

hnp-crackme-crackedOgłoszony konkurs o wejściówkę na konferencję Honeynet Workshop 2014 w Warszawie został dzisiaj zakończony. Zwycięzcami są: Dariusz Tytko (z Polski) oraz @_zairon_, który opublikował również swoje rozwiązanie CrackMe na blogu. Zapraszamy do zapoznania się z rozwiązaniem konkursu, które znajduje się poniżej. Oczywiście jeśli wciąż rozwiązujecie zadanie to nie czytajcie dalej tego wpisu, bo zawiera spojlery.

Na początek jednak garść statystyk.

    • Plik został pobrany 236 razy.
    • Przesłano 114 flag, w tym 92 były poprawne.
    • Odpowiedzi przesłały 23 osoby, w tym 13 z Polski.
    • Prawie wszyscy uczestnicy znaleźli 3 lub więcej flag.

    Zwycięzcom gratulujemy i mamy nadzieję, że wszyscy się dobrze bawili.

    Zadanie polegało na znalezieniu 10 flag w przygotowanym przez nas pliku. Poniżej opisane są sposoby znalezienia wszystkich 10 flag w kolejności od najczęściej znajdowanej do tej najmniej popularnej.

    Flaga: ThisWasEasyToFind! (20 zgłoszeń)

    malware-icon

    Kilkadziesiąt minut po ogłoszeniu konkursu plik znalazł się już w serwisie VirusTotal. Dwa z testowanych programów błędnie rozpoznało go jako malware. W zakładce „Szczegóły pliku” znalazła się jednak jedna z flag. Hasło to zostało ukryte jako komentarz w metadanych pliku, które wyświetlane są (przynajmniej w systemie Windows XP) po wejściu we „właściwości” pliku tak jak na zdjęciu po lewej. Alternatywnie, jeśli nasz system nie wyświetla tych metadanych, wystarczyło skorzystać z narzędzia strings z przełącznikiem -e l oznaczającym wypisanie wszystkich znalezionych w pliku tekstów zapisanych w kodowaniu Unicode 16-bitowym w konwencji Little Endian. Wyjście polecenia zaprezentowano poniżej, flaga znajduje się w 9 linii.

    $ strings -e l ./CrackMe.exe
    INTERESTING_PCITURE
    VS_VERSION_INFO
    VarFileInfo
    Translation
    StringFileInfo
    04090025
    Comments
    flag{ThisWasEasyToFind!}
    CompanyName
    CERT Polska
    FileDescription
    CrackMe
    FileVersion
    InternalName
    LegalCopyright
    CERT Polska
    LegalTrademarks
    OriginalFilename
    PrivateBuild
    ProductName
    CrackMe
    ProductVersion
    SpecialBuild

    Flaga: HowToFindStringsInPEYouKnow (15 zgłoszeń)

    Tę flagę można było uzyskać za pomocą narzędzia strings. Wystarczyło jedynie rozpakować plik EXE. Plik ten był spakowany za pomocą popularnego packera o nazwie UPX, a następnie zmieniono w nim nazwę jednej z sekcji ze standardowej na niestandardową. Powodowało to, że rozpakowanie pliku powodowało wyświetlenie następującego komunikatu:

    $ upx -d ../CrackMe.exe
    Ultimate Packer for eXecutables
    Copyright (C) 1996 - 2013
    UPX 3.09 Markus Oberhumer, Laszlo Molnar & John Reiser Feb 18th 2013

    File size Ratio Format Name
    -------------------- ------ ----------- -----------
    upx: ../CrackMe.exe: CantUnpackException: file is modified/hacked/protected; take care!!!

    Unpacked 0 files.

    Offsety 179-17C zawierały ciąg znaków XXX0, a w standardowo spakowanym pliku tym ciągiem powinno być nazwa sekcji UPX0. Zmianę tej nazwy można było wykonać dowolnym edytorem tekstowym. Po zmianie plik rozpakowuje się normalnie, a wykonanie na nim polecenia strings powoduje znalezienie ciągu znaków ZmxhZ3tIb3dUb0ZpbmRTdHJpbmdzSW5QRVlvdUtub3d9Cg==, który po zdekodowaniu za pomocą base64 daje nam kolejną flagę.

    Flaga: HaveNoFear,ConsoleFlagIsHere! (13 zgłoszeń)

    Znalezienie tej flagi również nie sprawiało większych problemów. Wystarczyło mieć uruchomiony program windowssystem32cmd.exe i następnie uruchomić nasze CrackMe. W konsoli powinna się wtedy wypisać flaga tak, jak to przedstawiono na zrzucie ekranu poniżej.

    malware-icon

    Flaga: VeryGoodHardDriveName (10 zgłoszeń)

    malware-icon

    Po uruchomieniu pliku CrackMe.exe drugim oknem dialogowym, które się wyświetlało było okno przedstawione po lewej. Zawierało ono podpowiedź, która wskazywała na malware o nazwie Andromeda. Malware ten implementował technikę sprawdzającą czy jest uruchomiony na maszynie wirtualnej. Jeśli tak było, to wykonywał inny kod niż gdyby był uruchomiony na zwykłej maszynie. Jednak autorzy malware’u również testowali go na maszynie wirtualnej, więc wymyślili sposób, aby go na niej uruchomić. Wystarczyło zmienić nazwę dysku C: na taką, której CRC32 wynosi 0x20C7DD84 (najbardziej znany przykład to CKF81X). Wtedy nasze CrackMe wyświetlało, zamiast wcześniejszego okna dialogowego, nowe, już zawierające flagę takie jak poniżej.

     

    malware-icon

    Flaga: YouKnowHowToDebugCode! (9 zgłoszeń)

    Aby uzyskać tę flagę, trzeba było zdebugować nasze CrackMe. Można to zrobić za pomocą darmowego narzędzia OllyDbg, albo za pomocą IDA Pro. Kod wywoływał funkcję IsDebuggerPresent, która sprawdzała czy proces jest debuggowany i jeśli tak było to wyświetlał monit You shall not pass!. Jednak po jego wyświetleniu wywoływana była funkcja, której wynikiem było wypisanie ciągu znaków za pomocą OutputDebugString. Ciąg ten był tworzony za pomocą xorowania ciągu znaków changeme z wcześniej „zaszyfrowanym” tekstem przedstawionym jako ciąg bajtów:

    0x39,0x47,0x47,0x46,0xa,0x6,0x44,0x53,0x6a,0x1f,0x30,0x5c,0x6e,0x4e,0x6,0xb,0x44,0x62,0x44,0x13,0x2a,0x4c,0x65,0x4e,0x15,0x3a,0xa,0x5b

    Oczywiście trzeba było zmienić ciąg znaków changeme na właściwe hasło. Można to było wykonać korzystając z faktu, że operacja xor jest operacją odwrotną do samej siebie oraz z faktu, że pierwsze pięć znaków każdej flagi to flag{, a tylko 5 bajtów z ciągu changeme było wykorzystywane. Wyliczenia wyglądają następująco:

    0x39 xor 0x66 ('f') = 0x5f ('_')
    0x47 xor 0x6c ('l') = 0x2b ('+')
    0x47 xor 0x61 ('a') = 0x26 ('&')
    0x46 xor 0x67 ('g') = 0x21 ('!')
    0x0a xor 0x7b ('{') = 0x71 ('q')

    Zatem hasłem jest _+&!q i po rozkodowaniu powyższej tablicy bajtów otrzymujemy zadaną flagę. Można to zrobić albo rozkodowując tablicę za pomocą skryptu, albo po prostu zmieniając w debuggerze ciąg znaków changeme na _+&!q.

    Flaga: RussianFlagItIs (8 zgłoszeń)

    malware-icon

    Tę flagę było stosunkowo łatwo znaleźć, ale osoby, które ją znalazły często źle ją interpretowały. Flaga znajdowała się w nagłówku pierwszego okna dialogowego, które pojawiało się po uruchomieniu CrackMe. Była ona zapisana cyrylicą i trzeba było ją transliterować, zgodnie z zasadami transliteracji, aby otrzymać ciąg znaków ASCII. Kilka osób wysłało flagę w oryginalnej pisowni, cyrylicą, nie czytając dokładnie opisu konkursu, w którym wyraźnie zaznaczyliśmy, że wszystkie flagi składają się z drukowalnych znaków ASCII bez znaków białych. Transliteracji można było dokonać np. za pomocą usługi Google Translate. Jedną z przeszkód mogło też być podobieństwo w cyrylicy znaków ф oraz Ф (odpowiednio małe i duże „f”) lub w ogóle ignorowanie wielkości liter. Jednak sprawiło to problem tylko dwóm uczestnikom.

     

    Flaga: RC4EncryptionIsFun!!!1 (6 zgłoszeń)

    Plik CrackMe tworzył plik systemowy decode.py w katalogu %TEMP%. Plik ten zawierał następujący kod języka Python:

    from Crypto.Cipher import ARC4
    from base64 import b64decode
    import sys
    obj = ARC4.new(sys.argv[1][:5])
    text = b64decode('LNLyv86npNDGrMxHrbpzHGoueiX3d3SPOmIZAg==')
    text = obj.decrypt(text)
    print text

    Kod ten dekodował najpierw, za pomocą base64, ciąg znaków z linii 5, a następnie deszyfrował go za pomocą klucza podanego z linii poleceń. Tylko pierwsze 5 znaków klucza było brane pod uwagę. Najprostszym sposobem odszyfrowania tekstu była metoda brute force. Wykonanie jej na 5 drukowalnych znakach ASCII za pomocą bardzo prostego skryptu Pythona przedstawionego poniżej powinno zajmować około 10 godzin, w zależności od użytej maszyny. Zrównoleglając proces poszukiwania, albo wręcz przepisując go np. na język C, proces ten można było skrócić nawet do 2-3 godzin.

    from Crypto.Cipher import ARC4
    from base64 import b64decode
    import sys
    import itertools, string
    org = b64decode('LNLyv86npNDGrMxHrbpzHGoueiX3d3SPOmIZAg==')
    for i in itertools.product(string.printable, repeat=5):
    key = ''.join(i)
    obj = ARC4.new(key)
    text = obj.decrypt(org)
    if text.startswith('flag{'):
    print key, text

    Prawidłowym kluczem, i jedynym wynikiem powyższego skryptu, było Oi01_.

     

    Flaga: JPEGalsoHasAFlag (5 zgłoszeń)

    malware-icon

    Po rozpakowaniu pliku exe za pomocą UPX jednym z zasobów w nim schowanych było archiwum xz. Zasób ten można wydobyć np. za pomocą darmowego narzędzia ResEdit. Po rozpakowaniu tego archiwum otrzymywaliśmy jeden plik o nazwie picture.png. Po otwarciu tego pliku prezentowana była flaga, tak jak na obrazku po prawej stronie.

    Flaga: PNGdoesNotHaveExif,ButStillIsFun (4 zgłoszenia)

    W wyżej wspomnianym pliku PNG znajdował się również pewien specjalny chunk. Chunk to dodatkowe informacje, które mogą być zawarte w pliku PNG. Niektóre są niezbędne, np. ten zawierający rozmiary obrazu, podczas gdy inne są czysto informacyjne. Jednym z najprostszych sposobów na wyświetlenie tych wartości jest użycie narzędzie strings, tak jak zaprezentowano poniżej.

    $ strings -n7 picture.png
    tEXtComment
    Created with GIMPW
    -tEXtFlag4U
    flag{PNGdoesNotHaveExif,ButStillIsFun}2+
    >}!fYYY

    Flaga: DOSisPower (2 zgłoszenia)

    Ostatnia z flag była ukryta jako program systemu DOS. Pliki PE, ze względu na kompatybilność wsteczną, na początku mogą zawierać program, który uruchamia się pod systemem DOS. Zwykle jest to program wyświetlający napis This program cannot be run in DOS mode. Tym razem jednak ten stub został podmieniony na inny program, którego kod jest zaprezentowany poniżej. Warto przypomnieć, że kod DOS jest 16-bitowy.

    00 0e push cs
    01 1f pop ds
    02 be0000 mov si, 0x0
    05 bb2200 mov bx, 0x22
    08 b86600 mov ax, 0x66
    0b 3200 xor al, [bx+si]
    0d 8800 mov [bx+si], al
    0f 46 inc si
    10 81fe1000 cmp si, 0x10
    14 75f5 jnz 0xb
    16 ba2200 mov dx, 0x22
    19 b409 mov ah, 0x9
    1b cd21 int 0x21
    1d b8004c mov ax, 0x4c00
    20 cd21 int 0x21

    malware-icon

    Program ten bierze ciąg znaków znajdujący się pod offsetem 0x22 a następnie dekoduje go do flagi. Dekowanie odbywa się za pomocą prostego algorytmu. Pierwszy znak jest xorowany z liczbą 0x66 (offset 0x08, 0x0b) , a każdy następny z poprzednio odszyfrowanym bajtem. W ten sposób otrzymujemy tekst flagi, który ma 16 znaków (offset 0x10). Następnie flaga jest wyświetlana (offsety 0x16, 0x19, 0x1b) i program zwraca kod wyjścia 0 (offsety 0x1d, 0x20).

    Aby uzyskać tę flagę wystarczyło uruchomić program pod systemem DOS, np. za pomocą programu DOSBox. Wykonanie zaprezentowano po prawej.

    Podsumowanie

    Konkurs nie okazał się zbyt trudny (i taką mieliśmy nadzieję). Pierwszy zestaw 10 flag dostaliśmy od osoby z Polski w sobotę, 5 kwietnia o 18:50:09 CEST. Z zagranicy komplet flag otrzymaliśmy o w niedzielę, 6 kwietnia o godzinie 18:48:06 CEST. Zwycięzcom jeszcze raz gratulujemy!