SlideShare a Scribd company logo
Minerva
Fuzzing interpretera PHP




Mateusz Kocielski
m.kocielski@logicaltrust.net

OWASP, 23 maja 2011, Kraków
$ whoami                       WSPÓŁPRACUJĘ Z

MATEUSZ KOCIELSKI
http://shm.nation.pl/
http://digitalsun.pl/
m.kocielski@logicaltrust.net   GSoC STUDENT

      PUBLIKACJE



                               2010         2011
      PROJEKTY
                                      STUDENT
Plan wykładu
●   Automatyczne wyszukiwanie błędów
●   Analiza statyczna, analiza dynamiczna
●   Krótki wstęp do fuzzingu
●   Język PHP
●   Minerva
●   Wykorzystywanie podatności intepretera


●




●   Przyszłość projektu
Automatyczne wyszukiwanie błędów
 ●   Dlaczego automatycznie?
     ●   Jesteśmy leniwi...
     ●   Niskie koszty (koszt napisania + analiza
         znalezionych podatności)
     ●   Oszczędność czasu (lub innych zasobów)
REWELACJA
                  ...ale...

●...czy automat potrafi tyle ile człowiek?
●…czy da się taki automat zbudować?


Jeżeli tak, to jak?
●….czy to wszystko oznacza, że


przestaniemy być potrzebni?
Automat idealny

●   Wczytuję inny program
●   Znajduję WSZYSTKIE błędy
●   Generuję ładny kolorowy raport
JAK GO ZBUDOWAĆ?
NIEROZSTRZYGALNOŚĆ
● Istnieją problemy nierozstrzygalne
(tzn. takie, których nie można
rozwiązać przy pomocy algorytmu)

● Problem stopu (czy dany program
się zatrzymuje?)

●Bazując na teorii mocy można się
szybko przekonać o tym, że Turing
ma rację

●Redukcja naszego problemu do
problemu stopu                         ALAN TURING (1912-1954)
NIE JEST ŹLE!

● Możemy budować automaty „prawie” idealne (takie, które
się czasem mylą)

… a co najważniejsze ...

●   Możemy czuć się potrzebni!!!
RODZAJE ANALIZ
    ANALIZA STATYCZNA          ANALIZA DYNAMICZNA
●   Badamy statyczne       ●   Badamy działające
    obiekty                    obiekty
●   Przykład:              ●   Przykład:
    Analizujemy źródła w          Podpinamy się pod
    poszukiwaniu wywołań          wywołania funkcji
     printf, w których           malloc(), free() i
      ciąg formatujący           badamy czy program
         pochodzi od            nie próbuje zwolnić
        użytkownika.             dwa razy tej samej
                                       pamięci.
ANALIZA STATYCZNA
●   clang
●   splint
●   klocwork
●   lint
●   …
ANALIZA DYNAMICZNA
●   Valgrind
●   dmalloc
●   SPIKE
●   ...
FUZZING
●   Metoda analizy dynamicznej polegająca na
    karmieniu programu losowymi danymi i badaniu
    jego zachowania (np. przy użyciu innych
    narzędzi analizy dynamicznej)
●   Dobrym wyznacznikiem tego, że dzieje się coś
    złego może być naruszenie ochrony pamięci
●   Metodę zaproponował prof. Barton Miller
●   Jak przeszukiwać wykładniczą przestrzeń?
JAK ZAPRZYJAŹNIĆ SIĘ Z
              FUZZINGIEM?
●   Uwierzyć, że działa!
    ●   Dobrym dowodem są fuzzery napisane przez
        DigitalDwarf.be (pokonały m.in.. OpenBSD, irssi...)
●   Nauczyć się teorii prawdopodobieństwa
●   Użyć mądrej metody generowania danych:
    ●   Algorytmy ewolucyjne
    ●   Gramatyki bezkontekstowe
    ●   Więzy
    ●   Whitebox fuzzing
JĘZYK PHP
[…] PHP - obiektowy, skryptowy język
programowania zaprojektowany do
generowania stron internetowych w czasie
rzeczywistym […]
                              źródło: Wikipedia
               „PHP jest prawie tak ekscytujący jak
               szczoteczka do zębów. Używasz go
               codziennie, odwala za ciebie robotę,
               jest prostym narzędziem, więc? Kto
               chciałby poczytać o szczoteczkach?”

                                 Rasmus Lerdorf
