Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/undo-ransomware/ransomware_detection.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Controller/ScanController.php')
-rw-r--r--lib/Controller/ScanController.php447
1 files changed, 0 insertions, 447 deletions
diff --git a/lib/Controller/ScanController.php b/lib/Controller/ScanController.php
deleted file mode 100644
index a7eca58..0000000
--- a/lib/Controller/ScanController.php
+++ /dev/null
@@ -1,447 +0,0 @@
-<?php
-
-/**
- * @copyright Copyright (c) 2018 Matthias Held <matthias.held@uni-konstanz.de>
- * @author Matthias Held <matthias.held@uni-konstanz.de>
- * @license GNU AGPL version 3 or any later version
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-
-namespace OCA\RansomwareDetection\Controller;
-
-use OCA\RansomwareDetection\Monitor;
-use OCA\RansomwareDetection\Classifier;
-use OCA\RansomwareDetection\Analyzer\SequenceAnalyzer;
-use OCA\RansomwareDetection\Analyzer\EntropyAnalyzer;
-use OCA\RansomwareDetection\Analyzer\FileCorruptionAnalyzer;
-use OCA\RansomwareDetection\Analyzer\FileExtensionAnalyzer;
-use OCA\RansomwareDetection\Analyzer\FileExtensionResult;
-use OCA\RansomwareDetection\AppInfo\Application;
-use OCA\RansomwareDetection\Db\FileOperation;
-use OCA\RansomwareDetection\Exception\NotAFileException;
-use OCA\RansomwareDetection\Service\FileOperationService;
-use OCA\RansomwareDetection\Scanner\StorageStructure;
-use OCP\Files\NotFoundException;
-use OCA\Files_Trashbin\Trashbin;
-use OCA\Files_Trashbin\Helper;
-use OCP\AppFramework\Http;
-use OCP\AppFramework\Http\JSONResponse;
-use OCP\AppFramework\OCSController;
-use OCP\Files\File;
-use OCP\Files\Folder;
-use OCP\IConfig;
-use OCP\IUserSession;
-use OCP\IRequest;
-use OCP\IDBConnection;
-use OCP\ILogger;
-
-class ScanController extends OCSController
-{
- /** @var IConfig */
- protected $config;
-
- /** @var IUserSession */
- protected $userSession;
-
- /** @var Classifier */
- protected $classifier;
-
- /** @var ILogger */
- protected $logger;
-
- /** @var Folder */
- protected $userFolder;
-
- /** @var FileOperationService */
- protected $service;
-
- /** @var SequenceAnalyzer */
- protected $sequenceAnalyzer;
-
- /** @var EntropyAnalyzer */
- protected $entropyAnalyzer;
-
- /** @var FileCorruptionAnalyzer */
- protected $fileCorruptionAnalyzer;
-
- /** @var FileExtensionAnalyzer */
- protected $fileExtensionAnalyzer;
-
- /** @var IDBConnection */
- protected $connection;
-
- /** @var string */
- protected $userId;
-
- /**
- * @param string $appName
- * @param IRequest $request
- * @param IUserSession $userSession
- * @param IConfig $config
- * @param Classifier $classifier
- * @param ILogger $logger
- * @param Folder $userFolder
- * @param FileOperationService $service
- * @param SequenceAnalyzer $sequenceAnalyzer
- * @param EntropyAnalyzer $entropyAnalyzer
- * @param FileCorruptionAnalyzer $fileCorruptionAnalyzer
- * @param FileExtensionAnalyzer $fileExtensionAnalyzer
- * @param IDBConnection $connection
- * @param string $userId
- */
- public function __construct(
- $appName,
- IRequest $request,
- IUserSession $userSession,
- IConfig $config,
- Classifier $classifier,
- ILogger $logger,
- Folder $userFolder,
- FileOperationService $service,
- SequenceAnalyzer $sequenceAnalyzer,
- EntropyAnalyzer $entropyAnalyzer,
- FileCorruptionAnalyzer $fileCorruptionAnalyzer,
- FileExtensionAnalyzer $fileExtensionAnalyzer,
- IDBConnection $connection,
- $userId
- ) {
- parent::__construct($appName, $request);
-
- $this->config = $config;
- $this->userSession = $userSession;
- $this->classifier = $classifier;
- $this->userFolder = $userFolder;
- $this->logger = $logger;
- $this->service = $service;
- $this->sequenceAnalyzer = $sequenceAnalyzer;
- $this->entropyAnalyzer = $entropyAnalyzer;
- $this->fileCorruptionAnalyzer = $fileCorruptionAnalyzer;
- $this->fileExtensionAnalyzer = $fileExtensionAnalyzer;
- $this->connection = $connection;
- $this->userId = $userId;
- }
-
- /**
- * Post scan recovery.
- *
- * @NoAdminRequired
- *
- * @param integer $id
- * @param integer $sequence
- * @param integer $command
- * @param string $path
- * @param string $name
- * @param integer $timestamp
- *
- * @return JSONResponse
- */
- public function recover($id, $sequence, $command, $path, $name, $timestamp)
- {
- if ($command === Monitor::WRITE) {
- // Delete file
- if ($this->deleteFromStorage($path . '/' . $name)) {
- return new JSONResponse(['status' => 'success', 'id' => $id, 'sequence' => $sequence], Http::STATUS_OK);
- } else {
- return new JSONResponse(['status' => 'error', 'message' => 'File cannot be deleted.'], Http::STATUS_OK);
- }
- } else if ($command === Monitor::DELETE) {
- // Restore file
- $trashPath = '/'.$name.'.d'.$timestamp;;
- if ($this->restoreFromTrashbin($trashPath, $name, $timestamp) !== false) {
- return new JSONResponse(['status' => 'success', 'id' => $id, 'sequence' => $sequence], Http::STATUS_OK);
- }
-
- return new JSONResponse(['status' => 'error', 'message' => 'File does not exist.', 'path' => $trashPath, 'name' => $name, 'mtime' => $timestamp], Http::STATUS_OK);
- } else {
- // Scan can only detect WRITE and DELETE this should never happen.
- $this->logger->error('postRecover: RENAME or CREATE operation.', array('app' => Application::APP_ID));
- return new JSONResponse(['status' => 'error', 'message' => 'Wrong command.'], Http::STATUS_BAD_REQUEST);
- }
-
- }
-
- /**
- * The files to scan.
- *
- * @NoAdminRequired
- *
- * @return JSONResponse
- */
- public function filesToScan()
- {
- $start = time();
- $storageStructure = $this->getStorageStructure($this->userFolder);
- $trashStorageStructure = $this->getTrashStorageStructure();
-
- $allFiles = array();
-
- // convert file to json and merge into one array
- $files = $storageStructure->getFiles();
- for ($i = 0; $i < count($files); $i++) {
- $allFiles[] = ['id' => $files[$i]->getId(), 'path' => $files[$i]->getInternalPath(), 'timestamp' => $this->getLastActivity($files[$i]->getId())['timestamp']];
- }
- $trashFiles = $trashStorageStructure->getFiles();
- for ($i = 0; $i < count($trashFiles); $i++) {
- $allFiles[] = ['id' => $trashFiles[$i]->getId(), 'path' => $trashFiles[$i]->getInternalPath(), 'timestamp' => $trashFiles[$i]->getMtime()];
- }
-
- // sort ASC for timestamp
- usort($allFiles, function ($a, $b) {
- if ($a['timestamp'] === $b['timestamp']) {
- return 0;
- }
- return $a['timestamp'] - $b['timestamp'];
- });
-
- // build sequences
- $sequencesArray = array();
- $sequence = array();
- for ($i = 0; $i < count($allFiles); $i++) {
- if ($i === 0) {
- $sequence = array();
- } else {
- if ($allFiles[$i]['timestamp'] - $allFiles[$i - 1]['timestamp'] > 180) {
- $sequencesArray[] = $sequence;
- $sequence = array();
- }
- }
- $sequence[] = $allFiles[$i];
- }
- $sequencesArray[] = $sequence;
- $end = time();
-
- return new JSONResponse(['status' => 'success', 'sequences' => $sequencesArray, 'numberOfFiles' => $storageStructure->getNumberOfFiles(), 'scanDuration' => $end - $start], Http::STATUS_OK);
- }
-
- /**
- * Scan sequence.
- *
- * @NoAdminRequired
- *
- * @param string $sequence
- * @return JSONResponse
- */
- public function scanSequence($sequence) {
- if (sizeof($sequence) > $this->config->getAppValue(Application::APP_ID, 'minimum_sequence_length', 0)) {
- $sequenceResults = array();
- foreach ($sequence as $file) {
- try {
- $fileOperation = $this->buildFileOperation($file);
- } catch (NotAFileException $exception) {
- $this->logger->debug('scanSequence: Path to file doesn\'t lead to file object.', array('app' => Application::APP_ID));
- continue;
- } catch (NotFoundException $exception) {
- $this->logger->debug('scanSequence: Not found.', array('app' => Application::APP_ID));
- continue;
- }
-
- $this->classifier->classifyFile($fileOperation);
- $jsonSequence[] = ['userId' => $fileOperation->getUserId(), 'path' => $fileOperation->getPath(), 'originalName' => preg_replace('/.d[0-9]{10}/', '', $fileOperation->getOriginalName()),
- 'type' => $fileOperation->getType(), 'mimeType' => $fileOperation->getMimeType(), 'size' => $fileOperation->getSize(), 'corrupted' => $fileOperation->getCorrupted(), 'timestamp' => $fileOperation->getTimestamp(), 'entropy' => $fileOperation->getEntropy(),
- 'standardDeviation' => $fileOperation->getStandardDeviation(), 'command' => $fileOperation->getCommand(), 'fileClass' => $fileOperation->getFileClass(), 'fileExtensionClass' => $fileOperation->getFileExtensionClass(), 'suspicionClass' => $fileOperation->getSuspicionClass()];
- $fileOperationSequence[] = $fileOperation;
- }
- if (sizeof($fileOperationSequence) > 0) {
- $sequenceResult = $this->sequenceAnalyzer->analyze(0, $fileOperationSequence);
- return new JSONResponse(['status' => 'success', 'suspicionScore' => $sequenceResult->getSuspicionScore(), 'sequence' => $jsonSequence], Http::STATUS_OK);
- } else {
- return new JSONResponse(['status' => 'error', 'message' => 'The file(s) requested do(es) not exist.']);
- }
- } else {
- return new JSONResponse(['status' => 'error', 'message' => 'Sequence is to short.'], Http::STATUS_OK);
- }
- }
-
- /**
- * Just for testing purpose to mock the external static method.
- *
- * @return array
- */
- protected function getTrashFiles() {
- return Helper::getTrashFiles("/", $this->userId, 'mtime', false);
- }
-
- /**
- * Just for testing purpose to mock the external static method.
- *
- * @param string $trashPath
- * @param array $pathInfo
- * @param integer $timestamp
- * @return boolean
- */
- protected function restoreFromTrashbin($trashPath, $name, $timestamp)
- {
- return Trashbin::restore($trashPath, $name, $timestamp);
- }
-
- /**
- * Get last activity.
- * Visibility 'protected' is that it's possible to mock the database access.
- *
- * @param $objectId
- */
- protected function getLastActivity($objectId)
- {
- $query = $this->connection->getQueryBuilder();
- $query->select('*')->from('activity');
- $query->where($query->expr()->eq('affecteduser', $query->createNamedParameter($this->userId)))
- ->andWhere($query->expr()->eq('object_id', $query->createNamedParameter($objectId)));
- $result = $query->execute();
- while ($row = $result->fetch()) {
- $rows[] = $row;
- }
- $result->closeCursor();
- if (isset($rows) && is_array($rows)) {
- return array_pop($rows);
- } else {
- $this->logger->debug('getLastActivity: No activity found.', array('app' => Application::APP_ID));
- return 0;
- }
- }
-
- /**
- * Builds a file operations from a file info array.
- *
- * @param array $file
- * @return FileOperation
- */
- private function buildFileOperation($file)
- {
- $fileOperation = new FileOperation();
- $fileOperation->setUserId($this->userId);
- if (strpos($file['path'], 'files_trashbin') !== false) {
- $node = $this->userFolder->getParent()->get($file['path'] . '.d' . $file['timestamp']);
- $fileOperation->setCommand(Monitor::DELETE);
- $fileOperation->setTimestamp($file['timestamp']);
- $pathInfo = pathinfo($node->getInternalPath());
- $fileOperation->setPath($pathInfo['dirname']);
- } else {
- $node = $this->userFolder->getParent()->get($file['path']);
- $lastActivity = $this->getLastActivity($file['id']);
- $fileOperation->setCommand(Monitor::WRITE);
- $fileOperation->setTimestamp($lastActivity['timestamp']);
- $pathInfo = pathinfo($node->getInternalPath());
- $fileOperation->setPath(str_replace('files', '', $pathInfo['dirname']));
- }
- if (!($node instanceof File)) {
- throw new NotAFileException();
- }
- $fileOperation->setOriginalName($node->getName());
- $fileOperation->setType('file');
- $fileOperation->setMimeType($node->getMimeType());
- $fileOperation->setSize($node->getSize());
- $fileOperation->setTimestamp($file['timestamp']);
-
- // file extension analysis
- $fileExtensionResult = $this->fileExtensionAnalyzer->analyze($node->getInternalPath());
- $fileOperation->setFileExtensionClass($fileExtensionResult->getFileExtensionClass());
-
- $fileCorruptionResult = $this->fileCorruptionAnalyzer->analyze($node);
- $isCorrupted = $fileCorruptionResult->isCorrupted();
- $fileOperation->setCorrupted($isCorrupted);
- if ($isCorrupted) {
- $fileOperation->setFileExtensionClass(FileExtensionResult::SUSPICIOUS);
- }
-
- // entropy analysis
- $entropyResult = $this->entropyAnalyzer->analyze($node);
- $fileOperation->setEntropy($entropyResult->getEntropy());
- $fileOperation->setStandardDeviation($entropyResult->getStandardDeviation());
- $fileOperation->setFileClass($entropyResult->getFileClass());
-
- return $fileOperation;
- }
-
- /**
- * Get trash storage structure.
- *
- * @return StorageStructure
- */
- private function getTrashStorageStructure()
- {
- $storageStructure = new StorageStructure(0, []);
- $nodes = $this->getTrashFiles();
- foreach ($nodes as $node) {
- $storageStructure->addFile($node);
- $storageStructure->increaseNumberOfFiles();
- }
- return $storageStructure;
- }
-
- /**
- * Get storage structure recursively.
- *
- * @param INode $node
- *
- * @return StorageStructure
- */
- private function getStorageStructure($node)
- {
- // set count for node to 0
- $storageStructure = new StorageStructure(0, []);
- if ($node instanceof Folder) {
- // it's a folder
- $nodes = $node->getDirectoryListing();
- if (count($nodes) === 0) {
- // folder is empty so nothing to do
- return $storageStructure;
- }
- foreach ($nodes as $tmpNode) {
- // analyse files in subfolder
- $tmpStorageStructure = $this->getStorageStructure($tmpNode);
- $storageStructure->setFiles(array_merge($storageStructure->getFiles(), $tmpStorageStructure->getFiles()));
- $storageStructure->setNumberOfFiles($storageStructure->getNumberOfFiles() + $tmpStorageStructure->getNumberOfFiles());
- }
- return $storageStructure;
- }
- else if ($node instanceof File) {
- // it's a file
- $storageStructure->addFile($node);
- $storageStructure->increaseNumberOfFiles();
- return $storageStructure;
- }
- else {
- // it's me Mario.
- // there is nothing else than file or folder
- $this->logger->error('getStorageStructure: Neither file nor folder.', array('app' => Application::APP_ID));
- }
- }
-
- /**
- * Deletes a file from the storage.
- *
- * @param string $path
- *
- * @return bool
- */
- private function deleteFromStorage($path)
- {
- try {
- $node = $this->userFolder->get($path);
- if ($node instanceof File && $node->isDeletable()) {
- $node->delete();
- } else {
- return false;
- }
-
- return true;
- } catch (\OCP\Files\NotFoundException $exception) {
- // Nothing found
- $this->logger->debug('deleteFromStorage: Not found exception.', array('app' => Application::APP_ID));
-
- return true;
- }
- }
-}