Download all samples for an event ID via the API

- as explained on the automation page
- also, better error handling

- all API calls that fail during authentication will now return a JSON/XML error message instead of redirecting to the login page
pull/542/merge
Iglocska 2015-08-07 16:10:40 +02:00
parent 00b7fb215b
commit 94398b8192
4 changed files with 89 additions and 49 deletions

View File

@ -1 +1 @@
{"major":2, "minor":3, "hotfix":105}
{"major":2, "minor":3, "hotfix":106}

View File

@ -2072,7 +2072,7 @@ class AttributesController extends AppController {
}
// download a sample by passing along an md5
public function downloadSample($hash=false, $allSamples = false) {
public function downloadSample($hash=false, $allSamples=false, $eventID=false) {
if (!$this->userRole['perm_auth']) throw new MethodNotAllowedException('This functionality requires API key access.');
//if (!$this->request->is('post')) throw new MethodNotAllowedException('Please POST the samples as described on the automation page.');
$isJson = false;
@ -2086,50 +2086,68 @@ class AttributesController extends AppController {
throw new BadRequestException('This action is for the API only. Please refer to the automation page for information on how to use it.');
}
if (!$hash && isset($data['request']['hash'])) $hash = $data['request']['hash'];
if (!$allSamples && isset($data['request']['hash'])) $allSamples = $data['request']['allSamples'];
if (!$allSamples && isset($data['request']['allSamples'])) $allSamples = $data['request']['allSamples'];
if (!$eventID && isset($data['request']['eventID'])) $eventID = $data['request']['eventID'];
if (!$eventID && !$hash) throw new MethodNotAllowedException('No hash or event ID received. You need to set at least one of the two.');
if (!$hash) $allSamples = true;
$validTypes = $this->Attribute->resolveHashType($hash);
$simpleFalse = array('hash', 'allSamples', 'eventID');
foreach ($simpleFalse as $sF) {
if (${$sF} === 'null' || ${$sF} == '0' || ${$sF} === false || strtolower(${$sF}) === 'false') ${$sF} = false;
}
// valid combinations of settings are:
// hash
// eventID + all samples
// hash + eventID
// hash + eventID + all samples
if ($hash) $validTypes = $this->Attribute->resolveHashType($hash);
$types = array();
if ($allSamples) {
if (empty($validTypes)) {
$error = 'Invalid hash format (valid options are ' . implode(', ', array_keys($this->Attribute->hashTypes)) . ')';
}
else {
foreach ($validTypes as $t) {
if ($t == 'md5') $types = array_merge($types, array('malware-sample', 'filename|md5', 'md5'));
else $types = array_merge($types, array('filename|' . $t, $t));
if ($hash && $allSamples) {
if ($hash) {
debug($hash);
if (empty($validTypes)) {
$error = 'Invalid hash format (valid options are ' . implode(', ', array_keys($this->Attribute->hashTypes)) . ')';
}
else {
foreach ($validTypes as $t) {
if ($t == 'md5') $types = array_merge($types, array('malware-sample', 'filename|md5', 'md5'));
else $types = array_merge($types, array('filename|' . $t, $t));
}
}
if (empty($error)) {
$event_ids = $this->Attribute->find('list', array(
'recursive' => -1,
'contain' => array('Event'),
'fields' => array('Event.id'),
'conditions' => array(
'OR' => array(
'AND' => array(
'LOWER(Attribute.value1) LIKE' => strtolower($hash),
'Attribute.value2' => '',
),
'LOWER(Attribute.value2) LIKE' => strtolower($hash)
)
),
));
$searchConditions = array(
'AND' => array('Event.id' => array_values($event_ids))
);
if (empty($event_ids)) $error = 'No hits with the given parameters.';
}
} else {
if (!in_array('md5', $validTypes)) $error = 'Only MD5 hashes can be used to fetch malware samples at this point in time.';
if (empty($error)) {
$types = array('malware-sample', 'filename|md5');
$searchConditions = array('AND' => array('LOWER(Attribute.value2) LIKE' => strtolower($hash)));
}
}
if (empty($error)) {
$event_ids = $this->Attribute->find('list', array(
'recursive' => -1,
'contain' => array('Event'),
'fields' => array('Event.id'),
'conditions' => array(
'OR' => array(
'AND' => array(
'LOWER(Attribute.value1) LIKE' => strtolower($hash),
'Attribute.value2' => '',
),
'LOWER(Attribute.value2) LIKE' => strtolower($hash)
)
),
));
$searchConditions = array(
'Event.id' => array_values($event_ids)
);
if (empty($event_ids)) $error = 'No hits on the passed hash.';
}
} else {
if (!in_array('md5', $validTypes)) $error = 'Only MD5 hashes can be used to fetch malware samples at this point in time.';
if (empty($error)) {
$types = array('malware-sample', 'filename|md5');
$searchConditions = array(
'LOWER(Attribute.value2) LIKE' => strtolower($hash)
);
}
}
if (!empty($eventID)) $searchConditions['AND'][] = array('Event.id' => $eventID);
if (empty($error)) {
$distributionConditions = array();
if (!$this->_isSiteAdmin()) {
@ -2155,7 +2173,7 @@ class AttributesController extends AppController {
$distributionConditions,
array('Attribute.type' => 'malware-sample')
))));
if (empty($attributes)) $error = 'No hits on the passed hash.';
if (empty($attributes)) $error = 'No hits with the given parameters.';
$results = array();
foreach ($attributes as $attribute) {
@ -2175,15 +2193,15 @@ class AttributesController extends AppController {
}
}
if ($error) {
$this->set('error', $error);
$this->set('_serialize', array('error'));
$this->set('message', $error);
$this->set('_serialize', array('message'));
} else {
$this->set('result', $results);
$this->set('_serialize', array('result'));
}
} else {
$this->set('error', $error);
$this->set('_serialize', array('error'));
$this->set('message', $error);
$this->set('_serialize', array('message'));
}
}

View File

@ -3328,6 +3328,8 @@ class EventsController extends AppController {
}
if (!isset($data['to_ids']) || !in_array($data['to_ids'], array('0', '1', 0, 1))) $data['to_ids'] = 1;
$successCount = 0;
$errors = array();
foreach ($data['files'] as $file) {
$temp = $this->Event->Attribute->handleMaliciousBase64($data['event_id'], $file['filename'], $file['data'], array_keys($hashes));
if ($temp['success']) {
@ -3357,10 +3359,28 @@ class EventsController extends AppController {
'title' => 'Error: Failed to create attribute using the upload sample functionality',
'change' => 'There was an issue creating an attribute (' . $typeName . ': ' . $file['filename'] . '|' . $file[$hash] . '). ' . 'The validation errors were: ' . json_encode($this->Event->Attribute->validationErrors),
));
}
if ($typeName == 'malware-sample') $errors[] = array('filename' => $file['filename'], 'hash' => $file[$hash], 'error' => $this->Event->Attribute->validationErrors);
} else if ($typeName == 'malware-sample') $successCount++;
}
} else {
$errors[] = array('filename' => $file['filename'], 'hash' => $file['hash'], 'error' => 'Failed to encrypt and compress the file.');
}
}
if (!empty($errors)) {
$this->set('errors', $errors);
if ($successCount > 0) {
$this->set('name', 'Partial success');
$this->set('message', 'Successfuly saved ' . $successCount . ' sample(s), but some samples could not be saved.');
} else {
$this->set('name', 'Failed');
$this->set('message', 'Failed to save any of the supplied samples.');
}
$this->set('_serialize', array('name', 'message', 'errors'));
} else {
$this->set('name', 'Success');
$this->set('message', 'Success, saved all attributes.');
$this->set('_serialize', array('name', 'message'));
}
$this->view($data['event_id']);
$this->render('view');
}