JĘZYK PHP
●   Dynamicznie typowany
●   Każda funkcja ma jakiś typ:
    ●   string * int → string
    ●   str_repeat(55, "foo"); → PHP Warning: str_repeat()
        expects parameter 2 to be long, string given in - on
        line...
JĘZYK PHP
●   Wsparcie dla popularnych serwerów HTTP
    ●   apache, apache2, thttpd
    ●   cgi, fcgi
    ●   cli
CIEKAWOSTKA
●   for(;!$x;); - 11 znaków
●   PHP 5.3.6 with Suhosin-Patch (cli) (built: Apr 18
    2011 11:14:25)

    $ php -d max_execution_time=1 bla.php 2>
    /dev/null
    *** glibc detected *** php: double free or
    corruption (fasttop): 0x093cf758 ***
PHP popularność




       25%<
●   25% httpd apache deklaruje, że używa PHP
●   z tego 25% używa patcha Suhosin
MINERVA
 ●   Fuzzer interpretera PHP
 ●   Licencja BEERWARE
 ●   Napisany w PYTHONIE
 ●   Łatwa konfiguracja
 ●   Duża elastyczność
     (możliwe dokładanie
     własnych funkcji)
 ●   Oficjalna wersja 1.0
POPRZEDNIE PRÓBY
         FUZZOWNIA INERPETERA
●   Fusil, Lixam
    ●   Weź losową ilość losowych danych i zaaplikuj do
        losowej funkcji
●   PFF
    ●   Na podstawie dostarczonego szablonu zbuduj
        losowe wywołania (typy ograniczone do napisów i
        liczb)
●   Powyższe fuzzery miały swoje 5 minut chwały:
    ●   Lixam znalazł jednego buffer overflowa...
    ●   ...a w PFF znaleziono błąd :-))
JAK DZIAŁA MINERVA?
CO UDAŁO SIĘ ZNALEŹĆ?
●   Use-after-free (sqlite)
●   Double free (imap)
●   Integer overflow (calendar)
●   Buffer overrun (mbstring)
●   ...inne (fnmatch, stream x 2, openssl)
●   …i wiele (jeszcze) nieopublikowanych błędów
JAK WYKORZYSTAĆ BŁĘDY W
          INTEPRETERZE
●   Błędy związane z zarządzniem pamięcią (use-
    after-free, double free)
    ●   PHP ma własny alokator pamięci, który jest
        zorganizowany w kolejki FIFO przechowujące
        kawałki pamięci o danej wielkości
    ●   Alokator ma zabezpieczenia... ale standardowo są
        one wyłączone (ze względu na narzut czasowy)
CZYLI


0.001DAY
STACK BUFFER OVERFLOW W
         MODULE SOCKET
●   błąd w funkcji socket_create:
●   W obsłudze socketów typu AF_UNIX
    […]
    memcpy(&s_un.sun_path, addr, addr_len);
    […]
JAK GO WYKORZYSTAĆ?
●   Do próby unieruchomienia serwera
●   Do zrzucenia sobie shella
●   Do zmuszenia httpd, żeby serwował treść, którą
    sobie życzymy!
SZCZEGÓŁY ATAKU
●   Dziękuję n1x0nowi i s1m0nowi za pomoc w
    przygotowaniu działającego exploita
●   Jak zmusić Apache do serwowania własnej
    treści?
    ●   ap_run_handler() → _hooks()
    ●   Sam złośliwy uchwyt może zawierać tylko
        wywołanie funkcji ap_rputs()
●   Musimy zadbać o „normalny” powrót do
    interpretera PHP (zend_try, zend_catch
    wykorzystuje long/set_jmp)
●   Skąd wziąć pamięć na własny uchwyt?
PLAN ATAKU




socket_connect()

                              HOOKS



         SHELLCODE
                         EVIL HANDLER
POTENCJALNE PROBLEMY
●   ASLR
●   SSP
●   PIE
●   Skąd wziąć adres ar_rputs i tablicy _hooks?
DEMO
Jak się zabezpieczyć?
●   Wyłączyć moduł socket w PHP
●   Naprawić błąd (dołożyć warunek sprawdzający
    czy nie próbujemy skopiować zbyt wiele
    pamięci)
