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:

Podatności od “drugiej” strony – Windows patch diffing

Deweloperzy systemu operacyjnego Windows w tym roku nie mają łatwego życia – sama “dziesiątka” od początku roku uzbierała ponad 150 CVE. Microsoft, swoim zwyczajem wydaje poprawki w drugi wtorek każdego miesiąca (słynne “patch tuesday”). Dzień ten wypadł akurat przedwczoraj – więc stąd wpis nieco odmienny od głównej tematyki bloga 😉

Biuletyny bezpieczeństwa często bardzo enigmatycznie opisują co było zepsute i co zostało naprawione – pozostaje liczyć na writeupy badaczy (głównie Google Project Zero) i czasami Microsoft (tutaj drugi post).

Wiedza ta może się przydać nie tylko do pisania exploitów i identyfikacji słabości softu, lecz również do patchowania starszych systemów (polecam zapoznać się z bardzo fajną inicjatywą o nazwie 0patch).

Od czego zacząć i co dalej?

Od czekania na patch tuesday 😉 Kiedy już pojawią się poprawki możemy wytypować interesujący nas błąd i pobrać do niego poprawkę (w przypadku Windows 10 nie jest to takie proste – zazwyczaj wydawane są tzw. Cumulative Update, które zawierają poprawki dla wielu różnych luk oraz nowe funkcjonalności).

Warto trzymać się zasady, że patche reversujemy na najstarszej możliwej wersji systemu – będzie po prostu łatwiej 🙂

Wszelkie kroki będę pokazywał z wykorzystaniem błędu CVE-2017-8463 (na Windows 10) poprawionego w ostatnim cyklu poprawek.

1. Pobieramy poprawkę z Windows Update Catalog. Możemy spróbować wypakować ją (format MSU / CAB) ręcznie za pomocą polecenia:

expand -F:* <msu/cab> <dest_folder>

2. Jest to bardzo niewygodne w przypadku Cumulative Update – w zamian polecam skrypt PatchExtact Grega Linaresa‏ (na wszelki wypadek: mój mirror).

Opdalenie jest bardzo proste i sprowadza się jedynie do podania ścieżek:

Powershell -ExecutionPolicy Bypass -File .\PatchExtract.ps1 -Patch .\windows10.0-kb4025338-x64_c99d7772ad0f6f3340a438d2e82eba7e5c3b2fca.msu -Path C:\Users\XYZ\Desktop\PATCH_TUESDAY_120717

3. Po około 15-20 minutach skrypt posprząta po sobie i uporządkuje nam pliki w strukturze katalogów jak na screenie:

Ze wszystkich folderów interesują nas tylko foldery: x86 oraz x64 – w nich będziemy poszukiwać swieżo poprawionych plików:

4. Wyszukujemy folder z interesującą binarką – w naszym wypadku poprawionym plikiem explorer.exe. Dla własnej wygody polecam zmienić nazwę pliku i przekopiować niezaktualizowany plik w inne miejsce.

5. Instalujemy Zynamic BinDiff (jeżeli jest za ciężki to polecam Diaphorę) i ładujemy obydwa pliki do IDA Pro, celem stworzenia baz IDB, potrzebnych do porównania binarnego.

W tym miejscu warto napisać o dwóch rzeczach:

  1. Polecam pobierać symbole za pomocą PDBDownloader (bardzo ułatwia pracę!)
  2. Należy przygotować się na pracę bez symboli i z dużą ilością “śmieci” które będa zaciemniać obraz sytuacji

6. Skrót klawiszowy CTRL+6 wyzwoli uruchomienie okienka BinDiffa z którego wybieramy opcje: Diff Database i wskazujemy bazę IDA niepatchowanej binarki

7. Po około kilkunastu sekundach pojawią się wyniki analizy – najbardziej interesującą zakładką z perspektywy analizy jest Matched Functions (zaznaczona na pomarańczowo). Najszybszym podejściem jest rozpoczęcie pracy od dwóch pierwszych kolumn pokazujących stopień podobieństwa funkcji oraz prawdopodobieństwo z jakim BinDiff jest w stanie stwierdzić, że jest to odpowiednik tej samej funkcji z drugiego pliku.

