19:30
11/7/2017

Najsłabszym elementem większości systemów komputerowych jest człowiek. Tak było i w tym przypadku. Tę historię opisuję w celach edukacyjnych, by pokazać na jakie problemy natknąłem się, starając się pomóc użytkownikom jednej z kryptowalut.

Autorem gościnnego wpisu jest Krzysztof ‘Noisy” Szumny.

By nadać sprawie trochę kontekstu, Steem (nie mylić ze Steam od Valve) to zdecentralizowana sieć do publikowania treści oparta o własny blockchain, posiadająca własny token kryptowalutowy. W czerwcu tego roku zaobserowowałem coś ciekawego. Pomyłkę użytkownika (a jak się później okazało, wielu użytkowników), która pozwalała przejąć konto ofiary i zgromadzoną na nim kryptowalutę, nierzadko wartą kilka tysięcy dolarów.

Zastanawiacie się, na czym polegała pomyłka? Okazało się, że część z osób, z jakiegoś powodu, w polu opisu przelewu (tzw. “memo”) podaje swoje hasło… W serwisie Steemit (najpopularniejszym serwisie korzystającym z tego blockchaina), transfer środków wykonuje się z pomocą takiego formularza:

Źle udzielona pomoc == niedźwiedzia przysługa

Było dla mnie jasne, że nie chcę tej pomyłki wykorzystać do tego, by szybko się wzbogacić czyimś kosztem. Jestem pewien, że bardzo wiele osób doszło by do tego samego wniosku. Następny krok wydawał się zatem dość jasny — musiałem skontaktować się z właścicielem konta w taki sposób, by mógł on ponownie zabezpieczyć swoje konto zmieniając hasło. Proste, prawda?

I tutaj nagle się okazało, że sprawa wcale taka łatwa nie jest, a moja nieodpowiednio udzielona pomoc, może wyrządzić całej społeczności więcej szkody niż pożytku. W momencie, w którym poinformowałbym kogoś, że upublicznił swoje hasło, ta osoba mogłaby się zacząć zastanawiać jak do tego doszło i doszłaby do dokładnie tego samego wniosku, do którego doszedłem i ja. Że nie tylko ona mogła taki błąd popełnić, a osób zmylonych interfejsem formatki przelewu jest więcej. W momencie w którym ujawnił bym przypadek tej osoby publicznie, mógłby się więc rozpocząć wyścig, mający na celu znalezienie wszystkich podobnych wpadek i przejęcie kont ich właścicieli.

Ocena szkód

Pierwszą rzeczą, jaką zrobiłem było sprawdzenie jak popularny był to problem. Musiałem wiedzieć o co toczy się stawka. Każdy blockchain z definicji powinien być w 100% otwarty, zarówno pod względem kodu jak i pod względem danych. Tak też było i w tym przypadku. To co zatem musiałem zrobić, to przeanalizować wszystkie transakcje jakie miały miejsce od kwietnia 2016 i znaleźć wszystkie te które w swoim memo upubliczniły swoje hasło. Tylko skąd wiedzieć, czy coś jest hasłem a co po prostu normalnym opisem przelewu?

Z pozoru to nie wydawało się trudne, bowiem obecnie, każdy nowy użytkownik zakładający konto w sieci Steem poprzez serwis Steemit ma generowane (w JS, po stronie klienta) 52-znakowe hasło, które zawsze posiada jako prefix dużą literę “P“. By znaleźć większość upublicznionych haseł wystarczyło, zrobić prostego selecta na dumpie z całego blockchaina. Takich haseł zostało znalezionych 28.

Problem był w tym, że takie zapytanie sprawdzało tylko trywialny przypadek, a nie brało pod uwagę:

  • że ktoś mógł zmienić już to hasło: właściciel lub ktoś inny, kto zauważył jego wyciek;
  • że przed lipcem 2016 z poziomu Steemit, możliwe było ustawienie dowolnego hasła posiadającego co najmniej 16 znaków;
  • że istnieją serwisy jak steemconnect czy http://anon.steem.network/, który generowały hasła według innych schematów;
  • że z poziomu konsoli każdy może ustawić sobie dowolne hasło o dowolnej długości;
  • że wyciec mogły nie tylko hasła… a i klucze prywatne!