●   SSP posiadające __memcpy_chk eliminuje
    problem
●   Użyć disabled_functions
CO DALEJ?
●PHP jest językiem obiektowym (aktualnie Minerva w żaden
sposób z tego nie korzysta)

Generowanie bardziej skomplikowanych programów
●




Pomiar pokrycia kodu przy pomocy gcov
●




Obsługa innych języków (MinervaJS)
●




Przetestowanie modułów FTP, MySQL itp.
●




Przeprojektowanie (OCaml)
●
Q&A

More Related Content

PDF
Jak zacząć, aby nie żałować - czyli 50 twarzy PHP
PDF
PPT
Zwiększanie produktywności programisty php
PDF
DTrace, czyli jak zobaczyć to czego nie widać.
PPTX
Laravel Poznań Meetup #4 - EloquentSequence - Historia pewnej biblioteki Open...
PPTX
EloquentSequence - historia pewnej biblioteki Open Source
PPTX
PSR czyli dobre praktyki programistyczne
PPTX
Od Javy 1.4 do Javy 1.6
Jak zacząć, aby nie żałować - czyli 50 twarzy PHP
Zwiększanie produktywności programisty php
DTrace, czyli jak zobaczyć to czego nie widać.
Laravel Poznań Meetup #4 - EloquentSequence - Historia pewnej biblioteki Open...
EloquentSequence - historia pewnej biblioteki Open Source
PSR czyli dobre praktyki programistyczne
Od Javy 1.4 do Javy 1.6

What's hot (10)

PPTX
PSR czyli dobre praktyki programistyczne
PPTX
INVOKEDYNAMIC - bardziej dynamiczna JVM (Confitura 2012)
PDF
ZamCamp - Ciekawe sposoby debugowania kodu
PDF
Bestiariusza wpisy wybrane
PDF
Noc informatyka - co ja wiem o testowaniu
PDF
Tworzenie, zaciemnianie i analiza złośliwego kodu JavaScript
PDF
Każdy popełnia błędy, czyli jak lepiej zarządzać błędami (ale nie wyjątkami)
PDF
WJUG #257 Just-In-Time compiler - ukryty "przyjaciel" - Krzysztof Ślusarski
PDF
DTrace
PDF
Space Wars Hack - Class #1
PSR czyli dobre praktyki programistyczne
INVOKEDYNAMIC - bardziej dynamiczna JVM (Confitura 2012)
ZamCamp - Ciekawe sposoby debugowania kodu
Bestiariusza wpisy wybrane
Noc informatyka - co ja wiem o testowaniu
Tworzenie, zaciemnianie i analiza złośliwego kodu JavaScript
Każdy popełnia błędy, czyli jak lepiej zarządzać błędami (ale nie wyjątkami)
WJUG #257 Just-In-Time compiler - ukryty "przyjaciel" - Krzysztof Ślusarski
DTrace
Space Wars Hack - Class #1
Ad

Similar to 201105 OWASP Fuzzing interpretera PHP (20)