W moim przypadku najwygodniejsze jest posortowanie funkcji względem kolumny confidence (zaznaczona na pomarańczowo) i wyszukanie funkcji z jak najwyższym współczynnikiem confidence i oczywiście jak najniższym similarity.

8. Aby podejrzeć poczynione zmiany należy kliknąć prawym przyciskiem myszy na interesującej pozycji i wybrać opcję View Flowgraphs (lub CTRL+E).

Legenda grafu: kolor zielony – pozycje niezmienione; kolor czerwony – pozycje usunięte; kolor szary – pozycje dodane

To by było na tyle szybkiego wprowadzenia do tematu 🙂

Dla zainteresowanych czytelników polecam prezentację “Microsoft Patch Analysis for Exploitation” Stephena Simsa (slajdy).

Petya Ransomware

Zachęcam do zapoznania sie z moim wpisem na blogu CERT Polska o wczorajszym ataku ransomware, który sparaliżował wiele instytucji na Ukrainie oraz w Polsce – Petya.

Zajawka:

Duet Petya & Mischa jest na rynku ransomware od końcówki 2015 roku. Po sukcesie ataku WannaCry, ostatni wariant został wzbogacony o funkcje propagacji wewnątrz sieci za pomocą exploita EternalBlue, PsExec oraz Windows Management Instrumentation Command-line (WMIC).

Wczorajsza kampania uderzyła przede wszystkim w sieci ukraińskie (dostawca energii Ukrenergo, system monitoringu promieniowania elektrowni w Czarnobylu oraz producent samolotów Antonov) oraz rosyjskie.

W przypadku Polski otrzymaliśmy tylko jedno potwierdzone zgłoszenie, lecz innymi kanałami udało się ustalić kilka zainfekowanych firm.

Całość wpisu 🙂

 

Pożyczony & zapomniany kod – dlaczego tego nie robić?

Krótka historyjka

Mimo, że tytuł posta jest dosyć oczywisty – porusza on całkiem poważny problem wielu projektów open-source i nie tylko.

Używamy cudzy kod, rozwijamy własny projekt, generalnie wszystko jest całkiem okej. Po pewnym czasie skupiamy się na funkcjonalnościach i problemach doraźnych, wiedząc, że “pożyczony” kod sobie działa i spełnia nasze oczekiwania. Mija nawet kilka lat, kod sobie dalej działa a priorytety projektu (i jego rozmiar) znacząco, lub nie, zostały zmienione i prawdopodobnie zmierzamy ku całkiem innemu celowi, niż pierwotnie zakładaliśmy.

Dlaczego zaczynam tak “na około”? Dlatego, że ta historia, całkiem niedawno dotknęła projekt radare2 (pisałem o nim m.in. tutaj). Efekt: cztery błędy związane tylko i wyłącznie z pożyczonym kodem z projektu GRUB. Kod ma ponad czteroletnią historię (okolice listopada 2013) i jest załatany od jakiegoś czasu w oficjalnym repozytorium projektu.

Morał z historyjki, w czterech częściach

Lista wyżej przytoczonych błędów w kolejności od “dotkliwości” problemu:

#1. Stack buffer underflow

radare2 Git HEAD: ad764839b20818d629131d4e07bda0038f9d747f

ASAN:

