d33tah's weblog

...ale czemu Madzia?

...Schodzę na chwilę na dół, rodzice siedzą przy telewizorze. Wiadomości. Tyle w tym kraju potencjalnie ciekawych, istotnych społecznie rzeczy się dzieje, a ja słyszę o tym, że jakiemuś gościowi zmarła w wypadku najbliższa rodzina, a teraz prokuratura podejrzewa, że to on do tego doprowadził. A ja się zastanawiam - w jaki sposób mnie to dotyczy, czemu jako Szary Obywatel miałoby mnie to obchodzić? Czy tego typu newsy mają zaspakajać moją potrzebę "rozrywki", mam się poczuć gorzej przez moją znieczulicę? A może znowu jest coś, od czego trzeba odciągnąć moją uwagę, kolejna podwyżka podatków, jak za czasów Madzi?

Przy okazji, Tool - Vicarious, z tekstem w temacie:


( 5 komentarzy )

"Hacking: Art of Exploitation" - pierwsze eksperymenty

Ostatnio mój znajomy żalił mi się, że jest już po dwudziestce, a jeszcze nie napisał żadnego exploita. Trochę mnie to rozbawiło, bo przecież jeszcze sporo czasu mu zostało ;). Chciałem go jednak przekonać (a przy okazji sam się upewnić), że faktycznie nie jest to jakaś tajemna wiedza i sam mechanizm dość łatwo zrozumieć. Postanowiłem, że wypróbuję w końcu podstawy wykorzystywania błędów pamięci i spróbuję napisać prosty program, który łączy się z danym serwerem, pobiera od niego dane i wykonuje je jako kod maszynowy. Dodatkowo, chciałem obyć się bez korzystania z dokumentacji w internecie. Na samym początku przekonałem się, że schemat nawiązywania połączeń TCP w POSIX jest dość skomplikowany i mógłbym mieć duże trudności gdyby nie program "strace" oraz komenda "LC_ALL=C man 3 getaddrinfo" (w polskiej wersji jest dużo słabiej). W końcu, napisałem taki, raczej pokraczny kod:

  1. #include <sys/types.h>
  2. #include <sys/socket.h>
  3. #include <netdb.h>
  4. #include <stdlib.h>
  5. #include <errno.h>
  6. #include <sys/mman.h>
  7. #include <unistd.h>
  8.  
  9. int main()
  10. {
  11.         void (*f)(void);
  12.         char buf[4096];
  13.         int fd = socket(AF_INET, SOCK_STREAM, 0);
  14.         struct addrinfo* sa = malloc(sizeof(struct addrinfo));
  15.         sa->ai_family = AF_INET;
  16.         sa->ai_flags = 0;
  17.         sa->ai_socktype = SOCK_STREAM;
  18.         sa->ai_protocol = 0;
  19.  
  20.         printf("%d\n", getaddrinfo("localhost", "31337",  NULL, &sa));
  21.  
  22.         sa->ai_addr->sa_family = AF_INET;
  23.  
  24.         printf("%d\n", connect(fd, sa->ai_addr, sa->ai_addrlen));
  25.         perror("connect");
  26.         read(fd, buf, sizeof(buf));
  27.         printf("%s\n", buf);
  28.         f = buf;
  29.         f();
  30. }

Rozumiejąc, co sugeruje mi GCC sypiąc warningami, zignorowałem jego ostrzeżenia i przeszedłem do pisania prostego payloadu. Wymyśliłem sobie, że dość łatwo będzie zauważyć, kiedy program po prostu zakończy pracę, wracając do powłoki z wybranym przeze mnie kodem powrotnym. Napisałem i zdeasembowałem program w stylu "main(){exit(4);}" i przekonałem się, że z takiego kodu nie wyłowię interesujących mnie instrukcji. Pamiętałem, że kiedyś podałem konkretne instrukcje asemblera na tym blogu i jako, że byłem już znudzony tym, ile czasu bez pomocy internetu pisałem prostego klienta TCP w C, pozwoliłem sobie na małe "oszustwo" i zerknąłem do mojego bloga. Chwilę później miałem gotowy payload, który w asemblerze wyglądał tak:

  1. xor eax, eax
  2. inc eax ; eax=1 => exit(ebx)
  3. xor ebx, ebx
  4. inc ebx ; ebx=1 => exit(1)
  5. int 0x80