Znalezienie przyczyny problemu

Z dużą dozą pewności założyłem, że ludzie nie narażali swoich środków zarobionych na blogowaniu z premedytacją. Coś musiało sprawiać, że coraz to nowi ludzie popełniali ten sam, bardzo niebezpieczny błąd. Moim celem stało się znalezienie problemu i dowiedzenie się, dlaczego tak naprawdę ludzie ten błąd popełniali. W tym celu zacząłem przyglądać się różnym sposobom wykonywania transferów.

Serwis Steemit nie przechowuje na swoich serwerach ani haseł ani kluczy prywatnych (które de facto są generowane z hasła, jako z seeda). By jednak użytkownik każdorazowo nie musiał przy każdej akcji (jak głosowanie, czy publikacja komentarza) dostarczać swojego klucza w celu podpisania transakcji, serwis wykorzystuje do tego celu localstorage przeglądarki. W ten sposób po stronie klienta przechowywany jest tylko jeden klucz prywatny generowany z hasła — private posting key służący do publikacji treści.

Gdy jednak użytkownik zechcę wykonać akcję wymagającą szerszych uprawnień (jak zrobienie transferu środków), system prosi albo o podanie hasła z którego wygeneruje stosowny klucz prywatny albo o stosowny klucz prywatny. W trakcie uzupełniania formularza transferu, wyświetla się drugi formularz (wprowadzający zamieszanie modal nad modalem), proszący o uwierzytelnienie. W momencie w którym hasło czy klucz prywatny jest wręcz niemożliwy do zapamiętania, większość użytkowników zapewne po prostu go kopiuje. I niektórzy skopiowali swoje hasło/klucz nie do tego pola, do którego powinni…

Kliknij, bo gif:

Głębsza analiza wykazała jednak, że niektóre klucze prywatne zostały upublicznione nawet wtedy, gdy przelew był robiony nie z poziomu interfejsu Steemit, lecz z poziomu giełd (np. poloniex, czy bittrex). O ile możliwe było dostarczenie poprawki dla serwisu Steemit, pewnym było że nie jest możliwe, dostarczenie poprawek, dla każdej giełdy, która sprawdzałaby, czy przypadkiem użytkownik nie wkleja w dowolne pole swojego hasła.

Ochrona poprzez atak

Rzeczą zabawną dla mnie był fakt, że chcąc bez narażania innych zapobiec utracie środków z kilku kont o których już wiedziałem, musiałem przeprowadzić atak na wszystkie konta. Gdybym ja tego nie zrobił, w momencie upublicznienia komukolwiek jakiejkolwiek informacji, ryzykowałem, że zrobiłby to ktoś inny.

Atak na wszystkie konta w serwisie z pomocą potencjalnych haseł/kluczy

Ataki Brute Force są trudne do przeprowadzenia, gdy do sprawdzenia poprawności klucza/hasła wymagana jest interakcja z atakowanym serwisem (requesty wysyłane przez internet są dość czasochłonne, a sama próba sprawdzenia milionów kombinacji może został łatwo wykryta i mogą zostać podjęte działania mające utrudnić taki atak, np. odcięcie serwera po IP). Choć jak pokazuje wpadka Wykopu, nie zawsze tak jest.

W moim przypadku jednak sytuacja od początku była zupełnie inna. Po pierwsze każdy może posiadać node danej sieci opartej na blockchainie, a co za tym idzie posiadać wszystkie dane potrzebne do weryfikacji danych wymaganych do uwierzytelnienia. Warto też pamiętać, że typowy blockchain nie przechowuje żadnych haseł, czy nawet ich solonych hashy. Blockchain Steem przechowuje wszystkie klucze publiczne użytkowników. Z kolei same klucze publiczne są generowane z kluczy prywatnych, które z kolei są generowane z seed’a, którym użytkownik posługuje się jako swoim hasłem.

