protected override async Task ExecuteAsync(CancellationToken stoppingToken) { //Read configs int checkInterval = Int32.Parse(_conf.GetSection("SignService")["CheckIntervalSeconds"]); _l.Debug($"Signing Service started; will check for new entries each {checkInterval} seconds."); while (!stoppingToken.IsCancellationRequested) { var signItems = _asvc.getItemsToBeSigned(); foreach (var ac in signItems) { _l.Debug($"Fetched item {ac.UniqueKey} for signing."); ac.Status = ApiActivity.ApiStatus.Signing; ac.Message = "Signing.."; _asvc.addUpdateApiActivity(ac); if (signFile(ac)) { ac.Status = ApiActivity.ApiStatus.Ready; // ac.Message already filled by output parser _asvc.addUpdateApiActivity(ac); _l.Information(ac.ToString()); } else { // ac.Message Error already filled by output parser ac.Status = ApiActivity.ApiStatus.Error; _asvc.addUpdateApiActivity(ac); _l.Error(ac.ToString()); } } await Task.Delay(1000 *checkInterval, stoppingToken); } }
protected override async Task ExecuteAsync(CancellationToken stoppingToken) { //Read configs var strCheckInterval = _conf.GetValue <string>("AnalyseService:CheckIntervalSeconds"); _l.Information($"chekinterval {strCheckInterval}"); int checkInterval = 15; try { checkInterval = System.Int32.Parse(strCheckInterval); } catch (Exception e) { _l.Error(e.Message); } // Read secrets JObject secretsConfig = JObject.Parse(File.ReadAllText(@"secrets.json")); //secrets.json file not checked in. .gitignore var vtApiKey = (string)secretsConfig["ApiKeys"]["VirusTotal"]; // Init analyserPlugins VirusTotal vt = new VirusTotal(vtApiKey); vt.UseTLS = true; _l.Debug($"Analyse Service started; will check for new entries each {checkInterval} seconds."); while (!stoppingToken.IsCancellationRequested) { var analyseItems = _asvc.getItemsToBeAnalysed(); foreach (var ac in analyseItems) { _l.Debug($"Fetched item {ac.UniqueKey} for analysing."); ac.Status = ApiActivity.ApiStatus.Analysing; ac.Message = "Analysing"; _asvc.addUpdateApiActivity(ac); //prepare filestream using (Stream fs = File.OpenRead(ac.SystemOfficeFilename)) { // new Analyser Service start e.g. VirusTotal ac.Message = $"Checking Virustotal for known file {ac.UserOfficeFilename}. This can take up to 5 Min..."; _asvc.addUpdateApiActivity(ac); _l.Information(ac.Message); // first check if file already known to VT var fileReport = await vt.GetFileReportAsync(fs); _l.Information($"File Report requested for Resource {fileReport.Resource}"); if (fileReport.ResponseCode == VirusTotalNet.ResponseCodes.FileReportResponseCode.Queued) { // file already submitted but still scanned -> not scanning again _l.Information($"File {ac.UserOfficeFilename} already submitted. Not scanning again. Resetting result to {ApiActivity.ApiStatus.QueuedAnalysis}"); ac.Status = ApiActivity.ApiStatus.QueuedAnalysis; _asvc.addUpdateApiActivity(ac); } if (fileReport.ResponseCode == VirusTotalNet.ResponseCodes.FileReportResponseCode.NotPresent) { ScanResult scanResult = null; // reset stream, otherwise it's posted from last position :( fs.Seek(0, SeekOrigin.Begin); //not known to VT -> start new scan ac.Message = "File not know to VT yet. Starting Scan..."; _asvc.addUpdateApiActivity(ac); _l.Information(ac.Message); scanResult = await vt.ScanFileAsync(fs, ac.SystemOfficeFilename); if (scanResult.ResponseCode == VirusTotalNet.ResponseCodes.ScanFileResponseCode.Queued) { // set to result queued to be picked up by loop next time; then Results should be already known // and can be retrieved. ac.Message = $"File Queued for Analysis in VirusTotal with ScanID {scanResult.ScanId}."; ac.Status = ApiActivity.ApiStatus.QueuedAnalysis; _asvc.addUpdateApiActivity(ac); _l.Information(ac.Message + $"Resetting result to {ApiActivity.ApiStatus.QueuedAnalysis}"); } } if (fileReport.ResponseCode == VirusTotalNet.ResponseCodes.FileReportResponseCode.Present) { //Filereport here, check _l.Information($"Filereport retrieved successfully. Checking if file file clean.."); // how many positives are OK? int maxPositives = Int32.Parse(_conf["AnalyseService:SecurityPlugins:Virustotal:MaxPositives"]); if (fileReport.Positives < maxPositives) { //file clean ac.Message = $"File scanned by VT: File has {fileReport.Positives} of max {maxPositives} Positives. File clean! Queued for Signing"; ac.Status = ApiActivity.ApiStatus.QueuedSigning; //send to signing service _asvc.addUpdateApiActivity(ac); _l.Information(ac.Message); } else { //file infected ac.Message = $"File scanned by VT: File has {fileReport.Positives} of max {maxPositives} Positives. File infected!! Cancel Signing"; ac.Status = ApiActivity.ApiStatus.Error; _asvc.addUpdateApiActivity(ac); _l.Warning(ac.Message); } } } } await Task.Delay(1000 *checkInterval, stoppingToken); } }
public IActionResult CheckOfficeFile(IFormFile officeFile) { ApiActivity ac = new ApiActivity(); ac.ClientIPAddress = Request.HttpContext.Connection.RemoteIpAddress.ToString(); ac.Operation = ApiActivity.ApiOperation.Verify; ac.Message = $"Check Office File started"; ac.Status = ApiActivity.ApiStatus.Verifying; ac.StatusUrl = GHelper.generateUrl(GHelper.UrlType.StatusUrl, ac, _httpctx); ac.DownloadUrl = GHelper.generateUrl(GHelper.UrlType.DownloadUrl, ac, _httpctx); _asvc.addUpdateApiActivity(ac); if (officeFile != null) { ac.UserOfficeFilename = officeFile.FileName; //check valid file extension _l.Debug("Checking for valid file extensions..."); string officeFileExt = Path.GetExtension(officeFile.FileName.ToLowerInvariant()); if (!GHelper.fileHasAllowedExtension(GHelper.ExtensionType.OfficeFile, officeFileExt)) { ac.Message = $"Office File extension {officeFileExt} not valid!"; ac.Status = ApiActivity.ApiStatus.Error; _asvc.addUpdateApiActivity(ac); _l.Error(ac.Message); return(Content(ac.getWebresult())); } // check magic number file types _l.Debug("Checking for magic number of file..."); if (!(GHelper.fileHasValidFormat(GHelper.ExtensionType.OfficeFile, officeFile.OpenReadStream()))) { ac.Message = $"Office File {officeFile.FileName} not a valid office file!"; ac.Status = ApiActivity.ApiStatus.Error; _asvc.addUpdateApiActivity(ac); _l.Error(ac.Message); return(Content(ac.getWebresult())); } //save office file string uniFilenameOfficeFile = GHelper.createUniqueFileName(officeFile.FileName); string systemFolderOfficeFile = GHelper.getOfficeFilesSystemDir(_webHostEnv, _conf); string systemFileNameOfficeFile = Path.Combine(systemFolderOfficeFile, uniFilenameOfficeFile); ac.SystemOfficeFilename = systemFileNameOfficeFile; // create dir if not exist System.IO.Directory.CreateDirectory(systemFolderOfficeFile); _l.Debug($"Saving file to {systemFileNameOfficeFile}"); using (var fileStream = new FileStream(systemFileNameOfficeFile, FileMode.Create)) { officeFile.CopyTo(fileStream); } //verify file // prepare run ProcessStartInfo psi = new ProcessStartInfo(); psi.FileName = _webHostEnv.ContentRootPath + @"\lib\signtool.exe"; psi.RedirectStandardError = true; psi.RedirectStandardOutput = true; psi.UseShellExecute = false; //use quoted filename otherwise systempath is revealed in error message!! psi.Arguments = $"verify /pa /debug /v \"{systemFileNameOfficeFile}\""; _l.Debug($"Executing {psi.FileName} {psi.Arguments}..."); // execute run StringBuilder stdOut = new StringBuilder(); StringBuilder stdErr = new StringBuilder(); Process p = new Process(); p.StartInfo = psi; p.Start(); while (!p.StandardOutput.EndOfStream) { stdOut.AppendLine(p.StandardOutput.ReadLine()); } while (!p.StandardError.EndOfStream) { stdErr.AppendLine(p.StandardError.ReadLine()); } p.WaitForExit(); _l.Debug("Process exited. Parsing..."); // parse result ac = SignToolOutputParser.parseSignToolOutput(SignToolOutputParser.SignToolOperation.Verify, ac, stdOut.ToString(), stdErr.ToString()); _l.Debug($"Parsed result = {ac.ToString()}"); _l.Debug($"Deleting file {systemFileNameOfficeFile}"); // delete after verify System.IO.File.Delete(Path.Combine(systemFileNameOfficeFile)); _asvc.addUpdateApiActivity(ac); return(Content(ac.getWebresult())); } else { var message = ("No Files submitted for Verifying!"); _l.Warning(message); ac.Operation = ApiActivity.ApiOperation.Verify; ac.Status = ApiActivity.ApiStatus.Error; ac.Message = message; return(Content(ac.getWebresult())); } }