2014-06-12 13:59:54 +02:00
|
|
|
<?php
|
|
|
|
|
|
|
|
class ComplexTypeTool {
|
2016-06-04 01:08:16 +02:00
|
|
|
|
2016-04-20 10:04:36 +02:00
|
|
|
private $__refangRegexTable = array(
|
|
|
|
'/^hxxp/i' => 'http',
|
|
|
|
'/\[\.\]/' => '.',
|
|
|
|
'/\[dot\]/' => '.',
|
|
|
|
'/\\\./' => '.',
|
|
|
|
'/\.+/' => '.'
|
|
|
|
);
|
2016-06-04 01:08:16 +02:00
|
|
|
|
2016-09-27 15:53:43 +02:00
|
|
|
public function checkComplexRouter($input, $type, $IANATLDentries) {
|
2014-06-19 08:44:35 +02:00
|
|
|
switch ($type) {
|
|
|
|
case 'File':
|
|
|
|
return $this->checkComplexFile($input);
|
|
|
|
break;
|
|
|
|
case 'CnC':
|
|
|
|
return $this->checkComplexCnC($input);
|
|
|
|
break;
|
2014-07-10 17:02:19 +02:00
|
|
|
case 'FreeText':
|
2016-09-27 15:53:43 +02:00
|
|
|
return $this->checkFreeText($input, $IANATLDentries);
|
2014-07-10 17:02:19 +02:00
|
|
|
break;
|
2014-06-19 08:44:35 +02:00
|
|
|
default:
|
|
|
|
return false;
|
2016-06-04 01:10:45 +02:00
|
|
|
}
|
2014-06-19 08:44:35 +02:00
|
|
|
}
|
2016-06-04 01:08:16 +02:00
|
|
|
|
2016-06-04 01:10:45 +02:00
|
|
|
// checks if the passed input matches a valid file description attribute's pattern (filename, md5, sha1, sha256, filename|md5, filename|sha1, filename|sha256)
|
2014-06-12 13:59:54 +02:00
|
|
|
public function checkComplexFile($input) {
|
2014-07-08 11:31:23 +02:00
|
|
|
$original = $input;
|
2014-06-12 13:59:54 +02:00
|
|
|
$type = '';
|
|
|
|
$composite = false;
|
|
|
|
if (strpos($input, '|')) {
|
|
|
|
$composite = true;
|
|
|
|
$result = explode('|', $input);
|
2016-06-06 16:42:16 +02:00
|
|
|
if (count($result) != 2 || !preg_match("#^.+#", $result[0])) {
|
|
|
|
$type = 'other';
|
|
|
|
} else {
|
|
|
|
$type = 'filename|';
|
|
|
|
}
|
2014-06-12 13:59:54 +02:00
|
|
|
$input = $result[1];
|
|
|
|
}
|
|
|
|
if (strlen($input) == 32 && preg_match("#[0-9a-f]{32}$#", $input)) $type .= 'md5';
|
|
|
|
if (strlen($input) == 40 && preg_match("#[0-9a-f]{40}$#", $input)) $type .= 'sha1';
|
|
|
|
if (strlen($input) == 64 && preg_match("#[0-9a-f]{64}$#", $input)) $type .= 'sha256';
|
|
|
|
if ($type == '' && !$composite && preg_match("#^.+#", $input)) $type = 'filename';
|
|
|
|
if ($type == '') $type = 'other';
|
2014-07-08 11:31:23 +02:00
|
|
|
return array('type' => $type, 'value' => $original);
|
2014-06-12 13:59:54 +02:00
|
|
|
}
|
2016-06-04 01:08:16 +02:00
|
|
|
|
2014-06-12 13:59:54 +02:00
|
|
|
public function checkComplexCnC($input) {
|
|
|
|
$toReturn = array();
|
|
|
|
// check if it's an IP address
|
2014-06-19 08:44:35 +02:00
|
|
|
if (filter_var($input, FILTER_VALIDATE_IP)) return array('type' => 'ip-dst', 'value' => $input);
|
2014-06-12 13:59:54 +02:00
|
|
|
if (preg_match("#^[A-Z0-9.-]+\.[A-Z]{2,4}$#i", $input)) {
|
|
|
|
$result = explode('.', $input);
|
|
|
|
if (count($result) > 2) {
|
2014-06-19 08:44:35 +02:00
|
|
|
$toReturn['multi'][] = array('type' => 'hostname', 'value' => $input);
|
2016-06-04 01:54:19 +02:00
|
|
|
$pos = strpos($input, '.');
|
|
|
|
$toReturn['multi'][] = array('type' => 'domain', 'value' => substr($input, (1 + $pos)));
|
|
|
|
return $toReturn;
|
2014-06-12 13:59:54 +02:00
|
|
|
}
|
2014-06-19 08:44:35 +02:00
|
|
|
return array('type' => 'domain', 'value' => $input);
|
2014-06-12 13:59:54 +02:00
|
|
|
}
|
2016-06-04 01:08:16 +02:00
|
|
|
|
2014-06-19 08:44:35 +02:00
|
|
|
if (!preg_match("#\n#", $input)) return array('type' => 'url', 'value' => $input);
|
|
|
|
return array('type' => 'other', 'value' => $input);
|
2014-06-12 13:59:54 +02:00
|
|
|
}
|
2016-06-04 01:08:16 +02:00
|
|
|
|
2016-09-04 23:31:24 +02:00
|
|
|
private function __returnOddElements($array) {
|
|
|
|
foreach ($array as $k => $v) if ($k % 2 != 1) unset($array[$k]);
|
2016-03-29 20:05:50 +02:00
|
|
|
return array_values($array);
|
|
|
|
}
|
2016-06-04 01:08:16 +02:00
|
|
|
|
2016-09-27 15:53:43 +02:00
|
|
|
public function checkFreeText($input, $IANATLDentries) {
|
2015-08-07 12:04:44 +02:00
|
|
|
$iocArray = preg_split("/\r\n|\n|\r|\s|\s+|,|;/", $input);
|
2016-03-29 20:05:50 +02:00
|
|
|
$quotedText = explode('"', $input);
|
2016-09-04 23:31:24 +02:00
|
|
|
foreach ($quotedText as $k => $temp) {
|
2016-08-08 17:32:01 +02:00
|
|
|
$temp = trim($temp);
|
|
|
|
if (empty($temp)) {
|
|
|
|
unset($quotedText[$k]);
|
2016-09-04 23:31:24 +02:00
|
|
|
} else {
|
|
|
|
$quotedText[$k] = $temp;
|
2016-08-08 17:32:01 +02:00
|
|
|
}
|
|
|
|
}
|
2016-03-29 20:05:50 +02:00
|
|
|
$iocArray = array_merge($iocArray, $this->__returnOddElements($quotedText));
|
2014-07-10 17:02:19 +02:00
|
|
|
$resultArray = array();
|
2016-03-29 20:05:50 +02:00
|
|
|
if (!empty($iocArray)) {
|
|
|
|
foreach ($iocArray as $ioc) {
|
|
|
|
$ioc = trim($ioc);
|
2016-08-08 17:32:01 +02:00
|
|
|
$ioc = trim($ioc, '"');
|
2016-03-29 20:05:50 +02:00
|
|
|
$ioc = trim($ioc, ',');
|
|
|
|
$ioc = preg_replace('/\p{C}+/u', '', $ioc);
|
|
|
|
if (empty($ioc)) continue;
|
2016-09-27 15:53:43 +02:00
|
|
|
$typeArray = $this->__resolveType($ioc, $IANATLDentries);
|
2016-03-29 20:05:50 +02:00
|
|
|
if ($typeArray === false) continue;
|
|
|
|
$temp = $typeArray;
|
|
|
|
if (!isset($temp['value'])) $temp['value'] = $ioc;
|
|
|
|
$resultArray[] = $temp;
|
|
|
|
}
|
2014-07-10 17:02:19 +02:00
|
|
|
}
|
|
|
|
return $resultArray;
|
|
|
|
}
|
2016-03-29 23:03:01 +02:00
|
|
|
|
|
|
|
private $__hexHashTypes = array(
|
|
|
|
32 => array('single' => array('md5', 'imphash'), 'composite' => array('filename|md5', 'filename|imphash')),
|
|
|
|
40 => array('single' => array('sha1', 'pehash', 'x509-fingerprint-sha1'), 'composite' => array('filename|sha1', 'filename|pehash')),
|
2016-04-20 10:55:56 +02:00
|
|
|
56 => array('single' => array('sha224', 'sha512/224'), 'composite' => array('filename|sha224', 'filename|sha512/224')),
|
|
|
|
64 => array('single' => array('sha256', 'authentihash', 'sha512/256'), 'composite' => array('filename|sha256', 'filename|authentihash', 'filename|sha512/256')),
|
|
|
|
96 => array('single' => array('sha384'), 'composite' => array('filename|sha384')),
|
|
|
|
128 => array('single' => array('sha512'), 'composite' => array('filename|sha512'))
|
2016-03-29 23:03:01 +02:00
|
|
|
);
|
2016-06-04 01:08:16 +02:00
|
|
|
|
2016-09-27 15:53:43 +02:00
|
|
|
private function __resolveType($input, $IANATLDentries) {
|
2014-10-13 17:43:31 +02:00
|
|
|
$input = trim($input);
|
2016-09-27 15:53:43 +02:00
|
|
|
// check for composite (|) attributes
|
2015-06-22 14:11:43 +02:00
|
|
|
if (strpos($input, '|')) {
|
|
|
|
$compositeParts = explode('|', $input);
|
|
|
|
if (count($compositeParts) == 2) {
|
|
|
|
if ($this->__resolveFilename($compositeParts[0])) {
|
2016-09-04 23:31:24 +02:00
|
|
|
foreach ($this->__hexHashTypes as $k => $v) {
|
2016-03-29 23:03:01 +02:00
|
|
|
if (strlen($compositeParts[1]) == $k && preg_match("#[0-9a-f]{" . $k . "}$#i", $compositeParts[1])) return array('types' => $v['composite'], 'to_ids' => true, 'default_type' => $v['composite'][0]);
|
|
|
|
}
|
2015-07-16 09:18:21 +02:00
|
|
|
if (preg_match('#^[0-9]+:.+:.+$#', $compositeParts[1])) return array('types' => array('ssdeep'), 'to_ids' => true, 'default_type' => 'filename|ssdeep');
|
2015-06-22 14:11:43 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-06-04 01:08:16 +02:00
|
|
|
|
2014-07-10 17:02:19 +02:00
|
|
|
// check for hashes
|
2016-09-04 23:31:24 +02:00
|
|
|
foreach ($this->__hexHashTypes as $k => $v) {
|
2016-03-29 23:03:01 +02:00
|
|
|
if (strlen($input) == $k && preg_match("#[0-9a-f]{" . $k . "}$#i", $input)) return array('types' => $v['single'], 'to_ids' => true, 'default_type' => $v['single'][0]);
|
|
|
|
}
|
2015-07-16 09:18:21 +02:00
|
|
|
if (preg_match('#^[0-9]+:.+:.+$#', $input)) return array('types' => array('ssdeep'), 'to_ids' => true, 'default_type' => 'ssdeep');
|
2016-04-20 10:04:36 +02:00
|
|
|
$inputRefanged = $input;
|
|
|
|
foreach ($this->__refangRegexTable as $regex => $replacement) $inputRefanged = preg_replace($regex, $replacement , $inputRefanged);
|
2016-01-23 20:19:44 +01:00
|
|
|
$inputRefanged = rtrim($inputRefanged, ".");
|
2016-03-11 16:02:38 +01:00
|
|
|
if (strpos($input, '@') !== false) {
|
|
|
|
if (filter_var($input, FILTER_VALIDATE_EMAIL)) return array('types' => array('email-src', 'email-dst'), 'to_ids' => true, 'default_type' => 'email-src');
|
|
|
|
}
|
2015-06-02 12:02:07 +02:00
|
|
|
// note down and remove the port if it's a url / domain name / hostname / ip
|
|
|
|
// input2 from here on is the variable containing the original input with the port removed. It is only used by url / domain name / hostname / ip
|
|
|
|
$comment = false;
|
2015-12-04 11:33:14 +01:00
|
|
|
if (preg_match('/(:[0-9]{2,5})$/', $inputRefanged, $port)) {
|
2015-06-02 12:02:07 +02:00
|
|
|
$comment = 'On port ' . substr($port[0], 1);
|
2015-12-04 11:33:14 +01:00
|
|
|
$inputRefangedNoPort = str_replace($port[0], '', $inputRefanged);
|
2016-06-04 01:10:45 +02:00
|
|
|
} else $inputRefangedNoPort = $inputRefanged;
|
2014-07-10 17:02:19 +02:00
|
|
|
// check for IP
|
2015-12-04 11:33:14 +01:00
|
|
|
if (filter_var($inputRefangedNoPort, FILTER_VALIDATE_IP)) return array('types' => array('ip-dst', 'ip-src', 'ip-src/ip-dst'), 'to_ids' => true, 'default_type' => 'ip-dst', 'comment' => $comment, 'value' => $inputRefangedNoPort);
|
|
|
|
if (strpos($inputRefangedNoPort, '/')) {
|
|
|
|
$temp = explode('/', $inputRefangedNoPort);
|
2016-06-06 16:43:25 +02:00
|
|
|
if (count($temp) == 2) {
|
2015-12-08 15:12:13 +01:00
|
|
|
if (filter_var($temp[0], FILTER_VALIDATE_IP) && is_numeric($temp[1])) return array('types' => array('ip-dst', 'ip-src', 'ip-src/ip-dst'), 'to_ids' => true, 'default_type' => 'ip-dst', 'comment' => $comment, 'value' => $inputRefangedNoPort);
|
2014-10-13 09:16:09 +02:00
|
|
|
}
|
|
|
|
}
|
2016-06-04 01:08:16 +02:00
|
|
|
|
2014-07-10 17:02:19 +02:00
|
|
|
// check for domain name, hostname, filename
|
2015-12-04 11:33:14 +01:00
|
|
|
if (strpos($inputRefanged, '.') !== false) {
|
|
|
|
$temp = explode('.', $inputRefanged);
|
2016-09-27 15:53:43 +02:00
|
|
|
// check for the new TLDs as known by IANA (if the Warninglists are not empty)
|
|
|
|
if (!empty($IANATLDentries)) {
|
|
|
|
$stringEnd = $temp[count($temp)-1];
|
|
|
|
if (in_array($stringEnd, $IANATLDentries)) {
|
|
|
|
$types = array('filename', 'domain');
|
|
|
|
if (count($temp) > 2)
|
|
|
|
$types[] = 'url';
|
|
|
|
return array('types' => $types, 'to_ids' => true, 'default_type' => 'filename', 'merge_categories' => true);
|
|
|
|
}
|
|
|
|
}
|
2016-05-20 00:48:54 +02:00
|
|
|
// TODO: use a more flexible matching approach, like the one below (that still doesn't support non-ASCII domains)
|
2014-07-10 17:02:19 +02:00
|
|
|
//if (filter_var($input, FILTER_VALIDATE_URL)) {
|
2016-01-18 15:24:48 +01:00
|
|
|
if (preg_match('/^([-\pL\pN]+\.)+([a-z][a-z]|biz|cat|com|edu|gov|int|mil|net|org|pro|tel|aero|arpa|asia|coop|info|jobs|mobi|name|museum|travel)(:[0-9]{2,5})?$/iu', $inputRefanged)) {
|
2014-07-10 17:02:19 +02:00
|
|
|
if (count($temp) > 2) {
|
2015-12-04 11:33:14 +01:00
|
|
|
return array('types' => array('hostname', 'domain', 'url'), 'to_ids' => true, 'default_type' => 'hostname', 'comment' => $comment, 'value' => $inputRefangedNoPort);
|
2014-07-10 17:02:19 +02:00
|
|
|
} else {
|
2015-12-04 11:33:14 +01:00
|
|
|
return array('types' => array('domain'), 'to_ids' => true, 'default_type' => 'domain', 'comment' => $comment, 'value' => $inputRefangedNoPort);
|
2014-07-10 17:02:19 +02:00
|
|
|
}
|
|
|
|
} else {
|
2015-07-09 10:56:17 +02:00
|
|
|
// check if it is a URL
|
|
|
|
// Adding http:// infront of the input in case it was left off. github.com/MISP/MISP should still be counted as a valid link
|
2015-12-04 11:33:14 +01:00
|
|
|
if (count($temp) > 1 && (filter_var($inputRefangedNoPort, FILTER_VALIDATE_URL) || filter_var('http://' . $inputRefangedNoPort, FILTER_VALIDATE_URL))) {
|
2016-06-04 01:10:45 +02:00
|
|
|
// TODO: add comment explaining why there is a check for a specific domain
|
2016-07-19 11:29:23 +02:00
|
|
|
if (preg_match('/^https:\/\/(www.)?virustotal.com\//i', $inputRefangedNoPort)) return array('types' => array('link'), 'to_ids' => false, 'default_type' => 'link', 'comment' => $comment, 'value' => $inputRefangedNoPort);
|
2016-04-22 21:50:05 +02:00
|
|
|
if (strpos($inputRefangedNoPort, '/')) return array('types' => array('url'), 'to_ids' => true, 'default_type' => 'url', 'comment' => $comment, 'value' => $inputRefangedNoPort);
|
2015-07-09 10:56:17 +02:00
|
|
|
}
|
2015-06-18 14:49:25 +02:00
|
|
|
if ($this->__resolveFilename($input)) return array('types' => array('filename'), 'to_ids' => true, 'default_type' => 'filename');
|
2015-07-09 10:56:17 +02:00
|
|
|
}
|
2014-07-10 17:02:19 +02:00
|
|
|
}
|
2016-06-04 01:08:16 +02:00
|
|
|
|
2014-07-10 17:02:19 +02:00
|
|
|
if (strpos($input, '\\') !== false) {
|
|
|
|
$temp = explode('\\', $input);
|
2016-08-08 17:32:01 +02:00
|
|
|
if (strpos($temp[count($temp)-1], '.') || preg_match('/^.:/i', $temp[0])) {
|
|
|
|
if ($this->__resolveFilename($temp[count($temp)-1])) return array('types' => array('filename'), 'categories' => array('Payload installation'), 'to_ids' => true, 'default_type' => 'filename');
|
2014-07-10 17:02:19 +02:00
|
|
|
} else {
|
|
|
|
return array('types' => array('regkey'), 'to_ids' => false, 'default_type' => 'regkey');
|
|
|
|
}
|
|
|
|
}
|
2016-06-04 01:08:16 +02:00
|
|
|
|
2014-08-20 15:34:42 +02:00
|
|
|
// check for CVE
|
2016-07-26 11:42:38 +02:00
|
|
|
if (preg_match("#^cve-[0-9]{4}-[0-9]{4,9}$#i", $input)) return array('types' => array('vulnerability'), 'categories' => array('External analysis'), 'to_ids' => false, 'default_type' => 'vulnerability');
|
2016-06-04 01:08:16 +02:00
|
|
|
|
2015-05-27 19:10:14 +02:00
|
|
|
return false;
|
2014-07-10 17:02:19 +02:00
|
|
|
}
|
2016-06-04 01:08:16 +02:00
|
|
|
|
2015-06-18 14:49:25 +02:00
|
|
|
private function __resolveFilename($input) {
|
2016-08-08 17:32:01 +02:00
|
|
|
if ((preg_match('/^.:/', $input) || strpos($input, '.') !=0)) return true;
|
2016-08-22 02:54:51 +02:00
|
|
|
return false;
|
2015-06-18 14:49:25 +02:00
|
|
|
}
|
2016-05-20 00:48:54 +02:00
|
|
|
}
|