Warto podkreślić, że w przypadku każdego poprawnie zrealizowanego systemu opartego na kryptografii asymetrycznej, zgadywanie klucza prywatnego mija się z celem, ponieważ zbiór wszystkich kombinacji jest znacznie większy niż szacowana liczba atomów we wszechświecie. Ataki brute-force na takie systemy są więc bezcelowe…

Wszystko się jednak diametralnie zmienia, gdy posiada się wskazówki co do możliwej wartości takiego klucza. A tutaj wskazówki były bardzo potężne: opis przelewu może być seed’em do generowania kluczy, bądź jednym z kluczy prywatnych:

  • osoby wykonującej przelew
  • osoby będącej adresatem przelewu (przypadek w którym użytkownik wysyła sobie środki np. z giełdy).

Więc zamiast potrzebnej całej wieczności do sprawdzenia wszystkich możliwości, potrzebne było tylko kilkaset godzin na sprawdzenie zbioru mającego dość duży potencjał :)

Skrypt

Kilkaset godzin czekania to dość dużo, gdy wie się, że w tym czasie ktoś inny może wyczyścić wszystkie konta. Nie chciałem zacząć zabezpieczać jednego konta po drugim zaraz po znalezieniu pasującego klucza lub hasła, gdyż ktoś mógłby wykryć, że utracił dostęp do konta. To z kolei mogło by wszcząć globalny alarm, który mógłby spowodować wykrycie przez kogoś powodu ataku.

W tamtym momencie naprawdę żałowałem, że częściej nie miałem okazji ćwiczyć pisania skryptów wielowątkowych, gdyż pisanie pod presją czasu i w lekkim stresie, nie jest zbyt efektywne, zwłaszcza gdy wiesz, że jeżeli coś przegapisz, to ktoś inny może to wykorzystać.

Ostatecznie skrypt napisany przeze mnie w Pythonie, odpalony na serwerze posiadającym 8 CPU wykonywał się około 20 godzin.

White Hat Hacking w praktyce jest trudniejszy niż myślałem

Gdybym chciał wszystko zgarnąć dla siebie, to po napisaniu takiego skryptu byłbym już prawie po robocie. Wystarczyłby już tylko drugi skrypt, który wykonałby kilkadziesiąt transferów. Ja musiałem jednak napisać inny skrypt, który w mgnieniu oka był w stanie zmienić hasła na nowe, unieważniając tym samym stare hasła, które zostały upublicznione. Czyli teraz już z górki? Niestety nie…

Problem numer 1
Jeżeli przejmujesz konto obcej osoby, w celu zabezpieczenia go… skąd masz wiedzieć, że osoba która później zgłosi się do Ciebie po to konto faktycznie będzie tą osobą, która posiadła te konto w pierwszym momencie? Przecież każdy może podać upublicznione kluczy czy hasło…

Problem numer 2
Przyczyna problemu na tamten moment nadal nie została usunięta. W dalszym ciągu inni użytkownicy mogli wejść na tą samą minę popełniając ten sam błąd. W momencie upublicznienia informacji, inni atakujący również będą to wiedzieć i szybko stworzą skrypty analizujące nowe transakcje i przechwytujące kolejne konta nieszczęśników.

Wniosek: przede mną stało zadanie napisania stosownej poprawki.

Problem numer 3
Nawet po napisaniu dość łatwego fixa, jego deployment nie mógł przebiegać już tak typowo. Normalny workflow w którym zgłasza się publicznie PullRequesta do repozytorium na githubie, defaco upubliczniałby znowuż cały problem.

Wniosek: by zminimalizować niebezpieczeństwo, powinno upłynąć bardzo mało czasu od momentu stworzenia PullRequesta do momentu jego deploymentu.

Problem numer 4
Co z przelewami wykonywanymi z giełd? Dostarczenie fixa takim giełdom było niemożliwe. Użytkowników popełniających błędy nie da się uchronić przed wszystkim, ale fajnie było by ich nie zostawiać na pastwę atakujących.

Napisałem skrypt przejmujący kolejne konta z haseł udostępnionych w memo. Miał on u mnie chodzić 24/7, problem w tym, że ktoś zawsze mógł być o milisekundy szybszy.

Realizacja