PPTX
Shall we play a game? PL version
PPT
Zwiększanie produktywności programisty php (v2)
PDF
CONFidence 2018: "Small money, a lot of bugs" - Large scale bughunting dla ty...
PDF
Nowoczesny obiektowy Pascal (1) - kurs BSC
PDF
Embedded Debugging, czyli co kryje się w jądrze?
PPTX
Testowanie bezpieczenstwa aplikacji mobilnych
PDF
PLNOG22 - Leszek Miś - Symulacje zdarzeń i anomalii sieciowych jako proaktywn...
PDF
Warsztaty: Podstawy PHP - część 2 - omówienie składni języka PHP (wersja 7)
PPTX
Iron Python I Dlr
PDF
Minimalistyczny workflow programisty PHP - klucz do efektywności
PDF
Asmebler
PPTX
Testy jednostkowe - PHPUnit
PDF
Nie tylko C# - Ekosystem Microsoft dla programistów
PPTX
PLNOG 22 - Kamil Frankowicz - Hakowanie AI - Czy da się zepsuć myślące maszyn...
PDF
PLNOG 4: Krzysztof Góźdź - Od ssh do batuty - czyli jak z administratora stać...
PDF
OWASP - analiza statyczna języka PHP
PDF
Pokaż kotku, co masz w środku profilowanie aplikacji z xhprof
PDF
OpenEmbedded
PPTX
Pułapki programowania obiektowego
ODP
Elasticsearch nie tylko dla Wielkodanowców
Shall we play a game? PL version
Zwiększanie produktywności programisty php (v2)
CONFidence 2018: "Small money, a lot of bugs" - Large scale bughunting dla ty...
Nowoczesny obiektowy Pascal (1) - kurs BSC
Embedded Debugging, czyli co kryje się w jądrze?
Testowanie bezpieczenstwa aplikacji mobilnych
PLNOG22 - Leszek Miś - Symulacje zdarzeń i anomalii sieciowych jako proaktywn...
Warsztaty: Podstawy PHP - część 2 - omówienie składni języka PHP (wersja 7)
Iron Python I Dlr
Minimalistyczny workflow programisty PHP - klucz do efektywności
Asmebler
Testy jednostkowe - PHPUnit
Nie tylko C# - Ekosystem Microsoft dla programistów
PLNOG 22 - Kamil Frankowicz - Hakowanie AI - Czy da się zepsuć myślące maszyn...
PLNOG 4: Krzysztof Góźdź - Od ssh do batuty - czyli jak z administratora stać...
OWASP - analiza statyczna języka PHP
Pokaż kotku, co masz w środku profilowanie aplikacji z xhprof
OpenEmbedded
Pułapki programowania obiektowego
Elasticsearch nie tylko dla Wielkodanowców
Ad

More from Logicaltrust pl (20)

PDF
Czy szczoteczka do zębów to wektor ataku?
PDF
Podsumowanie ze 100 pentestów - na co zwrócić uwagę.
PDF
Podsumowanie z 200 pentestow - na co zwrócić uwagę.
PDF
Certyfikaty: CRTP / CRTO - Testy z CobaltStrike czy jednak bez? Przygotowanie...
PDF
Redteaming na zapleczu sklepu - opis testów penetracyjnych wraz z testami bez...
PDF
Dzień z życia pentestera - proces oraz narzędzia do testów penetracyjnych
PDF
Jak cyberprzęstepcy okradają dziś firmy - webinar 2020.06.24
PDF
Security Awareness po polsku - webinar 2019.11.29
PDF
8 zasad skutecznego security awareness
PDF
Ataki socjotechniczne w praktyce - SecurityBSides Warsaw 2019
PDF
Ataki socjotechniczne w praktyce - Confidence 2019
PDF
Minerva_lib - fuzzing tool
PDF
"Spear phishing - jak się bronić? Case studies." - SecurityBSides 2018
PDF
Spear phishing - jak się bronić? Case studies - Confidence 2018
PDF
Redteaming in Poland - test cases (Security)
PDF
Redteaming w Polsce - przykłady
PDF
Testy bezpieczeństwa - niesztampowe przypadki
PDF
Krytyczne błędy konfiguracji
PDF
Urządzenia i usługi bezpieczeństwa IT - pełna ochrona czy... zaproszenie dla ...
PDF
Devops/Sysops security
Czy szczoteczka do zębów to wektor ataku?
Podsumowanie ze 100 pentestów - na co zwrócić uwagę.
Podsumowanie z 200 pentestow - na co zwrócić uwagę.
Certyfikaty: CRTP / CRTO - Testy z CobaltStrike czy jednak bez? Przygotowanie...
Redteaming na zapleczu sklepu - opis testów penetracyjnych wraz z testami bez...
Dzień z życia pentestera - proces oraz narzędzia do testów penetracyjnych
Jak cyberprzęstepcy okradają dziś firmy - webinar 2020.06.24
Security Awareness po polsku - webinar 2019.11.29
8 zasad skutecznego security awareness
Ataki socjotechniczne w praktyce - SecurityBSides Warsaw 2019
Ataki socjotechniczne w praktyce - Confidence 2019
Minerva_lib - fuzzing tool
"Spear phishing - jak się bronić? Case studies." - SecurityBSides 2018
Spear phishing - jak się bronić? Case studies - Confidence 2018
Redteaming in Poland - test cases (Security)
Redteaming w Polsce - przykłady
Testy bezpieczeństwa - niesztampowe przypadki
Krytyczne błędy konfiguracji
Urządzenia i usługi bezpieczeństwa IT - pełna ochrona czy... zaproszenie dla ...
Devops/Sysops security