View File

@ -314,22 +314,24 @@ For example, to get all IDS signature attributes of type md5 and sha256, but not
<h3>Download malware sample by hash</h3>
<p>You can also download samples by knowing its MD5 hash. Simply pass the hash along as a JSON/XML object or in the URL (with the URL having overruling the passed objects) to receive a JSON/XML object back with the zipped sample base64 encoded along with some contextual information.</p>
<p>You can also use this API to get all samples from events that contain the passed hash. For this functionality, just pass the "allSamples" flag along. Note that if you are getting all samples from matching events, you can use all supported hash types (<?php echo h(implode(', ', $hashTypes)); ?>) for the lookup.</p>
<p>You can also get all the samples from an event with a given event ID, by passing along the eventID parameter. Make sure that either an event ID or a hash is passed along, otherwise an error message will be returned. Also, if no hash is set, the allSamples flag will get set automatically.</p>
<pre>
<?php
echo Configure::read('MISP.baseurl').'/attributes/downloadSample/[hash]/[allSamples]';
echo Configure::read('MISP.baseurl').'/attributes/downloadSample/[hash]/[allSamples]/[eventID]';
?>
</pre>
<p>POST message payload (XML):</p>
<p><code>
<?php echo h("<request><hash>7c12772809c1c0c3deda6103b10fdfa0</hash><allSamples>1</allSamples></request>"); ?>
<?php echo h("<request><hash>7c12772809c1c0c3deda6103b10fdfa0</hash><allSamples>1</allSamples><eventID>13</eventID</request>"); ?>
</code></p>
<p>POST message payload (json):</p>
<p><code>
{"request": {"hash": "7c12772809c1c0c3deda6103b10fdfa0", "allSamples": 1}}
{"request": {"hash": "7c12772809c1c0c3deda6103b10fdfa0", "allSamples": 1, "eventID": 13}}
</code></p>
<p>A quick description of all the parameters in the passed object:</p>
<b>hash</b>: A hash in MD5 format. If allSamples is set, this can be any one of the following: <?php echo h(implode(', ', $hashTypes)); ?><br />
<b>allSamples</b>: If set, it will return all samples from events that have a match for the hash provided above.<br />
<b>eventID</b>: If set, it will only fetch data from the given event ID.<br />
<h3>Upload malware samples using the "Upload Sample" API</h3>
<pre>
<?php