Najpoważniejszym problemem był problem pierwszy. W najlepszym przypadku byłbym w stanie zweryfikować tożsamość kilku użytkowników, bowiem wcześniej jako blogerzy udostępnili oni alternatywny sposób kontaktu z nimi za pomocą innych mediów, bądź udostępnili swoje zdjęcia przedstawiając się społeczności. Te informacje musiałbym jednak znaleźć wśród wszystkich artykułów i komentarzy tychże osób. Gdyby podobna sytuacja dotyczyła innego blockchaina, prawdopodobnie nie wiedziałbym jak upewnić się, że środki wrócą do prawowitych właścicieli.

Moje szczęście tym razem polegało na tym, że po tym jak w lipcu 2016 Steemit został już raz zhakowany (JS injection), efektem było przejęcie kluczy prywatnych z localstorage kilkudziesięciu użytkowników, do sieci Steem zostały wprowadzone pewne zmiany.

Każdy użytkownik dostał m.in. możliwość ustawienia swojego zaufanego recovery-account-partnera, z którego pomocą mógł zmienić wszystkie klucze, posiadając swoje stare hasło. W tym przypadku okazało się to wybawieniem, bowiem z mojej strony upubliczniając wszystko, musiałem tylko przypomnieć użytkownikom, jak z pomocą tego mechanizmu odzyskać dostęp do konta.

W przypadku problemu 2. i 3. napisałem prosty fix sprawdzający formularz po stronie klienta, czy z zawartości pola memo nie można przypadkiem wygenerować pasującego klucza publicznego użytkownika, co świadczyłoby o tym, że użył on swojego hasła, bądź klucza prywatnego.

Do problemu 4. postanowiłem podejść kontaktując się ze wszystkimi delegatami sieci, publicznie znanymi odpowiednikami górników z bitcoina (node’y owych delegatów tworząc nowe blocki najszybciej są w stanie wykryć upublicznione hasła, i zamiast zautoryzować taką transakcje, są w stanie postarać się szybciej autoryzować transakcje własnego autorstwa zabezpieczając konto). Rozwiązanie dalekie od ideału, ale przygotowanie softforka sobie darowałem, nie mając narzędzi do przetestowania go.

Finał i Podsumowanie

7 czerwca miał miejsce finał sprawy. W bardzo krótkim czasie, został:

  1. wysłany email do twórców serwisu Steemit z patchem gotowym do zaaplikowania;
  2. zostały przejęte wszystkie konta, do których dane wisiały sobie w sieci (niektóre od 10 miesięcy);
  3. delegaci całej sieci zostali poinformowani o zdarzeniu i zostali wyposażeni w skrypt mający chronić kolejnych użytkowników, upubliczniających swoje hasła z pomocą giełd;
  4. został opublikowany finalny artykuł w serwisie Steemit, upubliczniający cały wyciek danych.

Ponad $20 000 w kryptowalutach, wróciło już do swoich prawowitych właścicieli. Przyznam szczerze, że o ile “znalezienie kilku haseł” było relatywnie proste, to dużo trudniejsze niż sądziłem było dla mnie upewnienie się, że zostaną zabezpieczone wszystkie “zagrożone” konta.

Autorem gościnnego wpisu jest Krzysztof ‘Noisy” Szumny. Jeśli i Ty chciałbyś podzielić się jakiś doświadczeniem z czytelnikami Niebezpiecznika, skontaktuj się z nami — z przyjemnością opublikujemy Twój artykuł.

Przeczytaj także:

22 komentarzy