==32384==ERROR: AddressSanitizer: stack-buffer-underflow on address 0x7ffd57d028f8 at pc 0x7fc9c5b6ac47 bp 0x7ffd57d01c40 sp 0x7ffd57d01c38
WRITE of size 16 at 0x7ffd57d028f8 thread T0
    #0 0x7fc9c5b6ac46 in grub_memmove XYZ/radare2/shlr/grub/kern/misc.c:98:7
    #1 0x7fc9c5b67800 in grub_disk_read XYZ/radare2/shlr/grub/kern/disk.c:488:3
    #2 0x7fc9c5b68268 in grub_disk_read_ex XYZ/radare2/shlr/grub/kern/disk.c:563:12
    #3 0x7fc9c5b0754d in grub_fshelp_read_file XYZ/radare2/shlr/grub/fs/fshelp.c:333:4
    #4 0x7fc9c5b1134d in grub_ext2_read_file XYZ/radare2/shlr/grub/fs/ext2.c:504:9
    #5 0x7fc9c5b1134d in grub_ext2_iterate_dir XYZ/radare2/shlr/grub/fs/ext2.c:690
    #6 0x7fc9c5b0faf2 in grub_ext2_dir XYZ/radare2/shlr/grub/fs/ext2.c:876:3
    #7 0x7fc9c5af0c58 in ext2__mount XYZ/radare2/libr/fs/p/fs_grub_base.c:74:8
    #8 0x7fc9c5afbeaa in r_fs_mount XYZ/radare2/libr/fs/fs.c:151:7
    #9 0x7fc9c8f20dfb in cmd_mount XYZ/radare2/libr/core/./cmd_mount.c:49:9
    #10 0x7fc9c90e76af in r_cmd_call XYZ/radare2/libr/core/cmd_api.c:226:10
    #11 0x7fc9c8fd5811 in r_core_cmd_subst_i XYZ/radare2/libr/core/cmd.c:2191:12
    #12 0x7fc9c8f1d5b7 in r_core_cmd_subst XYZ/radare2/libr/core/cmd.c:1395:9
    #13 0x7fc9c8f16d24 in r_core_cmd XYZ/radare2/libr/core/cmd.c:2799:9
    #14 0x7fc9c8f0183f in r_core_cmdf XYZ/radare2/libr/core/cmd.c:2957:8
    #15 0x7fc9c90c1752 in bin_info XYZ/radare2/libr/core/cbin.c:621:4
    #16 0x7fc9c90c1752 in r_core_bin_info XYZ/radare2/libr/core/cbin.c:2870
    #17 0x7fc9c90b1e41 in r_core_bin_set_env XYZ/radare2/libr/core/cbin.c:115:3
    #18 0x7fc9c903d974 in r_core_file_do_load_for_io_plugin XYZ/radare2/libr/core/file.c:434:2
    #19 0x7fc9c903d974 in r_core_bin_load XYZ/radare2/libr/core/file.c:567
    #20 0x555f8a113f6b in main XYZ/radare2/binr/radare2/radare2.c:952:14
    #21 0x7fc9c1bc782f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
    #22 0x555f8a043f38 in _start (/usr/local/bin/radare2+0x20f38)

ASAN:DEADLYSIGNAL
AddressSanitizer: nested bug in the same thread, aborting

GitHub Issue #7683

Commit naprawiający

CVE: CVE-2017-9949

#2. Excessive stack usage (zmienna przepełniająca ramkę stosu)

radare2 Git HEAD: ba25be4934ecd65b71170f7381655325157bde09

ASAN:

