Evet en son shellcode'da Linux System Call Number işliyorduk hatta birde pause fonksiyonunu işlemişiz ikinci seride biraz daha string ağırlıklı ve system call function kullanarak hareket etmeyi göreceğiz ama öncelikle kaldığımız yerden devam edelim kafa karışıklıklarına meydan vermeyelim.
Evet biraz string yazdıralım cümleleyelim felan salıverelim isterseniz, evet yeniden terminalimiz açıp gerekli kodları yazdıralım zaten bu asm editor hakkında da bilgi vermiştim isteyen nasm isteyen gasm vs. gibi editörler kullanabilir. Basic olarak .asm öğrendiğinizde bir nevi shellcode ilerletmiş oluyorsunuz ama tam manasıyla ben asm. hakim olmak istiyorum diyorsanız opcode ve sistemin işleyişi hakkında tam manasıyla çözümleme yapmanız gerekmektedir. Peki bunlar nelerdir diye soracak olursanız her yazımda belirttiğim gibi "EZBER YAPMAYIN" sözüyle bir örnekle açıklamak istiyorum; CMP fonksiyonun açılımı "compare" yani karşılaştırma fonksiyonudur. Şöyle ki,
mov ebx, 0x1
xor eax, eax
inc eax
cmp eax, ebx
:?
Peki ne yaptık burada ? Açıklayalım, asm mantığı tamamen ilkokulda düşman olduğumuz matematik ile bağdaşır ve bu şekilde çalışır; "Ya bilgisayar 0 ve 1 ile çalışıyor yaa" diye söylediğimizde nasıl yaa ? Diye sorduklarında şu yukarıda ki çıktı ve binary aklınıza gelirse eğer tam manasıyla anlamış olursunuz. Yukarıda ebx 1 atadık, sonrasında eax register xor operatoru ile "0" sayısına çevirdik ve inc operatoruyle 1 sayı öne attırdık daha sonrasında cmp fonksiyonu ilede karşılaştırdık karşılaştırmadan sonrada yapılacak şeyler size kalıyor artık "je" mi dersiniz "jne" mi dersiniz orası size kalmış bir mevzu kısa bir açıklamadan sonra mevzumuza dönelim.
Shellcode mantığında yazdığımız kodlar, belirli bir şekilde kodlanarak istismar edilecek sistemlere enjekte edilmektedir. İsterseniz "kem küm" etmeden direkt mevzuya geçelim;
Aşağıda gördüğünüz kod silsilesiyle /bin/bash çağırarak konsolda istediğimiz gibi komutlama işlemini yapabiliriz öncelikle bu kodu bir inceleyelim ve olup biteni anlayalım.
#include <stdio.h>int main() {char *name[2];name[0] = "/bin/sh";name[1] = NULL;execve(name[0], name, NULL);}
/* gcc shell2.c -o shell2 -static */
hafif bir C bilgisiyle yukarı da "/bin/sh" çağırdığımızı görebilirsiniz bunun içinde execve fonksiyonu kullanılmaktadır birde gdb ile inceleyelim;
(gdb) set disassembly-flavor intel (gdb) disas main Dump of assembler code for function main: 0x08048254 <+0>: push ebp 0x08048255 <+1>: mov ebp,esp 0x08048257 <+3>: and esp,0xfffffff0 0x0804825a <+6>: sub esp,0x20 0x0804825d <+9>: mov DWORD PTR [esp+0x18],0x80aad68 0x08048265 <+17>: mov DWORD PTR [esp+0x1c],0x0 0x0804826d <+25>: mov eax,DWORD PTR [esp+0x18] 0x08048271 <+29>: mov DWORD PTR [esp+0x8],0x0 0x08048279 <+37>: lea edx,[esp+0x18] 0x0804827d <+41>: mov DWORD PTR [esp+0x4],edx 0x08048281 <+45>: mov DWORD PTR [esp],eax 0x08048284 <+48>: call 0x804f770 <execve> 0x08048289 <+53>: leave 0x0804828a <+54>: ret End of assembler dump.
Evet ilk satırlarda da göreceğimiz üzere klasik bir işlem yapılmaktadır;
0x08048254 <+0>: push ebp 0x08048255 <+1>: mov ebp,esp 0x08048257 <+3>: and esp,0xfffffff0 0x0804825a <+6>: sub esp,0x20push operatoruyle stack yazılan bilgilerden sonra, stack ve base pointer aynı değerler üstünden bilgiler çekiliyor(LIFO), and operatörüyle yapılan mantıksal karşılaştırma ve sonrasında sub fonksiyonuyla bir stack segmentinden 0x20(32) bir değer çıkartılıyor ve değer ilk parametreye aktarıldı. Unutmadan cmp operatörü işlemlerinde subtract yani çıkarma işlemi kullanır.
(gdb) p/d 0xfffffff0 $7 = 4294967280Değeri incelediğimiz karşımıza şu şekilde bir değer çıkıyor bu değerin sonrasında tabi ki çıkarma işlemi ile işlemimiz devam ediyor. Burada "sys call number" dikkat çekmek istediğim bir konu var son satırda da görüldüğü üzere bir fonksyion call ediliyor bunun biraz daha derinine inmek gerekirse;
(gdb) disas execve Dump of assembler code for function execve: 0x0804f770 <+0>: push ebp 0x0804f771 <+1>: mov ebp,esp 0x0804f773 <+3>: mov edx,DWORD PTR [ebp+0x10] 0x0804f776 <+6>: push ebx 0x0804f777 <+7>: mov ecx,DWORD PTR [ebp+0xc] 0x0804f77a <+10>: mov ebx,DWORD PTR [ebp+0x8] 0x0804f77d <+13>: mov eax,0xb <====== 0x0804f782 <+18>: int 0x80 0x0804f784 <+20>: cmp eax,0xfffff000 0x0804f789 <+25>: ja 0x804f78e <execve+30> 0x0804f78b <+27>: pop ebx 0x0804f78c <+28>: pop ebp 0x0804f78d <+29>: ret 0x0804f78e <+30>: mov edx,0xffffffe8 0x0804f794 <+36>: neg eax 0x0804f796 <+38>: mov DWORD PTR gs:[edx],eax 0x0804f799 <+41>: or eax,0xffffffff 0x0804f79c <+44>: jmp 0x804f78b <execve+27> End of assembler dump."0x0804f77d" hex adresine baktığımızda "eax" register bir adres taşıması(0xb) gerçekleşiyor;
~ :/c# cat /usr/include/i386-linux-gnu/asm/unistd_32.h | grep 11 #define __NR_execve 11
Yukarıda "sys call" dosyasını geçen yazıda belirtmiştim çıkan sonuçta tam olarak "eax" registerıyla çağırılan fonksiyonla örtüşür durumda tesadüf değil : )
En son tanımladığımız ve asıl amacımızı belirten "bash" mevzusuna geri dönelim;
(gdb) printf "%s\n", 0x80aad68 /bin/sh
Yukarıda main fonksiyonuna bağlı hex değerini okuduğumuzda bizi tam olarak amacımıza götürüyor üzerine sayfalarca shellcode yazılmış ve halen daha yazılmakta olan "/bin/sh" modülü.
Tam olarak bu offsete yapılan çağrılarda yahut bu hex tanımlanmış olan bu bu fonksiyonu istediğiniz gibi başka hex değerler üstünde kullanabilirsiniz yukarıda print edilen hex değerinin açılını "/bin/sh" demektir ve bu hex değeri okutularak /bin/sh çağırılabilir vs.
Mevzumuz bitmiş değil şimdi bir açılımla elde ettiğimiz değerler üstünden yürüyerek neler yaptığımızı görelim isterseniz(1. yazı seriye dahil);
1. Sistem call number ile fonksiyon numaraları,
2. C kod ile sistem çağrıları,
3. Sağlıklı ve terminate olmayacak şekilde shellcode yazılması(Null Byte),
4. C kod ile /bin/sh çağrısı ve çalışma mantığı,
5. Syscall fonksiyonlarından birinin Debug edilmesi.
//3. Seride görüşmek dileğiyle
0 yorum:
Yorum Gönder