Honggfuzz – Fast Track

Słowo wstępu

Nie będę ukrywał, że honggfuzz był długo przeze mnie ignorowanyAFL działał dobrze, a tam gdzie nie domagał, korzystałem z LibFuzzera (tutaj część pierwsza i druga jego fast tracka). Nawet nie pomagały błędy, które za jego pomocą zostały odkryte – na przykład jedyny do tej pory krytyczny błąd w OpenSSL. Dopiero zachęcony przez Kamila Rytarowskiego, który w ramach GSOC 18 integrował różnej maści fuzzery z NetBSD, postanowiłem dać mu szansę.

Efekt? Po niecałych 20 godzinach fuzzingu radare2, honggfuzz znalazł 893 awaryjne przypadki testowe (z czego 23 są unikalne). Testy zakończyły się lepszym wynikiem niż się spodziewałem, zwracam honor twórcy narzędzia 🙂

O co chodzi?

Honggfuzz bardzo fajnie integruje ze sobą dwa podejścia do fuzzingu, reprezentowane przez AFL oraz LibFuzzera – możemy pisać własne fuzzery (honggfuzz rozumie funkcje wykorzystywane przez LibFuzzera, a przy okazji nie pożera olbrzymich ilości pamięci RAM), jak również skompilować binarki za pomocą hfuzz-clang i korzystać z programów testowych w projekcie poddawanym badaniu (“styl” AFL).

Moją ulubioną funkcjonalnością, której nie ma żaden z wyżej opisanych fuzzerów jest wykorzystanie technologii Intel Processor Trace oraz Intel Branch Trace Store do maksymalizacji pokrycia kodu podczas fuzzingu. Efekty są bardzo fajne i nie są obarczone upierdliwymi wymaganiami. Wystarczy kompilacja biblioteki libipt oraz posiadanie względnie nowego (po 2013 roku) procesora Intel.

Oczywiście to nie jest koniec “dobrego”, poza tym honggfuzz oferuje:

  • Persistent mode (podobny do AFL),
  • Możliwość startu bez korpusu początkowego,
  • Monitoring sygnałów za pomocą np. ptrace, co pozwala zwiększyć wykrywalność sygnału zakończenia danej iteracji fuzzingu, a przez to skuteczniej wykrywać awarie,
  • Podpinanie się do danego procesu – przydatne podczas fuzzingu usług sieciowych.

Jak odpalić i zacząć fuzzing?

Bardzo prosto (Ubuntu 16.04+):

sudo apt-get install libbfd-dev libunwind-dev
git clone https://github.com/google/honggfuzz
cd honggfuzz
make -j4
sudo make install 

Kompilacja (w tym ASAN i instrumentacja) na przykładzie radare2:

git clone https://github.com/radare/radare2
cd radare2
CC=hfuzz-clang CXX=hfuzz-clang++ ./configure
make -j4
sudo make install 

Pozyskanie korpusu testowego (można zacząć z pominięciem tego kroku, lecz nie polecam fuzzować “na pusto”)

