piątek, 17 października 2025

picoCTF - hashcrack

W tym poście opiszę rozwiązanie zadania Hashcrack z picoCTF. 


Hash jest wynikiem działania funkcji matematycznej która przekształca dane wejściowe w ciąg o stałej długości. Wprowadzenie małej zmiany w danych wejściowych spowoduje kompletną zmianę wyniku. 

Stosuje się ją do weryfikacji integralności danych. Na przykład weźmy jakiś plik konfiguracyjny w systemie, czy dane z pamięci. Jeśli na danych wykonamy zmianę i ktoś je zmodyfikuje bez aktualizacji hash'a np. ręcznie bądź przez jakiś błąd. To po odczycie i weryfikacji będziemy w stanie stwierdzić, że dane zostały zmienione w niezatwierdzony przez programistę/administratora sposób. 

Inne zastosowanie to przechowywanie haseł w bazie danych jako hash. Przy logowaniu do konta użytkownik wpisuje swoje hasło. Z tego hasła wyliczany jest hash i następuje porównanie z hashem z bazy danych, a nie bezpośrednio z hasłem. Jeśli ktoś uzyska dostęp do bazy danych to nie będzie mógł odzyskać haseł bezpośrednio. Wynika to z faktu, że hash jest jednostronny. Jest praktycznie niemożliwe by z hash'a odtworzyć wynik. Zamiast tego stosuje się porównywanie wygenerowanych hashów przy próbach złamania haseł. Z tego powodu trudne hasło znacznie wydłuża bądź całkowicie uniemożliwia taki proces. 

Wyróżnia się następujące funkcje hashujące:
MD5, SHA-1 - nie zalecane. 
SHA-256, SHA-512 - powszechnie stosowane. Uznane za bezpieczne. 

Jedną ze wskazówek do zadania jest zapoznanie się z plikiem rockyou.txt (można znaleźć np. na githubie).


Zawiera on gigantyczną listę powszechnie używanych haseł. Można go użyć do przygotowania programu, który będzie tworzył hash z tekstów i porównywał z szukanym ciągiem. 

Wszystkie hasła wykorzystane w tym zadaniu znajdują się na tej liście. 

Po uruchomieniu zadania dostajemy następującą informacje:


Dostaliśmy hash i musimy na jego podstawie odgadnąć hasło:

  1. 482c811da5d5b4bc6d497ffa98491e38
 
Hash ma długość 32 znaków. Z tego powodu można zakładać, że jest to MD5. Co potwierdzamy na stronie (link https://hashes.com/en/tools/hash_identifier):


W celu odzyskania danych z takiego hash'a, najprościej, posłużyć się stroną internetową, która po wgraniu danych wydrukuje poprawną odpowiedź (link: https://crackstation.net/):

W celu weryfikacji można sprawdzić jak wygląda hash dla password123: 

  1. import hashlib
  2. print(hashlib.md5(b"password123").hexdigest())

Można też odrobinę rozbudować ten program i porównywać dane z szukanym hashem:

  1. import hashlib
  2.  
  3. # Lista potencjalnych haseł
  4. passwords = [
  5.     "123456",
  6.     "password",
  7.     "pass",
  8.     "password123",
  9.     "tset123",
  10.     "test4566"
  11. ]
  12.  
  13. target_hash = "482c811da5d5b4bc6d497ffa98491e38"
  14.  
  15. for pass_d in passwords:
  16.     hashed = hashlib.md5(pass_d.encode()).hexdigest()
  17.     if hashed == target_hash:
  18.         print(f"Znaleziono hasło: {pass_d}")
  19.         break
  20. else:
  21.     print("Nie znaleziono pasującego hasła")
Po wprowadzeniu hasła przechodzimy do następnego:


Tym razem jest to SHA-1. Długość 40 znaków. 


Wracamy na stronę crackstation:


Po wprowadzeniu hasła dostajemy kolejnego hash'a tym razem są to 64 znaki, czyli SHA-256:


Znowu crackstation:


Dla testu wygenerowałem trudne hasło leHowtWfkdWkNPXL. Z niego wygenerowałem hash SHA-256

48ebcae188291687c08beaf0a15b98a8576569ca701ddaefd13845e28d7b1b79

Wrzuciłem go na stronę crackstation:


Tym razem nie poradził sobie z zadaniem. Jak widać każdy z hashów jest możliwy do złamania jeśli wykorzystywane są przewidywalne ciągi znaków. 

W celu utrudnienia łamania haseł stosuje się takie techniki jak salt oraz pepper. 

Salt jest losowym ciągiem znaków dodawanym do hasła przed jego wygenerowaniem. Każdy użytkownik ma przypisany do swojego konta inny salt. Przez co nawet jak hasło jest proste to nie łatwo je znaleźć na gotowych stronach. Czyli użytkownik A i użytkownik B z hasłem "123456test" będą mieli dwa różne hash'e. Jest on najczęściej przechowywany jawnie w bazie danych. Nawet jak ktoś dostanie bazę danych to musiałby liczyć hash'e osobno dla każdego użytkownika.

Pepper jest dosyć podobny, z tym wyjątkiem, że jest on tajny i wspólny dla całej aplikacji. Nie przechowuje się go w bazie danych. Czyli wraz z salt są dwa dodatkowe elementy do hasha.

Te elementy wraz z funckjami hashowania jak Argon2, który jest wolny (bruteforce trwa zdecydowanie dłużej niż dla np. SHA256) i zajmuje dużo pamięci stanowią dosyć dobre zabezpieczenie całej aplikacji.

Dobra wróćmy do zadania. 

Jak już mamy wszystkie hasła to otrzymujemy pełne rozwiązanie:


Teraz można wprowadzić flagę i zakończyć zadanie.