1
2
3
4
5 import socket
6 import logging
7 import xmlrpclib
8 import subprocess
9
10 log = logging.getLogger(__name__)
11
12 try:
13 import bs4
14 import requests
15 import wakeonlan.wol
16 HAVE_FOG = True
17 except ImportError:
18 HAVE_FOG = False
19 log.critical(
20 "The bs4, requests, and wakeonlan Python libraries are required for "
21 "proper FOG integration with Cuckoo (please install them through "
22 "`pip install bs4 requests wakeonlan`)."
23 )
24
25 from lib.cuckoo.common.abstracts import Machinery
26 from lib.cuckoo.common.constants import CUCKOO_GUEST_PORT
27 from lib.cuckoo.common.exceptions import CuckooCriticalError
28 from lib.cuckoo.common.exceptions import CuckooMachineError
29 from lib.cuckoo.common.utils import TimeoutServer
30
32 """Manage physical sandboxes."""
33
34
35 RUNNING = "running"
36 STOPPED = "stopped"
37 ERROR = "error"
38
64
66 """Retrieve all machine info given a machine's name.
67 @param label: machine name.
68 @return: machine dictionary (id, ip, platform, ...).
69 @raises CuckooMachineError: if no machine is available with the given label.
70 """
71 for m in self.machines():
72 if label == m.label:
73 return m
74
75 raise CuckooMachineError("No machine with label: %s." % label)
76
77 - def start(self, label, task):
93
94 - def stop(self, label):
95 """Stops a physical machine.
96 @param label: physical machine name.
97 @raise CuckooMachineError: if unable to stop.
98 """
99
100
101 creds = "%s%%%s" % (
102 self.options.physical.user, self.options.physical.password
103 )
104
105 if self._status(label) == self.RUNNING:
106 log.debug("Rebooting machine: %s.", label)
107 machine = self._get_machine(label)
108
109 args = [
110 "net", "rpc", "shutdown", "-I", machine.ip,
111 "-U", creds, "-r", "-f", "--timeout=5"
112 ]
113 output = subprocess.check_output(args)
114
115 if "Shutdown of remote machine succeeded" not in output:
116 raise CuckooMachineError("Unable to initiate RPC request")
117 else:
118 log.debug("Reboot success: %s." % label)
119
120
121 self.fog_queue_task(label)
122
124 """Lists physical machines installed.
125 @return: physical machine names list.
126 """
127 active_machines = []
128 for machine in self.machines():
129 if self._status(machine.label) == self.RUNNING:
130 active_machines.append(machine.label)
131
132 return active_machines
133
135 """Gets current status of a physical machine.
136 @param label: physical machine name.
137 @return: status string.
138 """
139
140
141
142 log.debug("Getting status for machine: %s.", label)
143 machine = self._get_machine(label)
144
145
146
147
148
149 url = "http://{0}:{1}".format(machine.ip, CUCKOO_GUEST_PORT)
150 server = TimeoutServer(url, allow_none=True, timeout=60)
151
152 try:
153 status = server.get_status()
154 except xmlrpclib.Fault as e:
155
156 log.debug("Agent error: %s (%s) (Error: %s).",
157 machine.id, machine.ip, e)
158 return self.ERROR
159 except socket.error as e:
160
161 log.debug("Agent unresponsive: %s (%s) (Error: %s).",
162 machine.id, machine.ip, e)
163 return self.STOPPED
164 except Exception as e:
165
166 log.debug("Received unknown exception: %s.", e)
167 return self.ERROR
168
169
170
171 if status:
172 return self.RUNNING
173
174 return self.ERROR
175
177 """Wrapper around requests for simplifying FOG API access. Assuming
178 you can call what FOG is providing an API."""
179 url = "http://%s/fog/management/index.php?%s" % (
180 self.options.fog.hostname, uri,
181 )
182
183 data.update({
184 "uname": self.options.fog.username,
185 "upass": self.options.fog.password,
186 })
187
188 return requests.post(url, data=data)
189
191 """Initiate by indexing FOG regarding all available machines."""
192 self.fog_machines = {}
193 if not HAVE_FOG or self.options.fog.hostname == "none":
194 return
195
196
197 r = self.fog_query("node=tasks&sub=listhosts")
198
199
200 b = bs4.BeautifulSoup(r.content, "html.parser")
201 if not b.find_all("table"):
202 raise CuckooCriticalError(
203 "The supplied FOG username and/or password do not allow us "
204 "to login into FOG, please configure the correct credentials."
205 )
206
207
208
209
210 for row in b.find_all("table")[0].find_all("tr")[1:]:
211 hostname, macaddr, download, upload, advanced = row.find_all("td")
212 self.fog_machines[hostname.text] = (
213 macaddr.text, next(download.children).attrs["href"][1:],
214 )
215
216
217 for machine in self.machines():
218 if machine.label not in self.fog_machines:
219 raise CuckooMachineError(
220 "The physical machine %s has not been defined in FOG, "
221 "please investigate and configure the configuration "
222 "correctly." % machine.label
223 )
224
226 """Queues a task with FOG to deploy the given machine after reboot."""
227 if hostname in self.fog_machines:
228 macaddr, download = self.fog_machines[hostname]
229 self.fog_query(download)
230
232 """Start a machine that's currently shutdown."""
233 if hostname in self.fog_machines:
234 macaddr, download = self.fog_machines[hostname]
235 wakeonlan.wol.send_magic_packet(macaddr)
236