This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification.
Student ID: SLAE-885
Assignment number: 5.3
Github repo:

In the third and final part of assignment 5 in SLAE, I’ll be analyzing the linux/x86/exec module using Ndisasm.

Ndisasm comes with Nasm assembler, unlike GDB and Libemu it doesn’t execute any code, just converts shellcode to the corresponding instructions, so we’ll be analyzing the instructions by hand.

1. Setting payload parameters

[email protected]:~# msfvenom -p linux/x86/exec --payload-options  
Options for payload/linux/x86/exec:  
       Name: Linux Execute Command  
     Module: payload/linux/x86/exec  
   Platform: Linux  
       Arch: x86  
Needs Admin: No  
 Total size: 36  
       Rank: Normal  
Provided by:  
    vlad902 <[email protected]>  
Basic options:  
Name  Current Setting  Required  Description  
----  ---------------  --------  -----------  
CMD                    yes       The command string to execute  
  Execute an arbitrary command  

Payload requires a single command, so let’s use something simple like /bin/date.

[email protected]:~# msfvenom -p linux/x86/exec CMD=/bin/date -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: 45 bytes  
Final size of c file: 213 bytes  
unsigned char buf[] =   

2. Analyzing payload with Ndisasm

[email protected]:~# echo -ne "\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\x0a\x00\x00\x00\x2f\x62\x69\x6e\x2f\x64\x61\x74\x65\x00\x57\x53\x89\xe1\xcd\x80" | ndisasm -u -  
00000000  6A0B              push byte +0xb  
00000002  58                pop eax  
00000003  99                cdq  
00000004  52                push edx  
00000005  66682D63          push word 0x632d  
00000009  89E7              mov edi,esp  
0000000B  682F736800        push dword 0x68732f  
00000010  682F62696E        push dword 0x6e69622f  
00000015  89E3              mov ebx,esp  
00000017  52                push edx  
00000018  E80A000000        call dword 0x27  
0000001D  2F                das  
0000001E  62696E            bound ebp,[ecx+0x6e]  
00000021  2F                das  
00000022  6461              fs popad  
00000024  7465              jz 0x8b  
00000026  005753            add [edi+0x53],dl  
00000029  89E1              mov ecx,esp  
0000002B  CD80              int 0x80  

Okay let’s go through every few commands and get an idea what’s going on.

push byte +0xb  
pop eax                 ; EAX = 0xb  
cdq                     ; Extend EAX -> EDX = 0x0  
push edx                ; Push zero byte  

Nothing special here so far.

push word 0x632d        ; Translates to "-c"  
mov edi,esp             ; Make EDI point to top of stack, which contains "-c" terminated with a null character  

This part is constructing the command to be executed, -c will execute the command.

push dword 0x68732f     ; PUSH "hs/"  
push dword 0x6e69622f   ; PUSH "nib/"  
mov ebx,esp             ; Make EBX point to top of stack "/bin/sh -c"  
push edx                ; Push another NULL byte  

Next part is interesting, code tells us to jump to instruction 27, but there isn’t one! Reason is that there is a string starting at 1D till 27.

[email protected]:~# echo -ne "\x2f\x62\x69\x6e\x2f\x64\x61\x74\x65\x00"  

Remaining instructions (after 27) are interpreted incorrectly, so let’s pipe them again to ndisasm and come up with the correct instructions.

[email protected]:~# echo -ne "\x57\x53\x89\xe1\xcd\x80" | ndisasm -u -  
00000000  57                push edi  
00000001  53                push ebx  
00000002  89E1              mov ecx,esp  
00000004  CD80              int 0x80  

Awesome, this makes more sense now. Let’s rewrite the assembly code in a more understandable structure.

00000000    push byte +0xb  
00000002    pop eax                 ; EAX = 0xb  
00000003    cdq                     ; Extend EAX -> EDX = 0x0  
00000004    push edx                ; Push zero byte  
00000005    push word 0x632d        ; Translates to "-c"  
00000009    mov edi,esp             ; Make EDI point to top of stack, which contains "-c" terminated with a null character  
0000000B    push dword 0x68732f     ; PUSH "hs/"  
00000010    push dword 0x6e69622f   ; PUSH "nib/"  
00000015    mov ebx,esp             ; Make EBX point to top of stack "/bin/sh"  
00000017    push edx                ; Push another NULL byte  
00000018    call dword 0x27         ; Address of "/bin/date" is pushed onto stack through "call"  
0000001D    "/bin/date"             ; Not executed  
00000027    push edi                ; Push pointer to "-c"  
00000027    push ebx                ; Push pointer to "/bin/sh"  
00000029    mov ecx,esp             ; ECX point to top of stack  
                                    ; This is the "meat" of the shellcode, ECX points to [addr1][addr2][addr3][null]  
                                    ; addr1 points to /bin/sh (0B, 10 and 15)  
                                    ; addr2 points to -c (05 and 09)  
                                    ; addr3 points to "/bin/date" (18 and 1D)  
                                    ; NULL indicates end of argv[]  
0000002B    int 0x80                ; EAX = 0xb, which is syscall for execve()  
                                    ; EBX points to argv[0]  
                                    ; ECX points to &argv[0]  


1. EAX is set to 0xb, which is the syscall number for execve()
2. EBX points to /bin/sh
3. ECX points to argv[] which is { "/bin/sh", "-c", "/bin/date" }

That’s it!

- Abatchy