git clone https://github.com/radare/radare2-regressions
cd radare2-regressions
mkdir ~/r2_corpus
mv bins/fuzzed/* ~/r2_corpus

Odpalenie fuzzera z maksymalizacją pokrycia za pomocą IPT:

mkdir ~/r2_fuzzing_output
honggfuzz --linux_perf_ipt_block -W ~/r2_fuzzing_output -f ~/r2_corpus -- /usr/local/bin/r2 -A ___FILE___

Po odpaleniu naszym oczom zostanie wyświetlony całkiem przyjemny interfejs tekstowy (bardziej czytelny niż w AFL):

------------------------[  0 days 23 hrs 03 mins 57 secs ]----------------------
  Iterations : 19,517 [19.52k]
        Mode : [2/2] Feedback Driven Mode
      Target : /usr/local/bin/r2 -A ___FILE___
     Threads : 1, CPUs: 1, CPU%: 100% [100%/CPU]
       Speed : 0/sec [avg: 0]
     Crashes : 1014 [unique: 23, blacklist: 0, verified: 0]
    Timeouts : 5,796 [10 sec]
 Corpus Size : 2,312, max size: 789,488 bytes, init dir: 417 files
  Cov Update : 0 days 00 hrs 02 mins 55 secs ago
    Coverage : edge: 64,269 pc: 1,570 cmp: 987,526
---------------------------------- [ LOGS ] ------------------/ honggfuzz 1.6 /-
 2018-08-11T20:52:29+0200][W][28659] subproc_checkTimeLimit():425 PID 52926 took too much time (limit 10 s). Killing it with SIGKILL
[2018-08-11T20:52:40+0200][W][28659] subproc_checkTimeLimit():425 PID 52928 took too much time (limit 10 s). Killing it with SIGKILL
[2018-08-11T20:52:50+0200][W][28659] subproc_checkTimeLimit():425 PID 52929 took too much time (limit 10 s). Killing it with SIGKILL
[2018-08-11T20:53:00+0200][W][28659] subproc_checkTimeLimit():425 PID 52930 took too much time (limit 10 s). Killing it with SIGKILL
[2018-08-11T20:53:10+0200][W][28659] subproc_checkTimeLimit():425 PID 52943 took too much time (limit 10 s). Killing it with SIGKILL
[2018-08-11T20:53:20+0200][W][28659] subproc_checkTimeLimit():425 PID 52944 took too much time (limit 10 s). Killing it with SIGKILL
[2018-08-11T20:53:30+0200][W][28659] subproc_checkTimeLimit():425 PID 52945 took too much time (limit 10 s). Killing it with SIGKILL
[2018-08-11T20:53:52+0200][W][28659] subproc_checkTimeLimit():425 PID 52952 took too much time (limit 10 s). Killing it with SIGKILL
[2018-08-11T20:54:03+0200][W][28659] subproc_checkTimeLimit():425 PID 52955 took too much time (limit 10 s). Killing it with SIGKILL

Maksymalizacja efektów fuzzingu

Krótkie przypomnienie jak uczynić proces jeszcze bardziej efektywnym:

  • Korpus testowy złożony z niewielkich plików (idealnie poniżej 1 kilobajta), które są różne funkcjonalnie,
  • Wykorzystanie Persistent Mode,
  • Budowa i użycie słowników (te z AFL również są obsługiwane przez honggfuzz),
  • Wspomaganie powiększania pokrycia kodu za pomocą IPT / BTS,
  • Oczywiście fuzzing tylko z ASAN / MSAN / UBSAN.

Podsumowanie

Jeżeli, drogi Czytelniku, zintegrowałeś swój projekt z LibFuzzerem to nic nie stoi na przeszkodzie aby wykorzystać go w połączeniu z honggfuzz – wystarczy jedynie zmienić kompilator podczas procesu budowania. A jeżeli nie, to wszystko przed Tobą – osobiście uważam, że ten fuzzer jako narzędzie w procesie usuwania błędów dodaje olbrzymią wartość i jest mi wstyd, że tak późno go odkryłem 😉

Automatyczne wyszukiwanie podatności bezpieczeństwa na dużą skalę

Mimo tego, że długo nie pojawiałem się na blogu to nie próżnowałem: popełniłem wpis na blogu CERT Polska, opisujący wykorzystywane przeze mnie metody i rozwiązania do detekcji podatności bezpieczeństwa 🙂

Jest to tekstowe rozszerzenie mojej prezentacji z Confidence 2018. Poniżej zajawka:

Problematyka podatności bezpieczeństwa dotyczy praktycznie wszystkich programów lub bibliotek wykorzystywanych w codziennej pracy. Proaktywność w tej kwestii jest jednym z kluczy do dostarczania wysokiej jakości oprogramowania, które zapewni spokojny sen osobom zajmującym się bezpieczeństwem IT.

W ciągu ostatnich dwóch lat firma Google za pomocą inicjatywy OSS-Fuzzwykryła oraz zgłosiła ponad osiem tysięcy błędów w szerokim spektrum projektów open source, m.in. Tor Browser, ImageMagick czy FreeType2.

CERT Polska również pracuje nad rozwojem mechanizmów i wyszukiwaniem podatności w projektach wykorzystywanych szeroko w Internecie. W tym momencie równolegle testujemy 12 projektów z 56 obsługiwanych przez nasz system.

Efekt? Znaleźliśmy prawie 500 błędów w oprogramowaniu open source, z czego ponad 130 miało wpływ na bezpieczeństwo i otrzymało numer podatności CVE.

Zachęcam do zapoznania się z całością tekstu oraz slajdami 🙂

Zaproszenie na Confidence 2018

Drugi post z rzędu z zaproszeniem na konferencje – tym razem krakowski Confidence 2018 🙂 Jestem szczerze przekonany, że eventu nie trzeba nikomu przedstawiać – jest to jedna z najstarszych i najpopularniejszych konferencji poświęconych bezpieczeństwu IT, odbywająca się regularnie na przełomie maja i czerwca (w tym roku: 04 – 05.06.2018).

Co? Jak? Dlaczego?

Wśród prelegentów pojawią się uznane marki z takich gigantów jak jak: Mozilla, Microsoft czy Positive Technologies – reprezentowane przez Michała PurzyńskiegoAdama “pi3” ZabrockiegoIdo Naora oraz Maxima Goryachy’ego. Również znalazłem się w gronie prelegentów i opowiem o autorskim systemie “Cloudfuzz”, który pomógł mi w znalezieniu prawie 400 błędów w oprogramowaniu open-source – część z nich została opisana na blogu 🙂

Moja roadmapa konferencji

Swoją obecność zaplanowałem na następujących sesjach – aczkolwiek lista może się zmienić:

Nie pozostaje mi nic innego jak życzyć owocnej konferencji i do zobaczenia w Krakowie! 🙂

Zaproszenie na SECURE Early Bird 2018

Dla każdego, kto chce poczuć przedsmak jesiennej edycji SECURE 2018, CERT Polska / NASK przygotował jednodniowe wydarzenie w nieco luźniejszym stylu – SECURE Early Bird 2018. Odbędzie się ono 23 maja w warszawskim centrum konferencyjnym Adgar Plaza – event jest całkowicie za darmo!

Zapowiedź ze strony:

SECURE Early Bird to nasza wiosenna jaskółka, krótki zwiastun jesiennej konferencji SECURE 2018. Przygotowaliśmy dla Was solidną porcję eksperckiej wiedzy w pigułce – konkretne problemy i propozycje technicznych rozwiązań od osób na co dzień zajmujących się walką z zagrożeniami.
I to całkowicie za darmo!

Kogo będzie można spotkać?

Prelegentami będą: Carsten Willems z VMRay, Jarek Jedynak z Google, Tomek Bukowski z Banku Millenium oraz ja jako reprezentacja CERT.PL.

W swojej prezentacji opowiem o różnych błędach (część z nich opisywałem na blogu) znalezionych we wszelakiej maści narzędziach analitycznych (lub bibliotekach) użytecznych przy analizie malware. Wiele podatności to bardzo podstawowe błędy, które, chyba tylko z pomocą promieniowania kosmicznego znalazły się w kodzie źródłowym 🙂

Program

  • Carsten Willems – Countering Innovative Sandbox Evasion Techniques Used by Malware
  • Jarek Jedynak – mquery, czyli jak znaleźć malware w morzu próbek. Indeksowanie i wyszukiwanie złośliwego oprogramowania
  • Kamil Frankowicz – Niebezpieczne przygody podczas analizy binarek – trochę o fuckupach narzędzi analitycznych
  • Tomek Bukowski – TBA

Zapraszam do rejestracji i do zobaczenia! 🙂

 

pwnable.kr – fenomenalna platforma do nauki exploitacji & CTF

Ostatnio dużo czasu spędzam na rozwiązywaniu zadań z CTF i praktykowaniu różnych metod exploitacji. Jeden z moich współpracowników (dzięki Mateusz!) polecił mi pwnable.kr jako ciekawe uzupełnienie kursu Modern Binary Exploitation z którego korzystałem (i bardzo polecam 🙂 ).

Platforma zachęca do siebie 64 zadaniami o czterech poziomach trudności oraz prawie 16000 użytkowników – niestety większość z nich “wymięka” po pierwszym lub drugim zadaniu (jeżeli Czytelniku będziesz chciał zrobić to samo polecam przeczytanie tego posta Ivana Fratrica).

Aby usprawnić zdobywanie wiedzy Czytelników jak i swoje postanowiłem stworzyć repozytorium z opisami rozwiązań zadań. Na chwilę pisania posta zawiera ono jedynie notatki z czterech zadań, lecz założyłem sobie, że po każdym rozwiązanym przeze mnie zadaniu będę coś wrzucał od siebie na GitHuba. Każdy PR oraz issue mile widziane 🙂

PS. Jeżeli Czytelniku przemawia do Ciebie bardziej forma wideo to polecam kanał YT LiveOverflow oraz blog.

Ponce – Symbolic Execution dla “klikaczy”

Osobiście lubię “zwalać” pracę na mój procesor. Zwłaszcza nudne i powtarzalne czynności bo często w nich strzelam babole, które skutkują jeszcze większymi błędami na wyjściu. Fajnym dodatkiem do tego wszystkiego jest sensowna “klikalność” rozwiązania – sensowność rozumiem w ten sposób, że czynność mogę zautomatyzować za pomocą trzech kliklnięć a nie trzydziestu trzech.

Wszystkie wymienione wyżej warunki spełnia Ponce, czyli plugin do IDA’y pozwalający wyklikać sobie zadane zmienne symbolic execution / taint analysis i rozwiązać odpowiednio skonstruowane równanie. W moim mniemaniu jest to bardzo fajne narzędzie do wszelakich zadań CTF które dają się rozwiązać w prosty sposób a brakuje czasu aby to zrobić 🙂

W tym poście przedstawię rozwiązanie zadania rozgrzewkowego RE, z wrześniowego CTF Ekoparty za pomocą Ponce.

Rozwiązanie zadania

Od ogranizatorów otrzymujemy statycznie skompilowaną pod x64 binarkę ELF:

a@b:~/Desktop$ file warmup 
warmup: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=b9e93849cf9889813b7f88f5b7a0609df69bbf48, stripped

Zadanie jest klasycznym problemem do rozwiązania za pomocą symbolic execution – poniżej długi listing zdekompilowanej funkcji:

signed __int64 sub_4009D8()
{
  signed __int64 result; // rax@29

  if ( unk_6CCD62 == aO[0] )
  {
    if ( unk_6CCD72 == a_[0] )
    {
      if ( unk_6CCD6D == aU[0] )
      {
        if ( unk_6CCD67 == aT[0] )
        {
          if ( unk_6CCD69 == a1[0] )
          {
            if ( unk_6CCD63 == asc_4A1725[0] )
            {
              if ( unk_6CCD6C == aJ[0] )
              {
                if ( unk_6CCD65 == aS_0[0] )
                {
                  if ( unk_6CCD68 == asc_4A172B[0] )
                  {
                    if ( unk_6CCD64 == a1[0] )
                    {
                      if ( unk_6CCD6A == aS_0[0] )
                      {
                        if ( unk_6CCD6B == a_[0] )
                        {
                          if ( unk_6CCD66 == a_[0] )
                          {
                            if ( unk_6CCD7A == a_[0] )
                            {
                              if ( unk_6CCD6E == a5[0] )
                              {
                                if ( unk_6CCD6F == aT[0] )
                                {
                                  if ( unk_6CCD70 == a_[0] )
                                  {
                                    if ( unk_6CCD71 == off_4A1731 )
                                    {
                                      if ( unk_6CCD61 == BYTE2(off_4A1731) )
                                      {
                                        if ( unk_6CCD73 == unk_4A1735 )
                                        {
                                          if ( unk_6CCD74 == off_4A1731 )
                                          {
                                            if ( unk_6CCD75 == unk_4A1737 )
                                            {
                                              if ( unk_6CCD76 == unk_4A1739 )
                                              {
                                                if ( unk_6CCD77 == a_[0] )
                                                {
                                                  if ( unk_6CCD78 == aU[0] )
                                                  {
                                                    if ( unk_6CCD79 == off_4A173B )
                                                    {
                                                      if ( unk_6CCD60 == BYTE2(off_4A173B) )
                                                      {
                                                        if ( unk_6CCD7B == unk_4A173F )
                                                        {
                                                          sub_4101B0("valid!");
                                                          result = 1LL;
                                                        }
                                                        else
                                                        {
                                                          result = 0LL;
                                                        }
                                                      }
                                                      else
                                                      {
                                                        result = 0LL;
                                                      }
                                                    }
                                                    else
                                                    {
                                                      result = 0LL;
                                                    }
                                                  }
                                                  else
                                                  {
                                                    result = 0LL;
                                                  }
                                                }
                                                else
                                                {
                                                  result = 0LL;
                                                }
                                              }
                                              else
                                              {
                                                result = 0LL;
                                              }
                                            }
                                            else
                                            {
                                              result = 0LL;
                                            }
                                          }
                                          else
                                          {
                                            result = 0LL;
                                          }
                                        }
                                        else
                                        {
                                          result = 0LL;
                                        }
                                      }
                                      else
                                      {
                                        result = 0LL;
                                      }
                                    }
                                    else
                                    {
                                      result = 0LL;
                                    }
                                  }
                                  else
                                  {
                                    result = 0LL;
                                  }
                                }
                                else
                                {
                                  result = 0LL;
                                }
                              }
                              else
                              {
                                result = 0LL;
                              }
                            }
                            else
                            {
                              result = 0LL;
                            }
                          }
                          else
                          {
                            result = 0LL;
                          }
                        }
                        else
                        {
                          result = 0LL;
                        }
                      }
                      else
                      {
                        result = 0LL;
                      }
                    }
                    else
                    {
                      result = 0LL;
                    }
                  }
                  else
                  {
                    result = 0LL;
                  }
                }
                else
                {
                  result = 0LL;
                }
              }
              else
              {
                result = 0LL;
              }
            }
            else
            {
              result = 0LL;
            }
          }
          else
          {
            result = 0LL;
          }
        }
        else
        {
          result = 0LL;
        }
      }
      else
      {
        result = 0LL;
      }
    }
    else
    {
      result = 0LL;
    }
  }
  else
  {
    result = 0LL;
  }
  return result;
}

Naszym zadaniem jest zlokalizowanie bufora wejściowego i oznaczenie go jako symbolizowanej pamięci. Można to zrobić w prosty sposób, wpisując dowolną wartość i lokalizując ją w pamięci na początku wykonania funkcji sprawdzającej. W moim przypadku inputem będzie EKO{AAAAAAAAAAAAAAAAAAAAAAA}.

Znaleziony bufor rezyduje w pamięci pod adresem 0x06CCD60. Taki adres, wraz z rozmiarem bufora (26) wpisujemy w okienku dialogowym Ponce po wybraniu opcji “Symbolize Memory” (pomarańczowa strzałka, czerwona pokazuje log z Ponce):

Po wskazaniu Ponce co nas interesuje, przechodzimy do dalszego wykonania naszego zadania. Ponce zaznacza na grafie symbolizowane odwołania do pamięci na jasnozielono, wraz z komentarzem.

Ostatnim krokiem jest wyklikanie rozwiązania solvera SMT (i tak do ostatniego znaku flagi 😉 ). W moim wypadku klikam prawym przyciskiem myszy na warunek jaki musi być spełniony, aby przejść do interesującego mnie brancha (czerwona strzałka) i wybieram opcję: SMT -> Solve formula:

W okienku Output pojawią się rozwiązania zadanego równania (na screenie rozwiązanie dla literki “O” z flagi):

Klikając tak kilkanaście razy i składając literki do kupy, naszym oczom ukaże się rozwiązanie zadania: EKO{1s_th1s_ju5t_4_w4rm_up?}.

PWNing 2017 – Rozwiązanie zadania RE150 “Military grade algorithm” za pomocą Manticore

Początek listopada przyniósł kolejną świetną konferencję – Security PWNing Conference 2017. W tym roku miałem przyjemność współtworzyć z zespołem P4 konkurs CTF organizowany w ramach tej konferencji. Poniżej zaprezentuję rozwiązanie mojego zadania z RE, wycenianego na 150 punktów. W trakcie trwania konkursu rozwiązało je dwanaście osób.

Clue zadania był szereg odwracalnych, lecz najzwyczajniej w świecie upierdliwych do ręcznego liczenia operacji (dodawanie, odejmowanie, mnożenie oraz xor). Zadanie to było modelowym problemem do zaprzęgnięcia Symbolic Execution, co ninejszym uczynię z pomocą bardzo interesującego frameworka Manticore.

Reverse engineering binarki

Poza enigmatycznym opisem zadania: “Dane Cyberlandii chroni nowy autorski algorytm o sile “military grade”. Czy podołasz zadaniu i go złamiesz?“, otrzymujemy niewielką binarkę w formacie ELF:

a@b:~/Downloads$ file re3 
re3: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=28f523af671d756a9abb946580d040a60acb486e, not stripped

Wrzućmy ją do IDA’y i zajrzyjmy do main():

Logika programu wygląda prosto: od użytkownika pobierana jest flaga za pomocą funkcji fgets i przekazywana do funkcji check_all po zdekompilowaniu wyglądająca w następujący sposób:

Widać wyraźnie, że każdej literze flagi “przypisana” jest funkcja sprawdzająca konkretną literę. Poniżej listing funkcji check_flag_17:

Skoro już wiemy z jakim problemem przyjdzie się nam zmierzyć, przystępujemy do działania 🙂

Łączymy kropki: RE + Manticore

Pierwszym problemem do pokonania jest pobieranie danych do programu – musimy w jakiś sposób zasymulować wprowadzanie danych przez użytkownika za pomocą skryptu. Z racji tego, że mamy pełną kontrolę nad wykonaniem kodu, możemy “przeskoczyć” niewygodny fragment kodu za pomocą ręcznego ustawienia rejestru RIP.

Ze screenshota można odczytać, że pierwsza instrukcja asma przed wyciętym fragmentem znajduje się pod adresem 0x40104E, a ostatnia pod 0x40106B. Z boku zapisujemy sobie również, że wywołanie funkcji check_all() jest pod adresem 0x401072.

Pierwszą rzeczą, którą musimy zrobić jest hook adresu 0x40104E i wpisanie w RIP wartości 0x40106B. Drugą jest zauważenie (albo statycznie, albo w debuggerze), że pod adresem 0x40106F w rejestrze RDI znajduje się adres wpisywanej flagi (oznaczony czerwoną strzałką).

Fragment kodu odpowiedzialny za tą operacje:

from manticore import Manticore

flag = None
m = Manticore('re3')

# "Przeskok" nad funkcją fgets()
@m.hook(0x40104E)
def hook(state):
    state.cpu.EIP = 0x40106B

# Pobranie adresu z rejestru RDI i stworzenie bufora dla symbolic execution wraz z zapisem danych do niego
@m.hook(0x401072)
def hook(state):
    global flag
    flag = state.cpu.RDI
    buffer = state.new_symbolic_buffer(34)
    state.cpu.write_bytes(flag, buffer)

Skoro mamy już załatwioną sprawę funkcji gets() oraz adresu bufora w którym przechowywana jest flaga, nie pozostaje nic innego jak kazać Manticore samodzielnie rozwiązać zadanie.

W tym celu musimy znaleźć adres końca funkcji wywołującej po kolei funkcje do sprawdzania poszczególnych znaków flagi, czyli check_all(). A konkretniej: nteresuje nas fragment przed instrukcją ret i oczywiście leave też 😉 :

Wiedząc, że jest to adres 0x40102B możemy w tym miejscu rozpocząć rozwiązywanie flagi za pomocą poniższego fragmentu kodu:

from manticore import Manticore

flag = None
m = Manticore('re3')

# "Przeskok" nad funkcją fgets()
@m.hook(0x40104E)
def hook(state):
    state.cpu.EIP = 0x40106B

# Pobranie adresu z rejestru RDI i stworzenie bufora dla symbolic execution wraz z zapisem danych do niego
@m.hook(0x401072)
def hook(state):
    global flag
    flag = state.cpu.RDI
    buffer = state.new_symbolic_buffer(34)
    state.cpu.write_bytes(flag, buffer)

# Rozwiązanie flagi w miejscu końca funkcji check_all()
@m.hook(0x40102B)
def hook(state):
    flag = ''.join(map(chr, state.solve_buffer(flag_buffer, 34)))
    print(flag)
    state.abandon()

# Porzucenie aktualnego stanu, tak aby nie rozwiązywać w nieskończoność: hook dla funkcji exit()
def exit_hook(state):
    state.abandon()

W poprzednim kroku “przemyciłem” też ważny fragment kodu – obsłużenie wywołania funkcji exit(), które zakończy operacje na poszukiwanym buforze. W tym celu musimy zrobić hooka na wszystkie wywołania funkcji exit(). Niestety nie jest to zapewnione z automatu i trzeba o tym pamiętać, aby w końcu dostać upragnioną flagę 😉

Informację pod jakimi adresami jest wywoływana funkcja exit() możemy znaleźć za pomocą polecenia: objdump -d re3 | grep exit

a@b:~/Downloads$ objdump -d re3 | grep exit
  4004e8:	e8 73 00 00 00       	callq  400560 <exit@plt+0x10>
0000000000400550 <exit@plt>:
  400685:	e8 c6 fe ff ff       	callq  400550 <exit@plt>
  4006b8:	e8 93 fe ff ff       	callq  400550 <exit@plt>
  4006ea:	e8 61 fe ff ff       	callq  400550 <exit@plt>
  40071a:	e8 31 fe ff ff       	callq  400550 <exit@plt>
  40074c:	e8 ff fd ff ff       	callq  400550 <exit@plt>
  400782:	e8 c9 fd ff ff       	callq  400550 <exit@plt>
  4007b5:	e8 96 fd ff ff       	callq  400550 <exit@plt>
  4007eb:	e8 60 fd ff ff       	callq  400550 <exit@plt>
  400824:	e8 27 fd ff ff       	callq  400550 <exit@plt>
  40085e:	e8 ed fc ff ff       	callq  400550 <exit@plt>
  400894:	e8 b7 fc ff ff       	callq  400550 <exit@plt>
  4008cd:	e8 7e fc ff ff       	callq  400550 <exit@plt>
  400908:	e8 43 fc ff ff       	callq  400550 <exit@plt>
  400941:	e8 0a fc ff ff       	callq  400550 <exit@plt>
  40097a:	e8 d1 fb ff ff       	callq  400550 <exit@plt>
  4009b0:	e8 9b fb ff ff       	callq  400550 <exit@plt>
  4009e2:	e8 69 fb ff ff       	callq  400550 <exit@plt>
  400a1b:	e8 30 fb ff ff       	callq  400550 <exit@plt>
  400a4b:	e8 00 fb ff ff       	callq  400550 <exit@plt>
  400a84:	e8 c7 fa ff ff       	callq  400550 <exit@plt>
  400ab7:	e8 94 fa ff ff       	callq  400550 <exit@plt>
  400af6:	e8 55 fa ff ff       	callq  400550 <exit@plt>
  400b2b:	e8 20 fa ff ff       	callq  400550 <exit@plt>
  400b64:	e8 e7 f9 ff ff       	callq  400550 <exit@plt>
  400b9a:	e8 b1 f9 ff ff       	callq  400550 <exit@plt>
  400bcd:	e8 7e f9 ff ff       	callq  400550 <exit@plt>
  400c03:	e8 48 f9 ff ff       	callq  400550 <exit@plt>
  400c41:	e8 0a f9 ff ff       	callq  400550 <exit@plt>
  400c7a:	e8 d1 f8 ff ff       	callq  400550 <exit@plt>
  400cad:	e8 9e f8 ff ff       	callq  400550 <exit@plt>
  400ce7:	e8 64 f8 ff ff       	callq  400550 <exit@plt>
  400d23:	e8 28 f8 ff ff       	callq  400550 <exit@plt>
  400d5d:	e8 ee f7 ff ff       	callq  400550 <exit@plt>

Parsowanie tego outputu możemy dodać do naszego skryptu w następujący sposób:

from manticore import Manticore

flag = None
m = Manticore('re3')

# Parsowanie outputu z objdump, celem wyciągnięcia adresów wywołania funkcji exit()
def get_exits():
    def addr(line):
        return int(line.split()[0][:-1], 16)

    exits_disasm = check_output("objdump -d re_symbolic | grep exit", shell=True)
    exits = [addr(line) for line in exits_disasm.split('\n')[2:-1]]
    for e in exits:
        yield e


# "Przeskok" nad funkcją fgets()
@m.hook(0x40104E)
def hook(state):
    state.cpu.EIP = 0x40106B

# Pobranie adresu z rejestru RDI i stworzenie bufora dla symbolic execution wraz z zapisem danych do niego
@m.hook(0x401072)
def hook(state):
    global flag
    flag = state.cpu.RDI
    buffer = state.new_symbolic_buffer(34)
    state.cpu.write_bytes(flag, buffer)

# Rozwiązanie flagi w miejscu końca funkcji check_all()
@m.hook(0x40102B)
def hook(state):
    flag = ''.join(map(chr, state.solve_buffer(flag_buffer, 34)))
    print(flag)
    state.abandon()

# Porzucenie aktualnego stanu, tak aby nie rozwiązywać w nieskończoność: hook dla funkcji exit()
def exit_hook(state):
    state.abandon()

# Dodanie hooków na wszyskie wywołania funkcji exit()
for index, exit in enumerate(get_exits()):
    m.add_hook(exit, exit_hook)

Ostatnim krokiem jest skonfigurowanie workerów obiektu Manticore i uruchomienie analizy:

from manticore import Manticore

flag = None
m = Manticore('re3')

# Parsowanie outputu z objdump, celem wyciągnięcia adresów wywołania funkcji exit()
def get_exits():
    def addr(line):
        return int(line.split()[0][:-1], 16)

    exits_disasm = check_output("objdump -d re_symbolic | grep exit", shell=True)
    exits = [addr(line) for line in exits_disasm.split('\n')[2:-1]]
    for e in exits:
        yield e


# "Przeskok" nad funkcją fgets()
@m.hook(0x40104E)
def hook(state):
    state.cpu.EIP = 0x40106B

# Pobranie adresu z rejestru RDI i stworzenie bufora dla symbolic execution wraz z zapisem danych do niego
@m.hook(0x401072)
def hook(state):
    global flag
    flag = state.cpu.RDI
    buffer = state.new_symbolic_buffer(34)
    state.cpu.write_bytes(flag, buffer)

# Rozwiązanie flagi w miejscu końca funkcji check_all()
@m.hook(0x40102B)
def hook(state):
    flag = ''.join(map(chr, state.solve_buffer(flag_buffer, 34)))
    print(flag)
    state.abandon()

# Porzucenie aktualnego stanu, tak aby nie rozwiązywać w nieskończoność: hook dla funkcji exit()
def exit_hook(state):
    state.abandon()

# Dodanie hooków na wszyskie wywołania funkcji exit()
for index, exit in enumerate(get_exits()):
    m.add_hook(exit, exit_hook)

# "Gadatliwość" Manticore
m.verbosity = 0
# 1 worker = 1 core w CPU
m.workers = 4
# Wewnętrzna optymalizacja działania Manticore
m.should_profile = True
# Uruchomienie analizy
m.run()

Skrypt po około minucie działania na VM (4 wirtualne procesory) wyświetli nam poszukiwaną flagę:

a@b:~/Downloads$ python re3_solver.py 
pwn{symbolic_execution_4_the_win}

Security BSides Warsaw 2017

Tradycją staje się, że co roku występuję na Security Bsides Warsaw 🙂 Podczas tegorocznej opowiadałem o lessons learned i efektach projektu, który prowadzę już rok: fuzzowania różnych projektów open-source.

Jak w poprzednim roku, Cooper zapewnił świetny streaming i każdą nagrywaną sesję można obejrzeć na kanale YT wydarzenia.

Tutaj znajduje się moja prezentacja, natomiast poniżej możesz obejrzeć nagranie z mojej sesji:

Zaproszenie na Security BSides Warsaw 2017

Tradycją już jest, że każdej jesieni, w okolicach połowy października spotykają się ze sobą IT Sec junkies w przyciasnej sali konferencyjnej, warszawskiej knajpy Państwo Miasto – mowa oczywiście o kolejnej odsłonie najlepszej i darmowej konferencji entuzjastów bezpieczeństwa Security BSides Warsaw 🙂

W poprzednim roku opowiadałem o procesie fuzzowania za pomocą American Fuzzy Lop. Podczas tegorocznej edycji wydarzenia tematem mojej prezentacji będzie podsumowanie, trwającego już ponad rok, projektu fuzzowania rozwiązań open-source.

Skupię się nie tylko na technicznych aspektach samego procesu fuzzingu i wyciągniętych wnioskach, lecz również na współpracy z deweloperami oraz rozszerzeniu procesu typowego CI. Wisienką na torcie będą nietypowe problemy znalezione na przestrzeni roku.

Jestem również pewien, że moi koledzy z pracy: Maciek Kotowicz tematem “Podobieństwa i różnice czyli analiza malware’u dla leniwych” oraz Jarek JedynakJak źle użyć kryptografii” zrobią niezłe show 🙂

Zapraszam na nasze prezentacje (piątek 13.10.2017, od 13:30 do 16:30) 🙂

tcpdump 4.9.0 & 4.9.1 – 15 x CVE

Z racji okresu urlopowego i wakacji, niewiele działo się na blogu – zaległości zaczynam odrabiać tym wpisem 🙂

Na początku września została wydana wersja 4.9.2 popularnego narzędzia do przechwytywania pakietów tcpdump, łatająca ponad 85 podatności bezpieczeństwa.

Moim “łupem” w tej niemałej puli było 15 poniższych CVE: