diff --git a/app/Controller/ShadowAttributesController.php b/app/Controller/ShadowAttributesController.php index 383fbda46..ddb5dade6 100644 --- a/app/Controller/ShadowAttributesController.php +++ b/app/Controller/ShadowAttributesController.php @@ -414,7 +414,6 @@ class ShadowAttributesController extends AppController { // combobox for distribution $count = 0; - $this->set('attrDescriptions', $this->ShadowAttribute->fieldDescriptions); $this->set('typeDefinitions', $this->ShadowAttribute->typeDefinitions); $this->set('categoryDefinitions', $this->ShadowAttribute->categoryDefinitions); } @@ -422,24 +421,37 @@ class ShadowAttributesController extends AppController { public function download($id = null) { $this->ShadowAttribute->id = $id; if (!$this->ShadowAttribute->exists()) { - throw new NotFoundException(__('Invalid ShadowAttribute')); + throw new NotFoundException(__('Invalid Proposal')); } - - $this->ShadowAttribute->read(); - $path = APP . "files" . DS . $this->ShadowAttribute->data['ShadowAttribute']['event_id'] . DS . 'shadow' . DS; - $file = $this->ShadowAttribute->data['ShadowAttribute']['id']; + $sa = $this->ShadowAttribute->find('first', array( + 'recursive' => -1, + 'contain' => array('Event' => array('fields' => array('Event.org', 'Event.distribution', 'Event.id'))), + 'conditions' => array('ShadowAttribute.id' => $id) + )); + if (!$this->_isSiteAdmin() && + $this->Auth->user('org') != + $sa['Event']['org'] && + $sa['Event']['distribution'] == 0) { + throw new UnauthorizedException('You do not have the permission to view this event.'); + } + $this->__downloadAttachment($sa['ShadowAttribute']); + } + + private function __downloadAttachment($shadowAttribute) { + $path = "files" . DS . $shadowAttribute['event_id'] . DS . 'shadow' . DS; + $file = $shadowAttribute['id']; $filename = ''; - if ('attachment' == $this->ShadowAttribute->data['ShadowAttribute']['type']) { - $filename = $this->ShadowAttribute->data['ShadowAttribute']['value']; + if ('attachment' == $shadowAttribute['type']) { + $filename = $shadowAttribute['value']; $fileExt = pathinfo($filename, PATHINFO_EXTENSION); $filename = substr($filename, 0, strlen($filename) - strlen($fileExt) - 1); - } elseif ('malware-sample' == $this->ShadowAttribute->data['ShadowAttribute']['type']) { - $filenameHash = explode('|', $this->ShadowAttribute->data['ShadowAttribute']['value']); + } elseif ('malware-sample' == $shadowAttribute['type']) { + $filenameHash = explode('|', $shadowAttribute['value']); $filename = $filenameHash[0]; $filename = substr($filenameHash[0], strrpos($filenameHash[0], '\\')); $fileExt = "zip"; } else { - throw new NotFoundException(__('ShadowAttribute not an attachment or malware-sample')); + throw new NotFoundException(__('Proposal not an attachment or malware-sample')); } $this->autoRender = false; $this->response->type($fileExt); @@ -463,12 +475,13 @@ class ShadowAttributesController extends AppController { $this->redirect(array('controller' => 'events', 'action' => 'index')); } if ($this->request->is('post')) { + $temp = $this->_getEventData($this->request->data['ShadowAttribute']['event_id']); // Check if there were problems with the file upload // only keep the last part of the filename, this should prevent directory attacks $filename = basename($this->request->data['ShadowAttribute']['value']['name']); $tmpfile = new File($this->request->data['ShadowAttribute']['value']['tmp_name']); if ((isset($this->request->data['ShadowAttribute']['value']['error']) && $this->request->data['ShadowAttribute']['value']['error'] == 0) || - (!empty( $this->request->data['ShadowAttribute']['value']['tmp_name']) && $this->request->data['ShadowAttribute']['value']['tmp_name'] != 'none') + (!empty( $this->request->data['ShadowAttribute']['value']['tmp_name']) && $this->request->data['ShadowAttribute']['value']['tmp_name'] != 'none') ) { if (!is_uploaded_file($tmpfile->path)) throw new InternalErrorException('PHP says file was not uploaded. Are you attacking me?'); @@ -476,90 +489,71 @@ class ShadowAttributesController extends AppController { $this->Session->setFlash(__('There was a problem to upload the file.', true), 'default', array(), 'error'); $this->redirect(array('controller' => 'events', 'action' => 'view', $this->request->data['ShadowAttribute']['event_id'])); } - $temp = $this->_getEventData($this->request->data['ShadowAttribute']['event_id']); + + $fails = array(); + $completeFail = false; + + $filename = basename($this->request->data['ShadowAttribute']['value']['name']); + $tmpfile = new File($this->request->data['ShadowAttribute']['value']['tmp_name']); + $hashes = array('md5' => 'malware-sample', 'sha1' => 'filename|sha1', 'sha256' => 'filename|sha256'); $event_uuid = $temp['uuid']; $event_org = $temp['orgc']; - // save the file-info in the database - $this->ShadowAttribute->create(); if ($this->request->data['ShadowAttribute']['malware']) { - $this->request->data['ShadowAttribute']['type'] = "malware-sample"; - // Validate filename - if (!preg_match('@^[\w-,\s]+\.[A-Za-z0-9_]{2,4}$@', $filename)) throw new Exception ('Filename not allowed'); - $this->request->data['ShadowAttribute']['value'] = $filename . '|' . $tmpfile->md5(); // TODO gives problems with bigger files - $this->request->data['ShadowAttribute']['to_ids'] = 1; // LATER let user choose to send this to IDS - } else { - $this->request->data['ShadowAttribute']['type'] = "attachment"; - // Validate filename - if (!preg_match('@^[\w-,\s]+\.[A-Za-z0-9_]{2,4}$@', $filename)) throw new Exception ('Filename not allowed'); - $this->request->data['ShadowAttribute']['value'] = $filename; - $this->request->data['ShadowAttribute']['to_ids'] = 0; - } - $this->request->data['ShadowAttribute']['uuid'] = $this->{$Model->alias}->generateUuid(); - $this->request->data['ShadowAttribute']['batch_import'] = 0; - $this->request->data['ShadowAttribute']['email'] = $this->Auth->user('email'); - $this->request->data['ShadowAttribute']['org'] = $this->Auth->user('org'); - $this->request->data['ShadowAttribute']['event_uuid'] = $event_uuid; - $this->request->data['ShadowAttribute']['event_org'] = $event_org; - $this->ShadowAttribute->save($this->request->data); - - // no errors in file upload, entry already in db, now move the file where needed and zip it if required. - // no sanitization is required on the filename, path or type as we save - // create directory structure - if (PHP_OS == 'WINNT') { - $rootDir = APP . "files" . DS . $this->request->data['ShadowAttribute']['event_id'] . DS . "shadow"; - } else { - $rootDir = APP . DS . "files" . DS . $this->request->data['ShadowAttribute']['event_id'] . DS . "shadow"; - } - $dir = new Folder($rootDir, true); - // move the file to the correct location - $destpath = $rootDir . DS . $this->ShadowAttribute->id; // id of the new ShadowAttribute in the database - $file = new File ($destpath); - $zipfile = new File ($destpath . '.zip'); - $fileInZip = new File($rootDir . DS . $filename); // FIXME do sanitization of the filename - - if ($file->exists() || $zipfile->exists() || $fileInZip->exists()) { - // this should never happen as the ShadowAttribute id should be unique - $this->Session->setFlash(__('Attachment with this name already exist in this event.', true), 'default', array(), 'error'); - // remove the entry from the database - $this->ShadowAttribute->delete(); - $this->redirect(array('controller' => 'events', 'action' => 'view', $this->request->data['ShadowAttribute']['event_id'])); - } - if (!move_uploaded_file($tmpfile->path, $file->path)) { - $this->Session->setFlash(__('Problem with uploading attachment. Cannot move it to its final location.', true), 'default', array(), 'error'); - // remove the entry from the database - $this->ShadowAttribute->delete(); - $this->redirect(array('controller' => 'events', 'action' => 'view', $this->request->data['ShadowAttribute']['event_id'])); - } - - // zip and password protect the malware files - if ($this->request->data['ShadowAttribute']['malware']) { - // TODO check if CakePHP has no easy/safe wrapper to execute commands - $execRetval = ''; - $execOutput = array(); - rename($file->path, $fileInZip->path); // TODO check if no workaround exists for the current filtering mechanisms - if (PHP_OS == 'WINNT') { - exec("zip -j -P infected " . $zipfile->path . ' "' . $fileInZip->path . '"', $execOutput, $execRetval); - } else { - exec("zip -j -P infected " . $zipfile->path . ' "' . addslashes($fileInZip->path) . '"', $execOutput, $execRetval); - } - if ($execRetval != 0) { // not EXIT_SUCCESS - $this->Session->setFlash(__('Problem with zipping the attachment. Please report to administrator. ' . $execOutput, true), 'default', array(), 'error'); - // remove the entry from the database - $this->ShadowAttribute->delete(); - $fileInZip->delete(); - $file->delete(); + $result = $this->Event->Attribute->handleMaliciousBase64($this->request->data['ShadowAttribute']['event_id'], $filename, base64_encode($tmpfile->read()), array_keys($hashes)); + if (!$result['success']) { + $this->Session->setFlash(__('There was a problem to upload the file.', true), 'default', array(), 'error'); $this->redirect(array('controller' => 'events', 'action' => 'view', $this->request->data['ShadowAttribute']['event_id'])); - }; - $fileInZip->delete(); // delete the original not-zipped-file - rename($zipfile->path, $file->path); // rename the .zip to .nothing + } + foreach ($hashes as $hash => $typeName) { + if (!$result[$hash]) continue; + $shadowAttribute = array( + 'ShadowAttribute' => array( + 'value' => $filename . '|' . $result[$hash], + 'category' => $this->request->data['ShadowAttribute']['category'], + 'type' => $typeName, + 'event_id' => $this->request->data['ShadowAttribute']['event_id'], + 'to_ids' => 1, + 'email' => $this->Auth->user('email'), + 'org' => $this->Auth->user('org'), + 'event_uuid' => $event_uuid, + 'event_org' => $event_org, + ) + ); + if ($hash == 'md5') $shadowAttribute['ShadowAttribute']['data'] = $result['data']; + $this->ShadowAttribute->create(); + $r = $this->ShadowAttribute->save($shadowAttribute); + if ($r == false) $fails[] = array($typeName); + if (count($fails) == count($hashes)) $completeFail = true; + } + } else { + $shadowAttribute = array( + 'ShadowAttribute' => array( + 'value' => $filename, + 'category' => $this->request->data['ShadowAttribute']['category'], + 'type' => 'attachment', + 'event_id' => $this->request->data['ShadowAttribute']['event_id'], + 'data' => base64_encode($tmpfile->read()), + 'to_ids' => 0, + 'email' => $this->Auth->user('email'), + 'org' => $this->Auth->user('org'), + 'event_uuid' => $event_uuid, + 'event_org' => $event_org, + ) + ); + $this->ShadowAttribute->create(); + $r = $this->ShadowAttribute->save($shadowAttribute); + if ($r == false) { + $fails[] = array('attachment'); + $completeFail = true; + } + } + if (!$completeFail) { + if (!$this->__sendProposalAlertEmail($eventId)) $emailResult = " but sending out the alert e-mails has failed for at least one recipient."; + if (empty($fails)) $this->Session->setFlash(__('The attachment has been uploaded')); + else $this->Session->setFlash(__('The attachment has been uploaded, but some of the proposals could not be created. The failed proposals are: ' . implode(', ', $fails))); + } else { + $this->Session->setFlash(__('The attachment could not be saved, please contact your administrator.')); } - - // everything is done, now redirect to event view - - $emailResult = ""; - if (!$this->__sendProposalAlertEmail($eventId)) $emailResult = " but sending out the alert e-mails has failed for at least one recipient."; - - $this->Session->setFlash(__('The attachment has been uploaded' . $emailResult)); $this->redirect(array('controller' => 'events', 'action' => 'view', $this->request->data['ShadowAttribute']['event_id'])); } else { diff --git a/app/Lib/Tools/XMLConverterTool.php b/app/Lib/Tools/XMLConverterTool.php index 13389fd6a..bdc9c9db6 100644 --- a/app/Lib/Tools/XMLConverterTool.php +++ b/app/Lib/Tools/XMLConverterTool.php @@ -31,10 +31,13 @@ class XMLConverterTool { if (isset($event['RelatedEvent'])) $event['Event']['RelatedEvent'] = $event['RelatedEvent']; $event['Event']['info'] = preg_replace ('/[^\x{0009}\x{000a}\x{000d}\x{0020}-\x{D7FF}\x{E000}-\x{FFFD}]+/u', ' ', $event['Event']['info']); $event['Event']['info'] = str_replace($toEscape, $escapeWith, $event['Event']['info']); - if (isset($event['RelatedAttribute'])) $event['Event']['RelatedAttribute'] = $event['RelatedAttribute']; + if (isset($event['RelatedAttribute'])) { + $event['Event']['RelatedAttribute'] = $event['RelatedAttribute']; + unset($event['RelatedAttribute']); + } else $event['Event']['RelatedAttribute'] = array(); - foreach ($event['Event']['RelatedAttribute'] as &$attribute_w_relation) { - foreach ($attribute_w_relation as $relation) { + foreach ($event['Event']['RelatedAttribute'] as &$attribute_w_relation) { + foreach ($attribute_w_relation as &$relation) { $relation['info'] = preg_replace ('/[^\x{0009}\x{000a}\x{000d}\x{0020}-\x{D7FF}\x{E000}-\x{FFFD}]+/u', ' ', $relation['info']); $relation['info'] = str_replace($toEscape, $escapeWith, $relation['info']); } diff --git a/app/Model/Attribute.php b/app/Model/Attribute.php index a30636c71..ce567bfde 100755 --- a/app/Model/Attribute.php +++ b/app/Model/Attribute.php @@ -1634,9 +1634,13 @@ class Attribute extends AppModel { // The zip archive is then passed back as a base64 encoded string along with the md5 hash and a flag whether the transaction was successful // The archive is password protected using the "infected" password // The contents of the archive will be the actual sample, named and the original filename in a text file named .filename.txt - public function handleMaliciousBase64($event_id, $original_filename, $base64, $hash_types) { + public function handleMaliciousBase64($event_id, $original_filename, $base64, $hash_types, $proposal = false) { if (!is_numeric($event_id)) throw new Exception('Something went wrong. Received a non numeric event ID while trying to create a zip archive of an uploaded malware sample.'); - $dir = new Folder(APP . "files" . DS . $event_id, true); + if ($proposal) { + $dir = new Folder(APP . "files" . DS . $event_id . DS . 'shadow', true); + } else { + $dir = new Folder(APP . "files" . DS . $event_id, true); + } $tmpFile = new File($dir->path . DS . $this->generateRandomFileName(), true, 0600); $tmpFile->write(base64_decode($base64)); $hashes = array(); diff --git a/app/Model/ShadowAttribute.php b/app/Model/ShadowAttribute.php index ca6a2169b..d5212ddbb 100644 --- a/app/Model/ShadowAttribute.php +++ b/app/Model/ShadowAttribute.php @@ -321,7 +321,6 @@ class ShadowAttribute extends AppModel { } public function afterSave($created, $options = array()) { - $result = true; // if the 'data' field is set on the $this->data then save the data to the correct file if (isset($this->data['ShadowAttribute']['type']) && $this->typeIsAttachment($this->data['ShadowAttribute']['type']) && !empty($this->data['ShadowAttribute']['data'])) { @@ -439,7 +438,7 @@ class ShadowAttribute extends AppModel { } public function saveBase64EncodedAttachment($attribute) { - $rootDir = APP . DS . "files" . DS . 'shadow' . DS . $attribute['event_id']; + $rootDir = APP . DS . "files" . DS . $attribute['event_id'] . DS . 'shadow'; $dir = new Folder($rootDir, true); // create directory structure $destpath = $rootDir . DS . $attribute['id']; $file = new File ($destpath, true); // create the file diff --git a/app/Model/User.php b/app/Model/User.php index 899a9e0a7..341c0cbd4 100755 --- a/app/Model/User.php +++ b/app/Model/User.php @@ -137,8 +137,9 @@ class User extends AppModel { ), ), 'gpgkey' => array( - 'valueNotEmpty' => array( - 'rule' => array('valueNotEmpty'), + 'validateGpgKey' => array( + 'rule' => array('validateGpgkey'), + 'message' => 'GPG key not valid, please enter a valid key.', ), ), 'nids_sid' => array( diff --git a/app/View/Elements/eventattribute.ctp b/app/View/Elements/eventattribute.ctp index 950104d4d..6b3cb8b95 100644 --- a/app/View/Elements/eventattribute.ctp +++ b/app/View/Elements/eventattribute.ctp @@ -174,7 +174,7 @@ endif; $sigDisplay = $object['value']; if ('attachment' == $object['type'] || 'malware-sample' == $object['type'] ) { - $t = ($object['type'] == 0 ? 'attributes' : 'shadow_attributes'); + $t = ($object['objectType'] == 0 ? 'attributes' : 'shadow_attributes'); $filenameHash = explode('|', nl2br(h($object['value']))); if (strrpos($filenameHash[0], '\\')) { $filepath = substr($filenameHash[0], 0, strrpos($filenameHash[0], '\\')); diff --git a/app/View/ShadowAttributes/add_attachment.ctp b/app/View/ShadowAttributes/add_attachment.ctp index e824f677d..4e6ebe71c 100644 --- a/app/View/ShadowAttributes/add_attachment.ctp +++ b/app/View/ShadowAttributes/add_attachment.ctp @@ -96,6 +96,22 @@ $('#ShadowAttributeTypeDiv').hide(); $('#ShadowAttributeCategoryDiv').hide(); $('#ShadowAttributeType').prop('disabled', true); +function malwareCheckboxSetter(id) { + // do checkbox un/ticked when the document is changed + if (formZipTypeValues[value] == "true") { + document.getElementById("AttributeMalware").setAttribute("checked", "checked"); + if (formAttTypeValues[value] == "false") document.getElementById("AttributeMalware").setAttribute("disabled", "disabled"); + else document.getElementById("AttributeMalware").removeAttribute("disabled"); + } else { + document.getElementById("AttributeMalware").removeAttribute("checked"); + if (formAttTypeValues[value] == "true") document.getElementById("AttributeMalware").setAttribute("disabled", "disabled"); + else document.getElementById("AttributeMalware").removeAttribute("disabled"); + } +} +$(function(){ + // do checkbox un/ticked when the document is ready + malwareCheckboxSetter("#ShadowAttributeCategory"); + } Js->writeBuffer(); // Write cached scripts