==13184==ERROR: AddressSanitizer: stack-overflow on address 0x7ffcef7dce98 (pc 0x7f9c7499cecc bp 0x7ffcf37dcf70 sp 0x7ffcef7dcea0 T0)
    #0 0x7f9c7499cecb in grub_ext2_read_block XYZ/radare2/shlr/grub/fs/ext2.c:389:4
    #1 0x7f9c74991326 in grub_fshelp_read_file XYZ/radare2/shlr/grub/fs/fshelp.c:305:15
    #2 0x7f9c7499b116 in grub_ext2_read_file XYZ/radare2/shlr/grub/fs/ext2.c:504:9
    #3 0x7f9c7499b116 in grub_ext2_iterate_dir XYZ/radare2/shlr/grub/fs/ext2.c:672
    #4 0x7f9c749999a1 in grub_ext2_dir XYZ/radare2/shlr/grub/fs/ext2.c:882:3
    #5 0x7f9c7497ae95 in ext2__mount XYZ/radare2/libr/fs/p/fs_grub_base.c:74:8
    #6 0x7f9c74985fc4 in r_fs_mount XYZ/radare2/libr/fs/fs.c:151:7
    #7 0x7f9c77d7537d in cmd_mount XYZ/radare2/libr/core/./cmd_mount.c:49:9
    #8 0x7f9c77f3a25c in r_cmd_call XYZ/radare2/libr/core/cmd_api.c:226:10
    #9 0x7f9c77e28ae1 in r_core_cmd_subst_i XYZ/radare2/libr/core/cmd.c:2156:12
    #10 0x7f9c77d70d1e in r_core_cmd_subst XYZ/radare2/libr/core/cmd.c:1360:9
    #11 0x7f9c77d6a626 in r_core_cmd XYZ/radare2/libr/core/cmd.c:2764:9
    #12 0x7f9c77d555ad in r_core_cmdf XYZ/radare2/libr/core/cmd.c:2922:8
    #13 0x7f9c77f142d7 in bin_info XYZ/radare2/libr/core/cbin.c:621:4
    #14 0x7f9c77f142d7 in r_core_bin_info XYZ/radare2/libr/core/cbin.c:2873
    #15 0x7f9c77f04fd0 in r_core_bin_set_env XYZ/radare2/libr/core/cbin.c:115:3
    #16 0x7f9c77e8e263 in r_core_file_do_load_for_io_plugin XYZ/radare2/libr/core/file.c:434:2
    #17 0x7f9c77e8e263 in r_core_bin_load XYZ/radare2/libr/core/file.c:567
    #18 0x556a34e7013d in main XYZ/radare2/binr/radare2/radare2.c:952:14
    #19 0x7f9c70671510 in __libc_start_main (/usr/lib/libc.so.6+0x20510)
    #20 0x556a34d7ce29 in _start (/usr/local/bin/radare2+0x20e29)

SUMMARY: AddressSanitizer: stack-overflow XYZ/radare2/shlr/grub/fs/ext2.c:389:4 in grub_ext2_read_block
==13184==ABORTING

GitHub Issue #7723

Commit naprawiający

CVE: CVE-2017-9763

#3. FPE (dzielenie przez zero)

radare2 Git HEAD: 90ffb5463df15326cd8261da99b85597d4eb6b3b

==10375==ERROR: AddressSanitizer: FPE on unknown address 0x7fb2f4af4726 (pc 0x7fb2f4af4726 bp 0x7fff41d52850 sp 0x7fff41d52720 T0)
#0 0x7fb2f4af4725 in grub_ext2_read_inode XYZ/radare2/shlr/grub/fs/ext2.c:525:29
#1 0x7fb2f4af2ce4 in grub_ext2_mount XYZ/radare2/shlr/grub/fs/ext2.c:593:3
#2 0x7fb2f4af19ac in grub_ext2_dir XYZ/radare2/shlr/grub/fs/ext2.c:863:10
#3 0x7fb2f4ad2c58 in ext2__mount XYZ/radare2/libr/fs/p/fs_grub_base.c:74:8
#4 0x7fb2f4addeaa in r_fs_mount XYZ/radare2/libr/fs/fs.c:151:7
#5 0x7fb2f7ef996b in cmd_mount XYZ/radare2/libr/core/./cmd_mount.c:49:9
#6 0x7fb2f80be7df in r_cmd_call XYZ/radare2/libr/core/cmd_api.c:226:10
#7 0x7fb2f7faddeb in r_core_cmd_subst_i XYZ/radare2/libr/core/cmd.c:2178:12
#8 0x7fb2f7ef6127 in r_core_cmd_subst XYZ/radare2/libr/core/cmd.c:1368:9
#9 0x7fb2f7eef8b9 in r_core_cmd XYZ/radare2/libr/core/cmd.c:2786:9
#10 0x7fb2f7eda74f in r_core_cmdf XYZ/radare2/libr/core/cmd.c:2942:8
#11 0x7fb2f8098e42 in bin_info XYZ/radare2/libr/core/cbin.c:621:4
#12 0x7fb2f8098e42 in r_core_bin_info XYZ/radare2/libr/core/cbin.c:2870
#13 0x7fb2f8089531 in r_core_bin_set_env XYZ/radare2/libr/core/cbin.c:115:3
#14 0x7fb2f8015064 in r_core_file_do_load_for_io_plugin XYZ/radare2/libr/core/file.c:434:2
#15 0x7fb2f8015064 in r_core_bin_load XYZ/radare2/libr/core/file.c:567
#16 0x55e7cf695f6b in main XYZ/radare2/binr/radare2/radare2.c:952:14
#17 0x7fb2f0bae82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
#18 0x55e7cf5c5f38 in _start (/usr/local/bin/radare2+0x20f38)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: FPE XYZ/radare2/shlr/grub/fs/ext2.c:525:29 in grub_ext2_read_inode
==10375==ABORTING