Przepuściłem go przez NASMa, otrzymałem kod maszynowy, stworzyłem Ncatem serwer, który go przekazuje klientowi... po czym otrzymałem "segmentation fault". Grzebiąc przez chwilę debuggerem nie dowiedziałem się, na czym polega problem i zwróciłem się pomoc do kanału #hackerspace-pl. Tam podsunęli mi następujące teorie:

  1. Wykonuję kod 32-bitowy na 64-bitowym procesorze,
  2. Mój system wymusza W^X,
  3. Z mojego bufora nie można wykonywać kodu.

Przy okazji w ten sposób odkryłem, że mój (i właściwie każdy inny) 64-bitowy procesor x86 nie odpali tak po prostu 32-bitowego payloadu w 64-bitowej aplikacji. Na szczęście po dopisaniu na początku kodu w asemblerze linijki "bits 64", wygenerowałem 64-bitową wersję mojego payloada... która także prowadziła do "segmentation fault". Zdecydowałem się więc sprawdzić, jakie uprawnienia ma pamięć. Uruchomiłem gdb, ustawiłem breakpoint przed wskoczeniem w bufor, po czym zczytałem /proc/`pgrep -f /tmp/test`/maps i wrzuciłem jego zawartość do napisanego na szybko (tak, wiem, brzydkiego) skryptu:

  1. #!/usr/bin/python
  2.  
  3. import sys
  4.  
  5. maps = """00400000-00401000 r-xp 00000000 00:20 256968                             /tmp/test
  6. 00600000-00601000 r--p 00000000 00:20 256968                             /tmp/test
  7. 00601000-00602000 rw-p 00001000 00:20 256968                             /tmp/test
  8. 3c38a00000-3c38a20000 r-xp 00000000 fd:00 1318992                        /usr/lib64/ld-2.18.so
  9. 3c38c1f000-3c38c20000 r--p 0001f000 fd:00 1318992                        /usr/lib64/ld-2.18.so
  10. 3c38c20000-3c38c21000 rw-p 00020000 fd:00 1318992                        /usr/lib64/ld-2.18.so
  11. 3c38c21000-3c38c22000 rw-p 00000000 00:00 0
  12. 3c38e00000-3c38fb4000 r-xp 00000000 fd:00 1325806                        /usr/lib64/libc-2.18.so
  13. 3c38fb4000-3c391b4000 ---p 001b4000 fd:00 1325806                        /usr/lib64/libc-2.18.so
  14. 3c391b4000-3c391b8000 r--p 001b4000 fd:00 1325806                        /usr/lib64/libc-2.18.so
  15. 3c391b8000-3c391ba000 rw-p 001b8000 fd:00 1325806                        /usr/lib64/libc-2.18.so
  16. 3c391ba000-3c391bf000 rw-p 00000000 00:00 0
  17. 7ffff7fbf000-7ffff7fc2000 rw-p 00000000 00:00 0
  18. 7ffff7ffc000-7ffff7ffd000 rw-p 00000000 00:00 0
  19. 7ffff7ffd000-7ffff7fff000 r-xp 00000000 00:00 0                          [vdso]
  20. 7ffffffde000-7ffffffff000 rw-p 00000000 00:00 0                          [stack]
  21. ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]""".split("\n")
  22.  
  23. addr = int(sys.argv[1],16)
  24. for line in maps:
  25.         start, end = map(lambda x: int(x, 16), line.split()[0].split('-'))
  26.         if start <= addr and end >= addr:
  27.                 print(line)

