| 437 | | sub _agentHandleFault { |
|---|
| 438 | | |
|---|
| 439 | | # Extract arguments. |
|---|
| 440 | | my ($class, $res) = @_; |
|---|
| 441 | | |
|---|
| 442 | | |
|---|
| 443 | | #NOTE: In the future we may want to have this check first to see if |
|---|
| 444 | | #the error is specific to a failed connection (by regexing the error |
|---|
| 445 | | #message. But for now, we have no evidence that multiple errors will |
|---|
| 446 | | #occur in other circumstances. |
|---|
| 447 | | $globalAgentErrorCount++; |
|---|
| 448 | | |
|---|
| 449 | | # Construct error message. |
|---|
| 450 | | # Figure out if the error occurred in transport or over |
|---|
| 451 | | # on the other side. |
|---|
| 452 | | my $errMsg = $class->transport->status; # Assume transport error. |
|---|
| 453 | | |
|---|
| 454 | | if (ref $res) { |
|---|
| 455 | | $errMsg = $res->faultcode . ": ". $res->faultstring . "\n"; |
|---|
| 456 | | } |
|---|
| 457 | | |
|---|
| 458 | | if (!$SUPPRESS_ERRORS) { |
|---|
| 459 | | $LOG->warn("Error occurred during processing. " . $errMsg); |
|---|
| 460 | | Carp::carp __PACKAGE__ . "->_handleFault(): Error occurred during processing.\n" . $errMsg; |
|---|
| 461 | | } |
|---|
| 462 | | } |
|---|
| 463 | | |
|---|
| 464 | | sub _handleFault { |
|---|
| 465 | | |
|---|
| 466 | | # Extract arguments. |
|---|
| 467 | | my ($class, $res) = @_; |
|---|
| 468 | | |
|---|
| 469 | | # Construct error message. |
|---|
| 470 | | # Figure out if the error occurred in transport or over |
|---|
| 471 | | # on the other side. |
|---|
| 472 | | my $errMsg = $class->transport->status; # Assume transport error. |
|---|
| 473 | | |
|---|
| 474 | | if (ref $res) { |
|---|
| 475 | | $errMsg = $res->faultcode . ": ". $res->faultstring . "\n"; |
|---|
| 476 | | } |
|---|
| 477 | | |
|---|
| 478 | | if (!$SUPPRESS_ERRORS) { |
|---|
| 479 | | $LOG->warn("Error occurred during processing. " . $errMsg); |
|---|
| 480 | | Carp::carp __PACKAGE__ . "->_handleFault(): Error occurred during processing.\n" . $errMsg; |
|---|
| 481 | | } |
|---|
| 482 | | } |
|---|
| 483 | | |
|---|
| 484 | | sub _handleFaultAndCleanup { |
|---|
| 485 | | |
|---|
| 486 | | # Extract arguments. |
|---|
| 487 | | my ($class, $res) = @_; |
|---|
| 488 | | |
|---|
| 489 | | # Print fault. |
|---|
| 490 | | _handleFault($class, $res); |
|---|
| 491 | | |
|---|
| 492 | | # Cleanup before dying. |
|---|
| 493 | | _cleanup(); |
|---|
| 494 | | } |
|---|
| 495 | | |
|---|
| 496 | | sub _cleanup { |
|---|
| 497 | | |
|---|
| 498 | | $LOG->info("Cleaning up."); |
|---|
| 499 | | |
|---|
| 500 | | # Mask all possible signals, so that we don't call this function multiple times. |
|---|
| 501 | | $SIG{HUP} = sub { }; |
|---|
| 502 | | $SIG{INT} = sub { }; |
|---|
| 503 | | $SIG{QUIT} = sub { }; |
|---|
| 504 | | $SIG{ABRT} = sub { }; |
|---|
| 505 | | $SIG{PIPE} = sub { }; |
|---|
| 506 | | $SIG{TERM} = sub { }; |
|---|
| 507 | | |
|---|
| 508 | | # XXX: Need to clean this up. |
|---|
| 509 | | my $stubFW = getClientHandle(namespace => "HoneyClient::Manager::FW", |
|---|
| 510 | | fault_handler => \&_handleFault); |
|---|
| 511 | | |
|---|
| 512 | | # XXX: Change this to installDefaultRules(), eventually. |
|---|
| 513 | | # Reset the firewall, to allow everything open. |
|---|
| 514 | | $stubFW->allowAllTraffic(); |
|---|
| 515 | | |
|---|
| 516 | | # This variable may contain a filename that the Manager |
|---|
| 517 | | # would use to dump its entire state information, upon termination. |
|---|
| 518 | | # XXX: May want to change this format/usage, eventually. |
|---|
| 519 | | my $STATE_FILE = getVar(name => "manager_state"); |
|---|
| 520 | | |
|---|
| 521 | | if (length($STATE_FILE) > 0 && |
|---|
| 522 | | defined($globalAgentState)) { |
|---|
| 523 | | $LOG->info("Saving state to '" . $STATE_FILE . "'."); |
|---|
| 524 | | my $dump_file = new IO::File($STATE_FILE, "a"); |
|---|
| 525 | | |
|---|
| 526 | | # XXX: Delete this block, eventually. |
|---|
| 527 | | $Data::Dumper::Terse = 0; |
|---|
| 528 | | $Data::Dumper::Indent = 2; |
|---|
| 529 | | print $dump_file Dumper(thaw(decode_base64($globalAgentState))); |
|---|
| 530 | | $dump_file->close(); |
|---|
| 531 | | } |
|---|
| 532 | | |
|---|
| 533 | | if ($DB_ENABLE && ($clientDbId > 0)) { |
|---|
| 534 | | if (defined($globalAgentState)) { |
|---|
| 535 | | $LOG->info("Saving URL History to Database."); |
|---|
| 536 | | insert_url_history(agent_state => $globalAgentState, |
|---|
| 537 | | client_id => $clientDbId); |
|---|
| 538 | | } |
|---|
| 539 | | |
|---|
| 540 | | # Mark the VM as suspended within the database. |
|---|
| 541 | | HoneyClient::Manager::Database::set_client_suspended($clientDbId); |
|---|
| 542 | | } |
|---|
| 543 | | |
|---|
| 544 | | |
|---|
| 545 | | # XXX: There is an issue where if we try to quit but are in the |
|---|
| 546 | | # process of asynchronously archiving a VM, then the async archive |
|---|
| 547 | | # process will fail. |
|---|
| 548 | | |
|---|
| 549 | | exit; |
|---|
| 550 | | } |
|---|
| 551 | | |
|---|
| 552 | | END { |
|---|
| 553 | | # Make sure all processes in our process group our dead. |
|---|
| 554 | | kill("KILL", -$$); |
|---|
| 555 | | } |
|---|
| 556 | | |
|---|
| 557 | | # XXX: Install the cleanup handler, in case the parent process dies |
|---|
| 558 | | # unexpectedly. |
|---|
| 559 | | $SIG{HUP} = \&_cleanup; |
|---|
| 560 | | $SIG{INT} = \&_cleanup; |
|---|
| 561 | | $SIG{QUIT} = \&_cleanup; |
|---|
| 562 | | $SIG{ABRT} = \&_cleanup; |
|---|
| 563 | | $SIG{PIPE} = \&_cleanup; |
|---|
| 564 | | $SIG{TERM} = \&_cleanup; |
|---|
| 565 | | |
|---|
| 566 | | ####################################################################### |
|---|
| 567 | | # Public Methods Implemented # |
|---|
| 568 | | ####################################################################### |
|---|
| 569 | | |
|---|
| 570 | | =pod |
|---|
| 571 | | |
|---|
| 572 | | =head1 EXPORTS |
|---|
| 573 | | |
|---|
| 574 | | =head2 run() |
|---|
| 575 | | |
|---|
| 576 | | =over 4 |
|---|
| 577 | | |
|---|
| 578 | | # XXX: Fill this in. |
|---|
| 579 | | |
|---|
| 580 | | I<Inputs>: |
|---|
| 581 | | B<$arg> is an optional argument. |
|---|
| 582 | | |
|---|
| 583 | | driver |
|---|
| 584 | | master_vm_config |
|---|
| 585 | | start_state |
|---|
| 586 | | |
|---|
| 587 | | I<Output>: XXX: Fill this in. |
|---|
| 588 | | |
|---|
| 589 | | =back |
|---|
| 590 | | |
|---|
| 591 | | #=begin testing |
|---|
| 592 | | # |
|---|
| 593 | | # XXX: Fill this in. |
|---|
| 594 | | # |
|---|
| 595 | | #=end testing |
|---|
| 596 | | |
|---|
| 597 | | =cut |
|---|
| 598 | | |
|---|
| 599 | | sub run { |
|---|
| 600 | | # Extract arguments. |
|---|
| 601 | | # Hash-based arguments are used, since HoneyClient::Util::SOAP is unable to handle |
|---|
| 602 | | # hash references directly. Thus, flat hashtables are used throughout the code |
|---|
| 603 | | # for consistency. |
|---|
| 604 | | my ($class, %args) = @_; |
|---|
| 605 | | my $agentState = undef; |
|---|
| 606 | | |
|---|
| 607 | | # Sanity check, make sure the master_vm_config is |
|---|
| 608 | | # set. |
|---|
| 609 | | my $argsExist = scalar(%args); |
|---|
| 610 | | if (!$argsExist || |
|---|
| 611 | | !exists($args{'master_vm_config'}) || |
|---|
| 612 | | !defined($args{'master_vm_config'})) { |
|---|
| 613 | | # Get the master_vm_config from the configuration file. |
|---|
| 614 | | $args{'master_vm_config'} = getVar(name => "master_vm_config", |
|---|
| 615 | | namespace => "HoneyClient::Manager::VM"); |
|---|
| 616 | | } |
|---|
| 617 | | |
|---|
| 618 | | for (;;) { |
|---|
| 619 | | print "Starting new session...\n"; |
|---|
| 620 | | $agentState = $class->runSession(%args); |
|---|
| 621 | | $args{'agent_state'} = $agentState; |
|---|
| 622 | | |
|---|
| 623 | | # XXX: Fix this, eventually. |
|---|
| 624 | | $globalAgentState = $agentState; |
|---|
| 625 | | } |
|---|
| 626 | | } |
|---|
| 627 | | |
|---|
| 628 | | sub runSession { |
|---|
| 629 | | |
|---|
| 630 | | # Extract arguments. |
|---|
| 631 | | # Hash-based arguments are used, since HoneyClient::Util::SOAP is unable to handle |
|---|
| 632 | | # hash references directly. Thus, flat hashtables are used throughout the code |
|---|
| 633 | | # for consistency. |
|---|
| 634 | | my ($class, %args) = @_; |
|---|
| 635 | | |
|---|
| 636 | | # XXX: Remove some of these, eventually. |
|---|
| 637 | | my $stubFW = undef; |
|---|
| 638 | | my $stubAgent = undef; |
|---|
| 639 | | my $som = undef; |
|---|
| 640 | | my $ret = undef; |
|---|
| 641 | | # XXX: Need to figure out a way to move this data into the VM object. |
|---|
| 642 | | my $vmCompromised = 0; |
|---|
| 643 | | my $vmStateTable = { }; |
|---|
| 644 | | |
|---|
| 645 | | # Temporary variable to hold each cloned VM. |
|---|
| 646 | | my $vm = undef; |
|---|
| 647 | | |
|---|
| 648 | | # Get a stub connection to the firewall. |
|---|
| 649 | | $stubFW = getClientHandle(namespace => "HoneyClient::Manager::FW", |
|---|
| 650 | | fault_handler => \&_handleFaultAndCleanup); |
|---|
| 651 | | |
|---|
| 652 | | # Open up the firewall initially, to allow the Agent to do an SVN update. |
|---|
| 653 | | #FIXME: This needs to be more limited for the multi-vm case, and should probably |
|---|
| 654 | | # just be included by making the default rules require no action |
|---|
| 655 | | $stubFW->allowAllTraffic(); |
|---|
| 656 | | |
|---|
| 657 | | # Check disk space. |
|---|
| 658 | | checkSpaceAvailable(); |
|---|
| 659 | | |
|---|
| 660 | | # Create a new cloned VM. |
|---|
| 661 | | $vm = HoneyClient::Manager::VM::Clone->new(); |
|---|
| 662 | | |
|---|
| 663 | | #Register Client with the Honeyclient Database |
|---|
| 664 | | if ($DB_ENABLE) { |
|---|
| 665 | | eval { |
|---|
| 666 | | dbRegisterClient($vm); |
|---|
| 667 | | $clientDbId = $vm->database_id; |
|---|
| 668 | | }; |
|---|
| 669 | | if ($@ || ($vm->database_id == 0) || !defined($vm->database_id)) { |
|---|
| 670 | | $vm->database_id(0); #$DB_FAILURE |
|---|
| 671 | | $LOG->warn("Failure Inserting Client Object:\n$@"); |
|---|
| 672 | | } |
|---|
| 673 | | } |
|---|
| 674 | | |
|---|
| 675 | | # Build our VM's connection table. |
|---|
| 676 | | # Note: We assume our VM has a single MAC address |
|---|
| 677 | | # and a single IP address. |
|---|
| 678 | | $vmStateTable->{$vm->name}->{sources}->{$vm->mac_address}->{$vm->ip_address} = { |
|---|
| 679 | | # XXX: We assume we can't pinpoint what source TCP ports the |
|---|
| 680 | | # corresponding driver will need. (We may want to get this |
|---|
| 681 | | # information eventually from the Agent, as part of Driver::next().) |
|---|
| 682 | | 'tcp' => [80,443], |
|---|
| 683 | | }; |
|---|
| 684 | | |
|---|
| 685 | | print "VM State Table:\n"; |
|---|
| 686 | | # Make Dumper format more verbose. |
|---|
| 687 | | $Data::Dumper::Terse = 0; |
|---|
| 688 | | $Data::Dumper::Indent = 2; |
|---|
| 689 | | print Dumper($vmStateTable) . "\n"; |
|---|
| 690 | | |
|---|
| 691 | | # Initialize the firewall. |
|---|
| 692 | | $stubFW->installDefaultRules(); |
|---|
| 693 | | |
|---|
| 694 | | # Add new chain, per cloned VM. |
|---|
| 695 | | $stubFW->addChain($vmStateTable); |
|---|
| 696 | | |
|---|
| 697 | | sleep (2); |
|---|
| 698 | | |
|---|
| 699 | | # Recreate the client stub; handle faults. |
|---|
| 700 | | $stubAgent = getClientHandle(namespace => "HoneyClient::Agent", |
|---|
| 701 | | address => $vm->ip_address, |
|---|
| 702 | | fault_handler => \&_handleFaultAndCleanup); |
|---|
| 703 | | |
|---|
| 704 | | # If supported, get a URL list from the database. |
|---|
| 705 | | if ($DB_ENABLE && ($vm->database_id > 0)) { |
|---|
| 706 | | $args{'agent_state'} = get_urls($vm, $args{'agent_state'}, $args{'driver'}); |
|---|
| 707 | | } |
|---|
| 708 | | |
|---|
| 709 | | # Call updateState() first, to seed initial data. |
|---|
| 710 | | # TODO: Need to support asynchronous updates (url adding) |
|---|
| 711 | | # from user input. |
|---|
| 712 | | print "Calling updateState()...\n"; |
|---|
| 713 | | $som = $stubAgent->updateState($args{'agent_state'}); |
|---|
| 714 | | |
|---|
| 715 | | # Recreate the client stub; ignore faults. |
|---|
| 716 | | $stubAgent = getClientHandle(namespace => "HoneyClient::Agent", |
|---|
| 717 | | address => $vm->ip_address, |
|---|
| 718 | | fault_handler => \&_agentHandleFault); |
|---|
| 719 | | |
|---|
| 720 | | # Recreate the firewall stub; ignore faults. |
|---|
| 721 | | $stubFW = getClientHandle(namespace => "HoneyClient::Manager::FW", |
|---|
| 722 | | fault_handler => \&_handleFault); |
|---|
| 723 | | |
|---|
| 724 | | for (my $counter = 1;; $counter++) { |
|---|
| 725 | | |
|---|
| 726 | | # XXX: This isn't a valid assumption! |
|---|
| 727 | | # From this point on, catch all errors generated and |
|---|
| 728 | | # assume that the Agent's watchdog process will recover. |
|---|
| 729 | | eval { |
|---|
| 730 | | print "Calling getStatus()...\n"; |
|---|
| 731 | | $som = $stubAgent->getStatus(); |
|---|
| 732 | | print "Result:\n"; |
|---|
| 733 | | $ret = thaw(decode_base64($som->result())); |
|---|
| 734 | | # Make Dumper format more verbose. |
|---|
| 735 | | $Data::Dumper::Terse = 0; |
|---|
| 736 | | $Data::Dumper::Indent = 2; |
|---|
| 737 | | print Dumper($ret->{$args{'driver'}}->{status}); |
|---|
| 738 | | #print Dumper($ret); |
|---|
| 739 | | |
|---|
| 740 | | # Derive current agent state from full status. |
|---|
| 741 | | my @driverNames = keys(%{$ret}); |
|---|
| 742 | | my $state = {}; |
|---|
| 743 | | foreach my $driverName (@driverNames) { |
|---|
| 744 | | $state->{$driverName} = $ret->{$driverName}->{'state'}; |
|---|
| 745 | | } |
|---|
| 746 | | $args{'agent_state'} = encode_base64(nfreeze($state)); |
|---|
| 747 | | $globalAgentState = $args{'agent_state'}; |
|---|
| 748 | | #print "GlobalAgentState:\n"; |
|---|
| 749 | | #print Dumper(thaw(decode_base64($globalAgentState))); |
|---|
| 750 | | |
|---|
| 751 | | # Check to see if Agent::run() thread has stopped |
|---|
| 752 | | # and that a compromise was detected. |
|---|
| 753 | | if (!$ret->{$args{'driver'}}->{status}->{is_running}) { |
|---|
| 754 | | if ($ret->{$args{'driver'}}->{status}->{is_compromised}) { |
|---|
| 755 | | # Check to see if the VM has been compromised. |
|---|
| 756 | | print "WARNING: VM HAS BEEN COMPROMISED!\n"; |
|---|
| 757 | | my $vmName = $vm->name; |
|---|
| 758 | | $vmCompromised = 1; |
|---|
| 759 | | |
|---|
| 760 | | my $fingerprint = $ret->{$args{'driver'}}->{status}->{fingerprint}; |
|---|
| 761 | | $LOG->warn("VM Compromised. Last Resource (" . $fingerprint->{'last_resource'} . ")"); |
|---|
| 762 | | |
|---|
| 763 | | # Dump the fingerprint to a file, if needed. |
|---|
| 764 | | # XXX: May want to change this format/usage, eventually. |
|---|
| 765 | | my $COMPROMISE_FILE = getVar(name => "fingerprint_dump"); |
|---|
| 766 | | if (length($COMPROMISE_FILE) > 0 && |
|---|
| 767 | | defined($fingerprint)) { |
|---|
| 768 | | $LOG->info("Saving fingerprint to '" . $COMPROMISE_FILE . "'."); |
|---|
| 769 | | my $dump_file = new IO::File($COMPROMISE_FILE, "a"); |
|---|
| 770 | | |
|---|
| 771 | | # XXX: Delete this block, eventually. |
|---|
| 772 | | $Data::Dumper::Terse = 0; |
|---|
| 773 | | $Data::Dumper::Indent = 2; |
|---|
| 774 | | print $dump_file "\$vmName = \"" . $vmName . "\";\n"; |
|---|
| 775 | | print $dump_file Dumper($fingerprint); |
|---|
| 776 | | $dump_file->close(); |
|---|
| 777 | | } |
|---|
| 778 | | |
|---|
| 779 | | # Archive the VM. |
|---|
| 780 | | $LOG->info("Archiving VM..."); |
|---|
| 781 | | $vm->archive(); |
|---|
| 782 | | |
|---|
| 783 | | # Insert Compromised Fingerprint into DB. |
|---|
| 784 | | if ($DB_ENABLE && ($vm->database_id > 0)) { |
|---|
| 785 | | # Put URL History in database. |
|---|
| 786 | | $LOG->info("Saving URL History to Database."); |
|---|
| 787 | | $args{'agent_state'} = insert_url_history(agent_state => $args{'agent_state'}, |
|---|
| 788 | | client_id => $vm->database_id); |
|---|
| 789 | | $globalAgentState = $args{'agent_state'}; |
|---|
| 790 | | |
|---|
| 791 | | # Delete the 'last_resource' attribute. |
|---|
| 792 | | delete $fingerprint->{last_resource}; |
|---|
| 793 | | |
|---|
| 794 | | # Associate the client who has this fingerprint. |
|---|
| 795 | | $fingerprint->{client_id} = $vm->database_id; |
|---|
| 796 | | |
|---|
| 797 | | $LOG->info("Inserting Fingerprint Into Database."); |
|---|
| 798 | | my $fingerprint_id = undef; |
|---|
| 799 | | eval { |
|---|
| 800 | | $fingerprint_id = HoneyClient::Manager::Database::insert_fingerprint($fingerprint); |
|---|
| 801 | | }; |
|---|
| 802 | | if ($@ || ($fingerprint_id == 0) || !defined($fingerprint_id)) { |
|---|
| 803 | | $LOG->warn("Failure Inserting Fingerprint Object:\n$@"); |
|---|
| 804 | | } |
|---|
| 805 | | |
|---|
| 806 | | $LOG->info("Database Insert Successful."); |
|---|
| 807 | | } |
|---|
| 808 | | # The VM should be suspended, at this point. Clear out the global DB ID, so |
|---|
| 809 | | # that our cleanup code doesn't re-mark the VM as suspended. |
|---|
| 810 | | $clientDbId = 0; |
|---|
| 811 | | |
|---|
| 812 | | # Make sure VM is suspended. |
|---|
| 813 | | $vm = undef; |
|---|
| 814 | | |
|---|
| 815 | | return; # Return out of eval block. |
|---|
| 816 | | } else { |
|---|
| 817 | | print "VM Integrity Check: OK!\n"; |
|---|
| 818 | | |
|---|
| 819 | | # Check to see if any links remain to be processed by the |
|---|
| 820 | | # Agent. |
|---|
| 821 | | if (!$ret->{$args{'driver'}}->{status}->{links_remaining}) { |
|---|
| 822 | | |
|---|
| 823 | | # If supported, get more URLs from the database. |
|---|
| 824 | | if ($DB_ENABLE && ($vm->database_id > 0)) { |
|---|
| 825 | | # Put URL History in database. |
|---|
| 826 | | $LOG->info("Saving URL History to Database."); |
|---|
| 827 | | $args{'agent_state'} = insert_url_history(agent_state => $args{'agent_state'}, |
|---|
| 828 | | client_id => $vm->database_id); |
|---|
| 829 | | |
|---|
| 830 | | $args{'agent_state'} = get_urls($vm, $args{'agent_state'}, $args{'driver'}); |
|---|
| 831 | | $globalAgentState = $args{'agent_state'}; |
|---|
| 832 | | print "Calling updateState()...\n"; |
|---|
| 833 | | $som = $stubAgent->updateState($args{'agent_state'}); |
|---|
| 834 | | } else { |
|---|
| 835 | | $LOG->info("All URLs exhausted. Shutting down Manager."); |
|---|
| 836 | | $vm = undef; |
|---|
| 837 | | _cleanup(); |
|---|
| 838 | | } |
|---|
| 839 | | } else { |
|---|
| 840 | | # The Agent::run() thread has stopped; we assume |
|---|
| 841 | | # it's because the Agent is waiting for the firewall |
|---|
| 842 | | # to allow access to the new targets. |
|---|
| 843 | | |
|---|
| 844 | | # Delete the old firewall rules, based upon existing |
|---|
| 845 | | # targets. |
|---|
| 846 | | $stubFW->deleteRules($vmStateTable); |
|---|
| 847 | | |
|---|
| 848 | | # Get the new targets from the Agent. |
|---|
| 849 | | $vmStateTable->{$vm->name}->{targets} = $ret->{$args{'driver'}}->{next}->{targets}; |
|---|
| 850 | | #$vmStateTable->{$vm->name}->{targets} = '0.0.0.0'; |
|---|
| 851 | | |
|---|
| 852 | | print "VM State Table:\n"; |
|---|
| 853 | | # Make Dumper format more verbose. |
|---|
| 854 | | $Data::Dumper::Terse = 0; |
|---|
| 855 | | $Data::Dumper::Indent = 2; |
|---|
| 856 | | print Dumper($vmStateTable) . "\n"; |
|---|
| 857 | | |
|---|
| 858 | | # Add the new targets from the Agent. |
|---|
| 859 | | $stubFW->addRules($vmStateTable); |
|---|
| 860 | | |
|---|
| 861 | | print "Calling run()...\n"; |
|---|
| 862 | | $som = $stubAgent->run(driver_name => $args{'driver'}); |
|---|
| 863 | | } |
|---|
| 864 | | } |
|---|
| 865 | | } |
|---|
| 866 | | }; |
|---|
| 867 | | if ($@) { |
|---|
| 868 | | print "Error: $@\n"; |
|---|
| 869 | | my $resetSuccessful = 0; |
|---|
| 870 | | while (!$resetSuccessful) { |
|---|
| 871 | | print "Resetting firewall...\n"; |
|---|
| 872 | | eval { |
|---|
| 873 | | # We assume the error was caused by some sort of communications |
|---|
| 874 | | # problem with the Agent. Assume the Agent's watchdog will restart |
|---|
| 875 | | # the daemon, in which case, we indefinately try to reset the |
|---|
| 876 | | # firewall accordingly. |
|---|
| 877 | | $stubFW->installDefaultRules(); |
|---|
| 878 | | $stubFW->addChain($vmStateTable); |
|---|
| 879 | | $stubFW->addRules($vmStateTable); |
|---|
| 880 | | }; |
|---|
| 881 | | if (!$@) { |
|---|
| 882 | | $resetSuccessful = 1; |
|---|
| 883 | | } else { |
|---|
| 884 | | sleep (3); |
|---|
| 885 | | } |
|---|
| 886 | | } |
|---|
| 887 | | } |
|---|
| 888 | | if ($vmCompromised) { |
|---|
| 889 | | # Reset the FW state table. |
|---|
| 890 | | $vmStateTable = ( ); |
|---|
| 891 | | return $args{'agent_state'}; |
|---|
| 892 | | } |
|---|
| 893 | | if ($globalAgentErrorCount >= getVar(name => "max_agent_error_count")) { |
|---|
| 894 | | if ($DB_ENABLE && ($vm->database_id > 0)) { |
|---|
| 895 | | # Mark the VM as suspended within the database. |
|---|
| 896 | | my $dt = DateTime::HiRes->now(time_zone => "local"); |
|---|
| 897 | | HoneyClient::Manager::Database::set_client_suspicious({ |
|---|
| 898 | | client_id => $vm->database_id, |
|---|
| 899 | | compromise => $dt->ymd('-').'T'.$dt->hms(':'), |
|---|
| 900 | | }); |
|---|
| 901 | | } |
|---|
| 902 | | # The VM should be suspended after the return. Clear out the global DB ID, so |
|---|
| 903 | | # that our cleanup code doesn't re-mark the VM as suspended. |
|---|
| 904 | | $clientDbId = 0; |
|---|
| 905 | | |
|---|
| 906 | | $globalAgentErrorCount = 0; |
|---|
| 907 | | # Reset the FW state table. |
|---|
| 908 | | $vmStateTable = ( ); |
|---|
| 909 | | return $args{'agent_state'}; |
|---|
| 910 | | } |
|---|
| 911 | | print "Sleeping for 2s...\n"; |
|---|
| 912 | | sleep (2); |
|---|
| 913 | | } |
|---|
| 914 | | } |
|---|
| 915 | | |
|---|
| 916 | | sub insert_url_history { |
|---|
| 917 | | |
|---|
| 918 | | # Extract arguments. |
|---|
| 919 | | my %args = @_; |
|---|
| 920 | | |
|---|
| 921 | | my $state = thaw(decode_base64($args{'agent_state'})); |
|---|
| 922 | | my $driver = undef; |
|---|
| 923 | | foreach my $key (keys %$state) { |
|---|
| 924 | | if ($state->{$key}) { |
|---|
| 925 | | $driver = $key; |
|---|
| 926 | | last; |
|---|
| 927 | | } |
|---|
| 928 | | } |
|---|
| 929 | | |
|---|
| 930 | | # Set the client ID. |
|---|
| 931 | | $state->{$driver}->{'client_id'} = $args{'client_id'}; |
|---|
| 932 | | |
|---|
| 933 | | # XXX: Delete this, eventually. |
|---|
| 934 | | #use Data::Dumper; |
|---|
| 935 | | #$LOG->info("agent_state = " . Data::Dumper::Dumper($state)); |
|---|
| 936 | | |
|---|
| 937 | | my $num_urls_inserted = HoneyClient::Manager::Database::insert_history_urls($state->{$driver}); |
|---|
| 938 | | $LOG->info($num_urls_inserted . " URL(s) Inserted."); |
|---|
| 939 | | |
|---|
| 940 | | # Flush the URL history, after committing to the database. |
|---|
| 941 | | $state->{$driver}->{'links_visited'} = {}; |
|---|
| 942 | | return encode_base64(nfreeze($state)); |
|---|
| 943 | | } |
|---|
| 944 | | |
|---|
| 945 | | sub dbRegisterClient { |
|---|
| 946 | | my $vm = shift; |
|---|
| 947 | | my $dt = DateTime::HiRes->now(time_zone => "local"); |
|---|
| 948 | | |
|---|
| 949 | | # Register the VM with the DB |
|---|
| 950 | | my $client = { |
|---|
| 951 | | cid => $vm->name, |
|---|
| 952 | | status => 'running', |
|---|
| 953 | | host => { |
|---|
| 954 | | org => getVar(name => "organization"), |
|---|
| 955 | | hostname => Sys::Hostname::Long::hostname_long, |
|---|
| 956 | | ip => Sys::HostIP->ip, |
|---|
| 957 | | }, |
|---|
| 958 | | os => { |
|---|
| 959 | | name => 'Default Windows XP SP2', |
|---|
| 960 | | shortname => 'Microsoft Windows', |
|---|
| 961 | | version => 'XP Professional', |
|---|
| 962 | | #os_patches => [{ |
|---|
| 963 | | # name => 'Service Pack 2', |
|---|
| 964 | | #}], |
|---|
| 965 | | os_applications => [{ |
|---|
| 966 | | manufacturer => 'Microsoft', |
|---|
| 967 | | shortname => 'Internet Explorer', |
|---|
| 968 | | version => '6', |
|---|
| 969 | | }], |
|---|
| 970 | | }, |
|---|
| 971 | | start => $dt->ymd('-').'T'.$dt->hms(':'), |
|---|
| 972 | | }; |
|---|
| 973 | | $vm->database_id(HoneyClient::Manager::Database::insert_client($client)); |
|---|
| 974 | | } |
|---|
| 975 | | |
|---|
| 976 | | sub get_urls { |
|---|
| 977 | | my $vm = shift; |
|---|
| 978 | | my $agent_state = shift; |
|---|
| 979 | | my $driver = shift; |
|---|
| 980 | | |
|---|
| 981 | | # Decode and thaw the initial agent state. |
|---|
| 982 | | my $state = thaw(decode_base64($agent_state)); |
|---|
| 983 | | |
|---|
| 984 | | my $queue_url_list = {}; |
|---|
| 985 | | $LOG->info("Waiting for new URLs from database."); |
|---|
| 986 | | # XXX: We hardcode the value of 10 URLs to request; this will change, eventually. |
|---|
| 987 | | $queue_url_list = HoneyClient::Manager::Database::get_queue_urls(10, $vm->database_id); |
|---|
| 988 | | my $remoteLinksExist = scalar(%{$queue_url_list}); |
|---|
| 989 | | |
|---|
| 990 | | # While we have no local URLs and no URLs from the database, re-query the database. |
|---|
| 991 | | while (!defined($state->{$driver}->{next_link_to_visit}) && |
|---|
| 992 | | !$remoteLinksExist) { |
|---|
| 993 | | |
|---|
| 994 | | # XXX: Hardcoded timeout. |
|---|
| 995 | | sleep (2); |
|---|
| 996 | | # XXX: Trap/ignore all errors and simply retry. |
|---|
| 997 | | eval { |
|---|
| 998 | | $queue_url_list = HoneyClient::Manager::Database::get_queue_urls(10, $vm->database_id); |
|---|
| 999 | | $remoteLinksExist = scalar(%{$queue_url_list}); |
|---|
| 1000 | | }; |
|---|
| 1001 | | } |
|---|
| 1002 | | |
|---|
| 1003 | | # If we do have URLs from t |
|---|