GitHub Issue #7650

Commit naprawiający

CVE: N/A

#4. FPE (modulo przez zero)

radare2 Git HEAD: 4ae632133ed34c7f80949a8a705635608a4ac45d

==1943==ERROR: AddressSanitizer: FPE on unknown address 0x7f5778308fd5 (pc 0x7f5778308fd5 bp 0x7fff9b7706a0 sp 0x7fff9b7705a0 T0)
#0 0x7f5778308fd4 in grub_ext2_read_inode XYZ/radare2/shlr/grub/fs/ext2.c:530:5
#1 0x7f5778307eb8 in grub_ext2_mount XYZ/radare2/shlr/grub/fs/ext2.c:582:3
#2 0x7f577830718f in grub_ext2_dir XYZ/radare2/shlr/grub/fs/ext2.c:848:10
#3 0x7f57782f55d8 in ext2__mount XYZ/radare2/libr/fs/p/fs_grub_base.c:74:8
#4 0x7f57782fd357 in r_fs_mount XYZ/radare2/libr/fs/fs.c:141:7
#5 0x7f577af9af12 in cmd_mount XYZ/radare2/libr/core/./cmd_mount.c:57:9
#6 0x7f577b0801fc in r_cmd_call XYZ/radare2/libr/core/cmd_api.c:213:10
#7 0x7f577afccbea in r_core_cmd_subst_i XYZ/radare2/libr/core/cmd.c:2039:16
#8 0x7f577af98f67 in r_core_cmd_subst XYZ/radare2/libr/core/cmd.c:1332:9
#9 0x7f577af95f85 in r_core_cmd XYZ/radare2/libr/core/cmd.c:2595:9
#10 0x7f577af8b538 in r_core_cmdf XYZ/radare2/libr/core/cmd.c:2735:8
#11 0x7f577b069581 in bin_info XYZ/radare2/libr/core/cbin.c:593:4
#12 0x7f577b066f0e in r_core_bin_info XYZ/radare2/libr/core/cbin.c:2610:45
#13 0x7f577b066d3d in r_core_bin_set_env XYZ/radare2/libr/core/cbin.c:109:3
#14 0x7f577b02d645 in r_core_file_do_load_for_io_plugin XYZ/radare2/libr/core/file.c:409:2
#15 0x7f577b02b8ef in r_core_bin_load XYZ/radare2/libr/core/file.c:527:4
#16 0x5586a8089593 in main XYZ/radare2/binr/radare2/radare2.c:822:14
#17 0x7f57748fe82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
#18 0x5586a7fbbe58 in _start (/usr/local/bin/radare2+0x20e58)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: FPE XYZ/radare2/shlr/grub/fs/ext2.c:530:5 in grub_ext2_read_inode
==1943==ABORTING

GitHub Issue #6327

Commit naprawiający

CVE: N/A