201105 OWASP Fuzzing interpretera PHP

  • 1. Minerva Fuzzing interpretera PHP Mateusz Kocielski m.kocielski@logicaltrust.net OWASP, 23 maja 2011, Kraków
  • 2. $ whoami WSPÓŁPRACUJĘ Z MATEUSZ KOCIELSKI http://shm.nation.pl/ http://digitalsun.pl/ m.kocielski@logicaltrust.net GSoC STUDENT PUBLIKACJE 2010 2011 PROJEKTY STUDENT
  • 3. Plan wykładu ● Automatyczne wyszukiwanie błędów ● Analiza statyczna, analiza dynamiczna ● Krótki wstęp do fuzzingu ● Język PHP ● Minerva ● Wykorzystywanie podatności intepretera ● ● Przyszłość projektu
  • 4. Automatyczne wyszukiwanie błędów ● Dlaczego automatycznie? ● Jesteśmy leniwi... ● Niskie koszty (koszt napisania + analiza znalezionych podatności) ● Oszczędność czasu (lub innych zasobów)
  • 5. REWELACJA ...ale... ●...czy automat potrafi tyle ile człowiek? ●…czy da się taki automat zbudować? Jeżeli tak, to jak? ●….czy to wszystko oznacza, że przestaniemy być potrzebni?
  • 6. Automat idealny ● Wczytuję inny program ● Znajduję WSZYSTKIE błędy ● Generuję ładny kolorowy raport
  • 8. NIEROZSTRZYGALNOŚĆ ● Istnieją problemy nierozstrzygalne (tzn. takie, których nie można rozwiązać przy pomocy algorytmu) ● Problem stopu (czy dany program się zatrzymuje?) ●Bazując na teorii mocy można się szybko przekonać o tym, że Turing ma rację ●Redukcja naszego problemu do problemu stopu ALAN TURING (1912-1954)
  • 9. NIE JEST ŹLE! ● Możemy budować automaty „prawie” idealne (takie, które się czasem mylą) … a co najważniejsze ... ● Możemy czuć się potrzebni!!!
  • 10. RODZAJE ANALIZ ANALIZA STATYCZNA ANALIZA DYNAMICZNA ● Badamy statyczne ● Badamy działające obiekty obiekty ● Przykład: ● Przykład: Analizujemy źródła w Podpinamy się pod poszukiwaniu wywołań wywołania funkcji printf, w których malloc(), free() i ciąg formatujący badamy czy program pochodzi od nie próbuje zwolnić użytkownika. dwa razy tej samej pamięci.
  • 11. ANALIZA STATYCZNA ● clang ● splint ● klocwork ● lint ● …
  • 12. ANALIZA DYNAMICZNA ● Valgrind ● dmalloc ● SPIKE ● ...
  • 13. FUZZING ● Metoda analizy dynamicznej polegająca na karmieniu programu losowymi danymi i badaniu jego zachowania (np. przy użyciu innych narzędzi analizy dynamicznej) ● Dobrym wyznacznikiem tego, że dzieje się coś złego może być naruszenie ochrony pamięci ● Metodę zaproponował prof. Barton Miller ● Jak przeszukiwać wykładniczą przestrzeń?
  • 14. JAK ZAPRZYJAŹNIĆ SIĘ Z FUZZINGIEM? ● Uwierzyć, że działa! ● Dobrym dowodem są fuzzery napisane przez DigitalDwarf.be (pokonały m.in.. OpenBSD, irssi...) ● Nauczyć się teorii prawdopodobieństwa ● Użyć mądrej metody generowania danych: ● Algorytmy ewolucyjne ● Gramatyki bezkontekstowe ● Więzy ● Whitebox fuzzing
  • 15. JĘZYK PHP […] PHP - obiektowy, skryptowy język programowania zaprojektowany do generowania stron internetowych w czasie rzeczywistym […] źródło: Wikipedia „PHP jest prawie tak ekscytujący jak szczoteczka do zębów. Używasz go codziennie, odwala za ciebie robotę, jest prostym narzędziem, więc? Kto chciałby poczytać o szczoteczkach?” Rasmus Lerdorf
  • 16. JĘZYK PHP ● Dynamicznie typowany ● Każda funkcja ma jakiś typ: ● string * int → string ● str_repeat(55, "foo"); → PHP Warning: str_repeat() expects parameter 2 to be long, string given in - on line...
  • 17. JĘZYK PHP ● Wsparcie dla popularnych serwerów HTTP ● apache, apache2, thttpd ● cgi, fcgi ● cli
  • 18. CIEKAWOSTKA ● for(;!$x;); - 11 znaków ● PHP 5.3.6 with Suhosin-Patch (cli) (built: Apr 18 2011 11:14:25) $ php -d max_execution_time=1 bla.php 2> /dev/null *** glibc detected *** php: double free or corruption (fasttop): 0x093cf758 ***
  • 19. PHP popularność 25%< ● 25% httpd apache deklaruje, że używa PHP ● z tego 25% używa patcha Suhosin
  • 20. MINERVA ● Fuzzer interpretera PHP ● Licencja BEERWARE ● Napisany w PYTHONIE ● Łatwa konfiguracja ● Duża elastyczność (możliwe dokładanie własnych funkcji) ● Oficjalna wersja 1.0
  • 21. POPRZEDNIE PRÓBY FUZZOWNIA INERPETERA ● Fusil, Lixam ● Weź losową ilość losowych danych i zaaplikuj do losowej funkcji ● PFF ● Na podstawie dostarczonego szablonu zbuduj losowe wywołania (typy ograniczone do napisów i liczb) ● Powyższe fuzzery miały swoje 5 minut chwały: ● Lixam znalazł jednego buffer overflowa... ● ...a w PFF znaleziono błąd :-))
  • 23. CO UDAŁO SIĘ ZNALEŹĆ? ● Use-after-free (sqlite) ● Double free (imap) ● Integer overflow (calendar) ● Buffer overrun (mbstring) ● ...inne (fnmatch, stream x 2, openssl) ● …i wiele (jeszcze) nieopublikowanych błędów
  • 24. JAK WYKORZYSTAĆ BŁĘDY W INTEPRETERZE ● Błędy związane z zarządzniem pamięcią (use- after-free, double free) ● PHP ma własny alokator pamięci, który jest zorganizowany w kolejki FIFO przechowujące kawałki pamięci o danej wielkości ● Alokator ma zabezpieczenia... ale standardowo są one wyłączone (ze względu na narzut czasowy)
  • 26. STACK BUFFER OVERFLOW W MODULE SOCKET ● błąd w funkcji socket_create: ● W obsłudze socketów typu AF_UNIX […] memcpy(&s_un.sun_path, addr, addr_len); […]
  • 27. JAK GO WYKORZYSTAĆ? ● Do próby unieruchomienia serwera ● Do zrzucenia sobie shella ● Do zmuszenia httpd, żeby serwował treść, którą sobie życzymy!
  • 28. SZCZEGÓŁY ATAKU ● Dziękuję n1x0nowi i s1m0nowi za pomoc w przygotowaniu działającego exploita ● Jak zmusić Apache do serwowania własnej treści? ● ap_run_handler() → _hooks() ● Sam złośliwy uchwyt może zawierać tylko wywołanie funkcji ap_rputs() ● Musimy zadbać o „normalny” powrót do interpretera PHP (zend_try, zend_catch wykorzystuje long/set_jmp) ● Skąd wziąć pamięć na własny uchwyt?
  • 29. PLAN ATAKU socket_connect() HOOKS SHELLCODE EVIL HANDLER
  • 30. POTENCJALNE PROBLEMY ● ASLR ● SSP ● PIE ● Skąd wziąć adres ar_rputs i tablicy _hooks?
  • 31. DEMO
  • 32. Jak się zabezpieczyć? ● Wyłączyć moduł socket w PHP ● Naprawić błąd (dołożyć warunek sprawdzający czy nie próbujemy skopiować zbyt wiele pamięci) ● SSP posiadające __memcpy_chk eliminuje problem ● Użyć disabled_functions
  • 33. CO DALEJ? ●PHP jest językiem obiektowym (aktualnie Minerva w żaden sposób z tego nie korzysta) Generowanie bardziej skomplikowanych programów ● Pomiar pokrycia kodu przy pomocy gcov ● Obsługa innych języków (MinervaJS) ● Przetestowanie modułów FTP, MySQL itp. ● Przeprojektowanie (OCaml) ●
  • 34. Q&A