File Coverage

blib/lib/Metabrik/Remote/Sandbox.pm
Criterion Covered Total %
statement 9 213 4.2
branch 0 132 0.0
condition 0 5 0.0
subroutine 3 23 13.0
pod 5 20 25.0
total 17 393 4.3


line stmt bran cond sub pod time code
1             #
2             # $Id$
3             #
4             # remote::sandbox Brik
5             #
6             package Metabrik::Remote::Sandbox;
7 1     1   666 use strict;
  1         3  
  1         34  
8 1     1   5 use warnings;
  1         2  
  1         80  
9              
10 1     1   8 use base qw(Metabrik::Remote::Winexe);
  1         1  
  1         2286  
11              
12             sub brik_properties {
13             return {
14 0     0 1   revision => '$Revision$',
15             tags => [ qw(unstable) ],
16             author => 'GomoR ',
17             license => 'http://opensource.org/licenses/BSD-3-Clause',
18             attributes => {
19             datadir => [ qw(datadir) ],
20             es_nodes => [ qw(nodes) ],
21             es_indices => [ qw(indices) ],
22             win_host => [ qw(host) ],
23             win_user => [ qw(user) ],
24             win_password => [ qw(password) ],
25             vm_id => [ qw(id) ],
26             vm_snapshot_name => [ qw(name) ],
27             use_regex_match => [ qw(0|1) ],
28             _client => [ qw(INTERNAL) ],
29             _ci => [ qw(INTERNAL) ],
30             _em => [ qw(INTERNAL) ],
31             _fb => [ qw(INTERNAL) ],
32             _sf => [ qw(INTERNAL) ],
33             _fr => [ qw(INTERNAL) ],
34             _cs => [ qw(INTERNAL) ],
35             _ce => [ qw(INTERNAL) ],
36             _rs => [ qw(INTERNAL) ],
37             _rw => [ qw(INTERNAL) ],
38             _rwd => [ qw(INTERNAL) ],
39             _sv => [ qw(INTERNAL) ],
40             _fs => [ qw(INTERNAL) ],
41             },
42             attributes_default => {
43             es_nodes => [ qw(http://localhost:9200) ],
44             es_indices => 'winlogbeat-*',
45             vm_snapshot_name => '666_before_malware',
46             use_regex_match => 0,
47             },
48             commands => {
49             create_client => [ ],
50             save_elasticsearch_state => [ qw(name|OPTIONAL) ],
51             restore_elasticsearch_state => [ ],
52             restart_sysmon_collector => [ ],
53             upload_and_execute => [ qw(file) ],
54             diff_ps_state => [ qw(processes|OPTIONAL) ],
55             diff_ps_network_connections => [ qw(processes|OPTIONAL) ],
56             diff_ps_target_filename_created => [ qw(processes|OPTIONAL) ],
57             diff_ps_registry_value_set => [ qw(processes|OPTIONAL) ],
58             diff_ps_registry_object_added_or_deleted => [ qw(processes|OPTIONAL) ],
59             diff_ps_target_process_accessed => [ qw(processes|OPTIONAL) ],
60             loop_and_download_created_files => [ qw(processes|OPTIONAL) ],
61             memdump_as_volatility => [ qw(output|OPTIONAL) ],
62             stop_vm => [ ],
63             restore_vm => [ ],
64             },
65             require_modules => {
66             'Metabrik::System::File' => [ ],
67             'Metabrik::String::Password' => [ ],
68             'Metabrik::Client::Smbclient' => [ ],
69             'Metabrik::Client::Elasticsearch' => [ ],
70             'Metabrik::Remote::Sysmon' => [ ],
71             'Metabrik::Remote::Winsvc' => [ ],
72             'Metabrik::Remote::Windefend' => [ ],
73             'Metabrik::System::Virtualbox' => [ ],
74             'Metabrik::Forensic::Sysmon' => [ ],
75             },
76             require_binaries => {
77             },
78             optional_binaries => {
79             },
80             need_packages => {
81             },
82             };
83             }
84              
85             sub brik_use_properties {
86 0     0 1   my $self = shift;
87              
88             return {
89 0           attributes_default => {
90             },
91             };
92             }
93              
94             sub brik_preinit {
95 0     0 1   my $self = shift;
96              
97             # Do your preinit here, return 0 on error.
98              
99 0           return $self->SUPER::brik_preinit;
100             }
101              
102             sub brik_init {
103 0     0 1   my $self = shift;
104              
105             # Do your init here, return 0 on error.
106              
107 0           return $self->SUPER::brik_init;
108             }
109              
110             sub create_client {
111 0     0 0   my $self = shift;
112              
113 0 0         if ($self->_client) {
114 0           return 1;
115             }
116              
117 0           my $win_user = $self->win_user;
118 0           my $win_host = $self->win_host;
119 0           my $win_password = $self->win_password;
120 0           my $es_nodes = $self->es_nodes;
121 0           my $vm_id = $self->vm_id;
122 0 0         $self->brik_help_set_undef_arg('win_user', $win_user) or return;
123 0 0         $self->brik_help_set_undef_arg('win_host', $win_host) or return;
124 0 0         $self->brik_help_set_undef_arg('vm_id', $vm_id) or return;
125              
126 0 0         if (! defined($win_password)) {
127 0 0         my $sp = Metabrik::String::Password->new_from_brik_init($self) or return;
128 0 0         $win_password = $sp->prompt or return;
129             }
130              
131 0           $self->host($win_host);
132 0           $self->user($win_user);
133 0           $self->password($win_password);
134              
135 0 0         my $cs = Metabrik::Client::Smbclient->new_from_brik_init($self) or return;
136 0           $cs->host($win_host);
137 0           $cs->user($win_user);
138 0           $cs->password($win_password);
139              
140 0 0         my $ce = Metabrik::Client::Elasticsearch->new_from_brik_init($self) or return;
141 0           $ce->nodes($es_nodes);
142 0 0         $ce->open or return;
143              
144 0 0         my $rs = Metabrik::Remote::Sysmon->new_from_brik_init($self) or return;
145 0           $rs->host($win_host);
146 0           $rs->user($win_user);
147 0           $rs->password($win_password);
148              
149 0 0         my $rw = Metabrik::Remote::Winsvc->new_from_brik_init($self) or return;
150 0           $rw->host($win_host);
151 0           $rw->user($win_user);
152 0           $rw->password($win_password);
153              
154 0 0         my $rwd = Metabrik::Remote::Windefend->new_from_brik_init($self) or return;
155 0           $rwd->host($win_host);
156 0           $rwd->user($win_user);
157 0           $rwd->password($win_password);
158              
159 0 0         my $sv = Metabrik::System::Virtualbox->new_from_brik_init($self) or return;
160 0           $sv->type('gui');
161              
162 0 0         my $fs = Metabrik::Forensic::Sysmon->new_from_brik_init($self) or return;
163 0           $fs->use_regex_match($self->use_regex_match);
164              
165 0           $self->_cs($cs);
166 0           $self->_ce($ce);
167 0           $self->_rs($rs);
168 0           $self->_rw($rw);
169 0           $self->_rwd($rwd);
170 0           $self->_sv($sv);
171 0           $self->_fs($fs);
172              
173 0           return $self->_client(1);
174             }
175              
176             sub save_elasticsearch_state {
177 0     0 0   my $self = shift;
178 0           my ($name) = @_;
179              
180 0 0         $self->brik_help_run_undef_arg('create_client', $self->_client) or return;
181              
182 0           my $ce = $self->_ce;
183 0           my $indices = $self->es_indices;
184              
185 0           return $ce->create_snapshot_for_indices($indices, $name);
186             }
187              
188             sub restore_elasticsearch_state {
189 0     0 0   my $self = shift;
190              
191 0 0         $self->brik_help_run_undef_arg('create_client', $self->_client) or return;
192              
193 0           my $ce = $self->_ce;
194              
195 0           my $indices = $self->es_indices;
196              
197 0 0         $ce->delete_index($indices) or return;
198              
199 0           $ce->restore_snapshot_for_indices($indices);
200              
201             # Waiting for restoration to complete.
202 0           while (! $ce->get_snapshot_state) {
203 0           sleep(1);
204             }
205              
206 0           return 1;
207             }
208              
209             sub restart_sysmon_collector {
210 0     0 0   my $self = shift;
211              
212 0 0         $self->brik_help_run_undef_arg('create_client', $self->_client) or return;
213              
214 0           my $rs = $self->_rs;
215 0 0         $rs->generate_conf or return;
216 0 0         $rs->update_conf or return;
217 0 0         $rs->redeploy or return;
218              
219 0           my $rw = $self->_rw;
220 0 0         $rs->restart('winlogbeat') or return;
221              
222 0           return 1;
223             }
224              
225             sub upload_and_execute {
226 0     0 0   my $self = shift;
227 0           my ($file) = @_;
228              
229 0 0         $self->brik_help_run_undef_arg('create_client', $self->_client) or return;
230 0 0         $self->brik_help_run_undef_arg('upload_and_execute', $file) or return;
231 0 0         $self->brik_help_run_file_not_found('upload_and_execute', $file) or return;
232              
233 0           my $ce = $self->_ce;
234 0           my $sv = $self->_sv;
235 0           my $cs = $self->_cs;
236 0           my $rwd = $self->_rwd;
237 0           my $fs = $self->_fs;
238              
239 0           $self->log->info("upload_and_execute: restoring Elasticsearch state...");
240 0 0         $self->restore_elasticsearch_state or return;
241 0           $self->log->info("upload_and_execute: done.");
242              
243             # We create a restore point if none exists yet.
244             # Or we restore the previous one.
245 0 0         my $list = $sv->snapshot_list($self->vm_id) or return;
246 0           my $found = 0;
247 0           for my $this (@$list) {
248 0 0         if ($this->{name} eq $self->vm_snapshot_name) {
249 0           $found = 1;
250 0           last;
251             }
252             }
253 0 0         if (! $found) {
254 0           $self->log->info("upload_and_execute: snapshoting VM state...");
255 0 0         $sv->snapshot_live($self->vm_id, $self->vm_snapshot_name) or return;
256 0           $self->log->info("upload_and_execute: done.");
257             }
258             else {
259 0           $self->log->info("upload_and_execute: restoring VM state...");
260 0           $sv->stop($self->vm_id);
261 0 0         $sv->snapshot_restore($self->vm_id, $self->vm_snapshot_name) or return;
262 0 0         $sv->start($self->vm_id) or return;
263 0           $self->log->info("upload_and_execute: done.");
264             }
265              
266 0           sleep(5); # Waiting for VM to start.
267              
268 0           $self->log->info("upload_and_execute: disabling Windows Defender...");
269 0 0         $rwd->disable or return;
270 0           $self->log->info("upload_and_execute: done.");
271              
272 0           $self->log->info("upload_and_execute: uploading file...");
273 0 0         $cs->upload($file) or return;
274 0           $self->log->info("upload_and_execute: done.");
275              
276 0           $self->log->info("upload_and_execute: saving sysmon state...");
277 0 0         $fs->save_state or return;
278 0           $self->log->info("upload_and_execute: done.");
279              
280 0           $self->log->info("upload_and_execute: executing malware...");
281 0           $self->execute('"c:\\windows\\temp\\'.$file.'"');
282 0           $self->log->info("upload_and_execute: done.");
283              
284 0           return 1;
285             }
286              
287             sub diff_ps_state {
288 0     0 0   my $self = shift;
289 0           my ($processes) = @_;
290              
291 0 0         $self->brik_help_run_undef_arg('create_client', $self->_client) or return;
292              
293 0 0         if (defined($processes)) {
294 0 0         $self->brik_help_run_invalid_arg('diff_ps_state',
295             $processes, 'ARRAY') or return;
296             }
297              
298 0           my $fs = $self->_fs;
299              
300 0           return $fs->diff_current_state('ps', $processes);
301             }
302              
303             sub diff_ps_network_connections {
304 0     0 0   my $self = shift;
305 0           my ($processes) = @_;
306              
307 0 0         $self->brik_help_run_undef_arg('create_client', $self->_client) or return;
308              
309 0 0         if (defined($processes)) {
310 0 0         $self->brik_help_run_invalid_arg('diff_ps_network_connections',
311             $processes, 'ARRAY') or return;
312             }
313              
314 0           my $fs = $self->_fs;
315              
316 0           return $fs->diff_current_state('ps_network_connections', $processes);
317             }
318              
319             sub diff_ps_target_filename_created {
320 0     0 0   my $self = shift;
321 0           my ($processes) = @_;
322              
323 0 0         $self->brik_help_run_undef_arg('create_client', $self->_client) or return;
324              
325 0 0         if (defined($processes)) {
326 0 0         $self->brik_help_run_invalid_arg('diff_ps_target_filename_created',
327             $processes, 'ARRAY') or return;
328             }
329              
330 0           my $fs = $self->_fs;
331              
332 0           return $fs->diff_current_state('ps_target_filename_created', $processes);
333             }
334              
335             sub diff_ps_registry_value_set {
336 0     0 0   my $self = shift;
337 0           my ($processes) = @_;
338              
339 0 0         $self->brik_help_run_undef_arg('create_client', $self->_client) or return;
340              
341 0 0         if (defined($processes)) {
342 0 0         $self->brik_help_run_invalid_arg('diff_ps_registry_value_set',
343             $processes, 'ARRAY') or return;
344             }
345              
346 0           my $fs = $self->_fs;
347              
348 0           return $fs->diff_current_state('ps_registry_value_set', $processes);
349             }
350              
351             sub diff_ps_registry_object_added_or_deleted {
352 0     0 0   my $self = shift;
353 0           my ($processes) = @_;
354              
355 0 0         $self->brik_help_run_undef_arg('create_client', $self->_client) or return;
356              
357 0 0         if (defined($processes)) {
358 0 0         $self->brik_help_run_invalid_arg('diff_ps_registry_object_added_or_deleted',
359             $processes, 'ARRAY') or return;
360             }
361              
362 0           my $fs = $self->_fs;
363              
364 0           return $fs->diff_current_state('ps_registry_object_added_or_deleted', $processes);
365             }
366              
367             sub diff_ps_target_process_accessed {
368 0     0 0   my $self = shift;
369 0           my ($processes) = @_;
370              
371 0 0         $self->brik_help_run_undef_arg('create_client', $self->_client) or return;
372              
373 0 0         if (defined($processes)) {
374 0 0         $self->brik_help_run_invalid_arg('diff_ps_target_process_accessed',
375             $processes, 'ARRAY') or return;
376             }
377              
378 0           my $fs = $self->_fs;
379              
380 0           return $fs->diff_current_state('ps_target_process_accessed', $processes);
381             }
382              
383             sub loop_and_download_created_files {
384 0     0 0   my $self = shift;
385 0           my ($processes, $output_dir) = @_;
386              
387 0 0         $self->brik_help_run_undef_arg('create_client', $self->_client) or return;
388              
389 0   0       $output_dir ||= defined($self->shell) && $self->shell->full_pwd || '/tmp';
      0        
390              
391 0 0         if (defined($processes)) {
392 0 0         $self->brik_help_run_undef_arg('loop_and_download_created_files', $processes)
393             or return;
394 0 0         $self->brik_help_run_invalid_arg('loop_and_download_created_files', $processes,
395             'ARRAY', 'SCALAR') or return;
396             }
397              
398 0           my $cs = $self->_cs;
399 0           my $fs = $self->_fs;
400              
401 0           $output_dir .= "/download";
402 0 0         my $sf = Metabrik::System::File->new_from_brik_init($self) or return;
403 0 0         $sf->mkdir($output_dir) or return;
404              
405 0           while (1) {
406 0 0         my $diff = $fs->diff_current_state('ps_target_filename_created', $processes)
407             or return;
408              
409 0 0         if (exists($diff->{ps_target_filename_created})) {
410 0           my $created = $diff->{ps_target_filename_created};
411 0           for my $process (keys %$created) {
412 0           for my $file (@{$created->{$process}}) {
  0            
413 0           $self->log->info("loop_and_download_created_files: ".
414             "downloading file [$file]");
415 0           $cs->download_in_background($file, $output_dir);
416             }
417             }
418             }
419             }
420              
421 0           return 1;
422             }
423              
424             sub memdump_as_volatility {
425 0     0 0   my $self = shift;
426              
427 0 0         $self->brik_help_run_undef_arg('create_client', $self->_client) or return;
428              
429 0           my $vm_id = $self->vm_id;
430 0           my $sv = $self->_sv;
431              
432 0 0         my $output = $sv->dumpvmcore($vm_id) or return;
433              
434 0           return $sv->extract_memdump_from_dumpguestcore($output);
435             }
436              
437             sub stop_vm {
438 0     0 0   my $self = shift;
439              
440 0 0         $self->brik_help_run_undef_arg('create_client', $self->_client) or return;
441              
442 0           my $vm_id = $self->vm_id;
443 0           my $sv = $self->_sv;
444              
445 0           return $sv->stop($vm_id);
446             }
447              
448             sub restore_vm {
449 0     0 0   my $self = shift;
450              
451 0 0         $self->brik_help_run_undef_arg('create_client', $self->_client) or return;
452              
453 0           my $vm_id = $self->vm_id;
454 0           my $sv = $self->_sv;
455              
456 0           $sv->stop($vm_id);
457              
458 0           return $sv->snapshot_restore($self->vm_id, $self->vm_snapshot_name);
459             }
460              
461             sub brik_fini {
462 0     0 1   my $self = shift;
463              
464             # Do your fini here, return 0 on error.
465              
466 0           return $self->SUPER::brik_fini;
467             }
468              
469             1;
470              
471             __END__