Moje obawy się potwierdziły - fragment pamięci, w którym był bufor, jest tylko do odczytu i zapisu. W moim rozumieniu tak napisany program nie jest więc raczej podatny na wykonywanie dowolnego kodu... postanowiłem go więc zmienić, zastępując linijkę "char buf[4096];" instrukcją "char * buf = mmap(0, 4096, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);". Oczywiście po drodze zrobiłem błąd i zamiast "-1, 0" napisałem "0, -1" i parę minut błądziłem po manualu do mmap. Gdy już byłem pewien, że mmap zadziałał, spróbowałem znowu i... "segmentation fault!". Zdesperowany eksperymentowałem z różnymi NOPami i innymi bezsensownymi pomysłami, po czym poddałem się i postanowiłem do tematu wrócić później.

Dwa dni później, zacząłem do programu dopisywać wykrywanie błędów i zauważyłem coś dziwnego... wywołanie read() mówi mi, że wczytano tylko 8 bajtów. Skróciłem mojego payloada do 8 bajtów i wykonał się on poprawnie. Chwilę później dotarło do mnie, że zapomniałem poprawić "sizeof(buf)" po zmianie typu zmiennej buf... Po poprawieniu błędu wszystko zaczęło działać poprawnie. Nie chciałem jednak na tym kończyć - zacząłem się zastanawiać, czy w 8 bajtach mógłbym zrobić odpowiednik wywołania "exit(4)". Po paru nieudanych eksperymentach zadałem to pytanie na #hackerspace-pl, usłyszałem, że nie ma co na to liczyć bez założenia z góry co może się znajdować w rejestrach procesora. Zostałem też pokierowany, żeby wypróbować instrukcje w stylu "mov bl, 4", która mieści się w dwóch bajtach.

Znowu zajrzałem w program debuggerem i sprawdziłem zawartość rejestrów zaraz po wskoczeniu w bufor. Wyglądało na to, że EAX zawiera adres bufora, ale ebx jest puste. Sprawdziłem więc taki kod:

  1. bits 64
  2. xor eax, eax
  3. inc eax
  4. mov bh, 4
  5. int 0x80

Uruchomiłem NASMa, zerknąłem na wynikową binarkę... miała dokładnie 8 bajtów - tyle co potrzeba. Wychodzi więc na to, że tyle wystarczy by wykonać to, co chciałem. Na sam koniec zerknąłem jeszcze do logów kernela:

[12879.929065] test[12681]: segfault at 7fc3fe494000 ip 00007fc3fe493fff sp 00007fffcfdf4620 error 15

W przypadku debuggowania tajemniczego "segmentation fault", może nas zainteresować numer znajdujący się po słowie "error". Przy dekodowaniu go, warto zerknąć do tutaj.


( Dodaj komentarz )

Waking up 20 years later

Today I had this strange feeling that I woke up after 20 years of sleeping while the technology kept progressing forward.

First, I watched Edward Snowden's TED talk. This was basically my first encounter with telepresence. A basic trick, but I'm absolutely amazed at how well it works.

And now, I saw Getin Bank advertise the "new" (actually, more of a "2012-new") mastercard breakthrough, a credit card with a screen and a keyboard. My first thought was something like "how is this thing even powered?". And then I thought of e-ink which drains power only when the screen state is toggled, microprocessors that probably don't need to be powered up all the time... And I guess that it makes sense. Still, I find it rather unbelievable that it came so early.

If I could choose which kind of future I'd like to find myself in, I'd rather it wasn't a classical cyberpunk dystopia... Though when one visits a news website and finds that countries in a warzone already spy each using drones, I guess it's kind of too late to wish that.


( Dodaj komentarz )

Bot Joggera siadł?

Hm, dziś do mnie dotarło, że nie dostaję powiadomień od Joggerowego bota na XMPP - czyżby został wyłączony jakiś czas temu?


( 13 komentarzy )

Wcześniejsze wpisy

Kategorie

Profile

Sponsorowane

Wolna kultura - książki

Wolna kultura - muzyka