Dodaj komentarz
  1. Wow, Zioom mega szacun ;)

    • Dokładnie, że też Ci się chciało tyle narobić za friko :)

  2. Postawa godna naśladowania :)

  3. Nie do końca za friko. Noisy dostał za swój wpis – https://steemit.com/steemit/@noisy/we-just-hacked-11-accounts-on-steemit-1158-sbd-and-8250-steem-is-under-our-control-but-we-are-good-guys-so

    $6,509.95 W krypto walutach bo tak działa ten serwis. Oprócz tego zyskał z pewnością pewną rozpoznawalność i szacunek.

    • Ale, że co? Należało mu się jak psu buda. Trzeba było samemu wykryć jakąś dziurę, naprawić ją za darmo, a jak byłyby z tej pracy jakieś pieniądze, to trzeba było je wydać np. na tacę albo inny bardziej zbożny cel.
      Twój wpis miał coś udowodnić poza typowymi dla Polaka cechami czyli zazdrością i zawiścią?

      Gdy wieczorne zgasną zorze,
      zanim głowę do snu złożę,
      modlitwę moją zanoszę,
      Bogu Ojcu i Synowi.
      Dopierdolcie sąsiadowi!
      Dla siebie o nic nie wnoszę,
      tylko mu dosrajcie, proszę!
      Kto ja jestem?
      Polak mały! Mały, zawistny i podły!
      Jaki znak mój? Krwawe gały!
      Oto wznoszę swoje modły
      do Boga, Maryi i Syna!
      Zniszczcie tego skurwysyna!
      Mojego rodaka, sąsiada,
      tego wroga, tego gada!
      Żeby mu okradli garaż,
      żeby go zdradzała stara,
      żeby mu spalili sklep,
      żeby dostał cegłą w łeb,
      żeby mu się córka z czarnym
      i w ogóle, żeby miał marnie!
      Żeby miał AIDS-a i raka,
      oto modlitwa Polaka!
      Co noc jak 37% Prawdziwyc Polaków odmawiasz tę modlitwę?

    • Moim zdaniem zasłużył na te pieniądze.

    • Ale ja nie mówię ze to źle że zarobił. – Świetnie wręcz.
      Ja chciałem zwrócić uwagę na specyfikę portalu, bo OkropNick napisał ze za friko.

    • A to coś zmienia? Dostał, to dostał i bez znaczenia jest jaka jest specyfika portalu i czy ktoś napisał, że robił za friko. A ty latasz po necie i obalasz nic nieznaczące wpisy.
      Do tego twój wpis umieszczasz ni z gruchy, ni z z pietruchy w głównym wątku, a nie w odpowiedzi na nic nieznaczący wpis.
      Poza tym ani ciebie ani nikogo nie powinno obchodzić czy i ile zarobił.
      Oczywiście jeśli on się sam pochwalił, lub ty się pochwalisz zarobkami, to OK. Wolno wam, ale własnymi.

    • To wiele wyjaśnia..

    • O co wam chodzi ludzie, Adrian napisał prawdę i tyle, a wy się zachowujecie,jakby on chciał mu te pieniądze ukraść… Może lepiej wcale tu nie komentować? Polecam portal Steemit, za pisanie artykułów zarabiamy krypto. Sam korzystam, pozdrawiam.

    • @Dzonszarkrat nie wiem o co Cie sciska, bo rzucasz sie jak wsciekly pies.
      Adrian napisal co sie stalo – i szacun, bo bez niego myslalbym, ze noisy robil za friko. Dzieki Adrianowi wiem, ze uczciwi ludzie jeszcze sa czasami oplacani na tym swiecie. Od Ciebie wiem, ze jestes marnym poeta ze sciskiem nie wiadomo gdzie…

  4. Jestem pod wrażeniem, brawo Ty!

  5. Dobra robota!

  6. No no Noisy, szacun! :D

  7. Gratulacje!

  8. I co, koleś nie poszedł za to siedzieć? W Polsce mieszka?

  9. swietna robota. gratulacje!

  10. Czapki z głów.

  11. Niezłą jatkę urządziłeś tam kolego. Pełen szacuneczek dla Ciebie!.

  12. To kolejny przykład na to, że jeśli jesteś dobrym człowiekiem i czynisz dobro, to wróci do Ciebie ze zdwojoną siłą…Brawo Krzysztof :-)

    • A ja znam tak: każde wyrządzone dobro zostanie surowo ukarane na tym podłym świecie. miałem okazję się o tym przekonać osobiście…

  13. Szacun

Twój komentarz

Zamieszczając komentarz akceptujesz regulamin dodawania komentarzy. Przez moderację nie przejdą: wycieczki osobiste, komentarze nie na temat, wulgaryzmy.

RSS dla komentarzy: