diff --git a/app/routes.py b/app/routes.py index d336044..f68565f 100644 --- a/app/routes.py +++ b/app/routes.py @@ -324,6 +324,156 @@ def validate_pid(pid): return False, f"Error validating PID: {str(e)}" +def get_entropy_risk_level(entropy): + if entropy > 7.2: + return 'High' + elif entropy > 6.8: + return 'Medium' + return 'Low' + +def format_hex(value): + if isinstance(value, str) and value.startswith('0x'): + return value.lower() + try: + return f"0x{int(value):x}" + except (ValueError, TypeError): + return str(value) + +def calculate_file_risk(file_info, static_results=None, dynamic_results=None): + """ + Calculate overall file risk score based on all available analysis results. + Returns a tuple of (risk_score, risk_factors) where: + - risk_score is a float between 0 and 100 + - risk_factors is a list of contributing risk factors + """ + risk_score = 0 + risk_factors = [] + + # Base weights for different analysis types + WEIGHTS = { + 'pe_info': 0.3, + 'static': 0.3, + 'dynamic': 0.4 + } + + # 1. PE Information Risk Calculation + if file_info.get('pe_info'): + pe_risk = 0 + pe_info = file_info['pe_info'] + + # Check section entropy + high_entropy_sections = 0 + for section in pe_info.get('sections', []): + if section.get('entropy', 0) > 7.2: # High risk threshold + high_entropy_sections += 1 + risk_factors.append(f"High entropy in section {section.get('name', 'UNKNOWN')}") + + pe_risk += min(high_entropy_sections * 20, 40) # Cap at 40 points + + # Check suspicious imports + suspicious_imports = len(pe_info.get('suspicious_imports', [])) + if suspicious_imports > 0: + pe_risk += min(suspicious_imports * 10, 30) # Cap at 30 points + risk_factors.append(f"Found {suspicious_imports} suspicious imports") + + # Check checksum mismatch + if pe_info.get('checksum_info'): + checksum = pe_info['checksum_info'] + if checksum.get('stored_checksum') != checksum.get('calculated_checksum'): + pe_risk += 30 + risk_factors.append("PE checksum mismatch detected") + + risk_score += (pe_risk / 100) * WEIGHTS['pe_info'] * 100 + + # 2. Static Analysis Risk Calculation + if static_results: + static_risk = 0 + + # YARA detections (static) + yara_matches = static_results.get('yara', {}).get('matches', []) + unique_yara_rules = len({match.get('rule') for match in yara_matches if match.get('rule')}) + if unique_yara_rules > 0: + static_risk += min(unique_yara_rules * 15, 50) # Cap at 50 points + risk_factors.append(f"Found {unique_yara_rules} YARA rule matches (static)") + + # CheckPLZ findings + checkplz_findings = static_results.get('checkplz', {}).get('findings', {}) + if checkplz_findings.get('initial_threat'): + static_risk += 50 + risk_factors.append("CheckPLZ detected initial threat indicators") + + risk_score += (static_risk / 100) * WEIGHTS['static'] * 100 + + # 3. Dynamic Analysis Risk Calculation + if dynamic_results: + dynamic_risk = 0 + + # YARA detections (dynamic) + yara_matches = dynamic_results.get('yara', {}).get('matches', []) + unique_yara_rules = len({match.get('rule') for match in yara_matches if match.get('rule')}) + if unique_yara_rules > 0: + dynamic_risk += min(unique_yara_rules * 15, 40) # Cap at 40 points + risk_factors.append(f"Found {unique_yara_rules} YARA rule matches (dynamic)") + + # PE-Sieve detections + pesieve_suspicious = int(dynamic_results.get('pe_sieve', {}) + .get('findings', {}).get('total_suspicious', 0)) + if pesieve_suspicious > 0: + dynamic_risk += min(pesieve_suspicious * 20, 40) # Cap at 40 points + risk_factors.append(f"PE-Sieve found {pesieve_suspicious} suspicious indicators") + + # Moneta memory anomalies + moneta_findings = dynamic_results.get('moneta', {}).get('findings', {}) + memory_anomalies = sum([ + int(moneta_findings.get('total_private_rwx', 0) or 0), + int(moneta_findings.get('total_private_rx', 0) or 0), + int(moneta_findings.get('total_modified_code', 0) or 0), + int(moneta_findings.get('total_heap_executable', 0) or 0), + int(moneta_findings.get('total_modified_pe_header', 0) or 0), + int(moneta_findings.get('total_inconsistent_x', 0) or 0), + int(moneta_findings.get('total_missing_peb', 0) or 0), + int(moneta_findings.get('total_mismatching_peb', 0) or 0) + ]) + if memory_anomalies > 0: + dynamic_risk += min(memory_anomalies * 10, 30) # Cap at 30 points + risk_factors.append(f"Found {memory_anomalies} memory anomalies") + + # Patriot detections + patriot_findings = len(dynamic_results.get('patriot', {}) + .get('findings', {}).get('findings', [])) + if patriot_findings > 0: + dynamic_risk += min(patriot_findings * 15, 30) # Cap at 30 points + risk_factors.append(f"Found {patriot_findings} suspicious behaviors") + + # HSB detections + hsb_findings = dynamic_results.get('hsb', {}).get('findings', {}) + if hsb_findings and hsb_findings.get('detections'): + hsb_detections = len(hsb_findings['detections'][0].get('findings', [])) + if hsb_detections > 0: + dynamic_risk += min(hsb_detections * 20, 40) # Cap at 40 points + risk_factors.append(f"Found {hsb_detections} HSB detections") + + risk_score += (dynamic_risk / 100) * WEIGHTS['dynamic'] * 100 + + # Normalize final score to 0-100 range and round to 2 decimal places + risk_score = round(min(max(risk_score, 0), 100), 2) + + return risk_score, risk_factors + +def get_risk_level(risk_score): + """ + Convert numerical risk score to categorical risk level + """ + if risk_score >= 75: + return "Critical" + elif risk_score >= 50: + return "High" + elif risk_score >= 25: + return "Medium" + else: + return "Low" + + def register_routes(app): analysis_manager = AnalysisManager(app.config) @@ -423,9 +573,62 @@ def register_routes(app): with open(file_info_path, 'r') as f: file_info = json.load(f) + + # Load static and dynamic results if they exist + static_results = None + dynamic_results = None + + static_path = os.path.join(result_path, 'static_analysis_results.json') + if os.path.exists(static_path): + with open(static_path, 'r') as f: + static_results = json.load(f) + + dynamic_path = os.path.join(result_path, 'dynamic_analysis_results.json') + if os.path.exists(dynamic_path): + with open(dynamic_path, 'r') as f: + dynamic_results = json.load(f) + + # Calculate overall risk + risk_score, risk_factors = calculate_file_risk(file_info, static_results, dynamic_results) + risk_level = get_risk_level(risk_score) + + # Add risk information to file_info + file_info['risk_assessment'] = { + 'score': risk_score, + 'level': risk_level, + 'factors': risk_factors + } + if analysis_type == 'info': - return render_template('file_info.html', file_info=file_info) + # Add helper data for the template + if 'pe_info' in file_info: + # Calculate section entropy risk levels + for section in file_info['pe_info']['sections']: + section['entropy_risk'] = get_entropy_risk_level(section['entropy']) + + # Group suspicious imports by DLL + grouped_imports = {} + for imp in file_info['pe_info'].get('suspicious_imports', []): + dll = imp['dll'] + if dll not in grouped_imports: + grouped_imports[dll] = [] + grouped_imports[dll].append(imp) + file_info['pe_info']['grouped_suspicious_imports'] = grouped_imports + + # Format checksum values + if 'checksum_info' in file_info['pe_info']: + checksum = file_info['pe_info']['checksum_info'] + checksum['stored_checksum'] = format_hex(checksum['stored_checksum']) + checksum['calculated_checksum'] = format_hex(checksum['calculated_checksum']) + + return render_template('file_info.html', + file_info=file_info, + entropy_risk_levels={ + 'High': 7.2, + 'Medium': 6.8, + 'Low': 0 + }) elif analysis_type in ['static', 'dynamic']: results_file = f'{analysis_type}_analysis_results.json' @@ -437,30 +640,29 @@ def register_routes(app): analysis_results = json.load(f) if analysis_type == 'static': - # Calculate detection counts for static analysis with safe defaults + # Calculate detection counts for static analysis with safe defaults and proper error handling try: yara_matches = analysis_results.get('yara', {}).get('matches', []) - yara_detections = len(yara_matches) if yara_matches is not None else 0 - except: + yara_detections = len({match.get('rule') for match in yara_matches}) if isinstance(yara_matches, list) else 0 + except (TypeError, ValueError): yara_detections = 0 - try: - checkplz_findings = analysis_results.get('checkplz', {}).get('findings', {}) - checkplz_detections = 1 if checkplz_findings and checkplz_findings.get('initial_threat') else 0 - except: - checkplz_detections = 0 + checkplz_detections = 0 + checkplz_findings = analysis_results.get('checkplz', {}).get('findings', {}) + if isinstance(checkplz_findings, dict): + checkplz_detections = 1 if checkplz_findings.get('initial_threat') else 0 - # Format scan duration as MM:SS.mmm + # Format scan duration with proper error handling + formatted_duration = "00:00.000" try: - scan_duration = analysis_results.get('checkplz', {}).get('findings', {}).get('scan_results', {}).get('scan_duration', 0) - if scan_duration is None: - scan_duration = 0 + scan_duration = float(analysis_results.get('checkplz', {}).get('findings', {}) + .get('scan_results', {}).get('scan_duration', 0)) minutes = int(scan_duration // 60) seconds = int(scan_duration % 60) milliseconds = int((scan_duration % 1) * 1000) formatted_duration = f"{minutes:02d}:{seconds:02d}.{milliseconds:03d}" - except: - formatted_duration = "00:00.000" + except (TypeError, ValueError, AttributeError): + pass return render_template('static_analysis.html', file_info=file_info, @@ -470,26 +672,62 @@ def register_routes(app): scan_duration=formatted_duration) elif analysis_type == 'dynamic': - # Calculate detection counts for dynamic analysis - yara_detections = len(analysis_results.get('yara', {}).get('matches', [])) if analysis_results.get('yara') else 0 - pesieve_detections = analysis_results.get('pe_sieve', {}).get('findings', {}).get('total_suspicious', 0) - moneta_detections = ( - analysis_results.get('moneta', {}).get('findings', {}).get('total_private_rwx', 0) + - analysis_results.get('moneta', {}).get('findings', {}).get('total_abnormal_private_exec', 0) - ) - patriot_detections = len(analysis_results.get('patriot', {}).get('findings', {}).get('findings', [])) - hsb_detections = analysis_results.get('hsb', {}).get('findings', {}).get('summary', {}).get('total_findings', 0) + # YARA: Count unique rule matches + try: + yara_matches = analysis_results.get('yara', {}).get('matches', []) + yara_detections = len({match.get('rule') for match in yara_matches}) if yara_matches else 0 + except (TypeError, ValueError): + yara_detections = 0 + + # PE-Sieve: Get total suspicious count + try: + pesieve_findings = analysis_results.get('pe_sieve', {}).get('findings', {}) + pesieve_detections = int(pesieve_findings.get('total_suspicious', 0) or 0) + except (TypeError, ValueError): + pesieve_detections = 0 + + # Moneta: Count memory anomalies including PEB violations + try: + moneta_findings = analysis_results.get('moneta', {}).get('findings', {}) + moneta_detections = sum([ + int(moneta_findings.get('total_private_rwx', 0) or 0), + int(moneta_findings.get('total_private_rx', 0) or 0), + int(moneta_findings.get('total_modified_code', 0) or 0), + int(moneta_findings.get('total_heap_executable', 0) or 0), + int(moneta_findings.get('total_modified_pe_header', 0) or 0), + int(moneta_findings.get('total_inconsistent_x', 0) or 0), + int(moneta_findings.get('total_missing_peb', 0) or 0), + int(moneta_findings.get('total_mismatching_peb', 0) or 0) + ]) + except (TypeError, ValueError): + moneta_detections = 0 + + # Patriot: Get findings directly from findings array length + try: + patriot_findings = analysis_results.get('patriot', {}).get('findings', {}).get('findings', []) + patriot_detections = len(patriot_findings) if isinstance(patriot_findings, list) else 0 + except (TypeError, ValueError): + patriot_detections = 0 + + # HSB: Count findings from severity counts and add any findings in the detections array + # HSB: Get total findings directly from detections[0] + try: + hsb_findings = analysis_results.get('hsb', {}).get('findings', {}) + if hsb_findings and hsb_findings.get('detections'): + hsb_detections = len(hsb_findings['detections'][0].get('findings', [])) + else: + hsb_detections = 0 + except (TypeError, ValueError, IndexError): + hsb_detections = 0 return render_template('dynamic_analysis.html', - file_info=file_info, - analysis_results=analysis_results, - yara_detections=yara_detections, - pesieve_detections=pesieve_detections, - moneta_detections=moneta_detections, - patriot_detections=patriot_detections, - hsb_detections=hsb_detections) - - return render_template('error.html', error='Invalid analysis type'), 400 + file_info=file_info, + analysis_results=analysis_results, + yara_detections=yara_detections, + pesieve_detections=pesieve_detections, + moneta_detections=moneta_detections, + patriot_detections=patriot_detections, + hsb_detections=hsb_detections) except Exception as e: return render_template('error.html', error=str(e)), 500 @@ -646,8 +884,7 @@ def register_routes(app): 'issues': [str(e)] }), 500 - @app.route('/summary') - @app.route('/summary.html') + @app.route('/summary', methods=['GET']) def summary_page(): """Route for the summary page""" return render_template('summary.html') @@ -657,24 +894,37 @@ def register_routes(app): try: results_dir = app.config['upload']['result_folder'] summary = {} - # Iterate through all subdirectories in the results folder for md5_dir in os.listdir(results_dir): dir_path = os.path.join(results_dir, md5_dir) if not os.path.isdir(dir_path): continue - + # Read file_info.json file_info_path = os.path.join(dir_path, 'file_info.json') if not os.path.exists(file_info_path): continue - + with open(file_info_path, 'r') as f: file_info = json.load(f) - # Check for existence of analysis results - static_exists = os.path.exists(os.path.join(dir_path, 'static_analysis_results.json')) - dynamic_exists = os.path.exists(os.path.join(dir_path, 'dynamic_analysis_results.json')) + # Load static analysis results if they exist + static_results = None + static_path = os.path.join(dir_path, 'static_analysis_results.json') + if os.path.exists(static_path): + with open(static_path, 'r') as f: + static_results = json.load(f) + + # Load dynamic analysis results if they exist + dynamic_results = None + dynamic_path = os.path.join(dir_path, 'dynamic_analysis_results.json') + if os.path.exists(dynamic_path): + with open(dynamic_path, 'r') as f: + dynamic_results = json.load(f) + + # Calculate risk score using our comprehensive function + risk_score, risk_factors = calculate_file_risk(file_info, static_results, dynamic_results) + risk_level = get_risk_level(risk_score) # Create summary for this file summary[md5_dir] = { @@ -686,8 +936,14 @@ def register_routes(app): 'result_dir_full_path': os.path.abspath(dir_path), 'entropy_value': file_info.get('entropy_analysis', {}).get('value', 0), 'detection_risk': file_info.get('entropy_analysis', {}).get('detection_risk', 'Unknown'), - 'has_static_analysis': static_exists, - 'has_dynamic_analysis': dynamic_exists + 'has_static_analysis': os.path.exists(static_path), + 'has_dynamic_analysis': os.path.exists(dynamic_path), + # Add the new risk assessment + 'risk_assessment': { + 'score': risk_score, + 'level': risk_level, + 'factors': risk_factors + } } return jsonify({ @@ -695,7 +951,6 @@ def register_routes(app): 'count': len(summary), 'files': summary }) - except Exception as e: return jsonify({ 'status': 'error', diff --git a/app/static/js/status.js b/app/static/js/status.js index 6c3bf3e..3455355 100644 --- a/app/static/js/status.js +++ b/app/static/js/status.js @@ -1,3 +1,5 @@ +// app/static/js/status.js + // Constants const CONFIG = { notificationDuration: 5000, diff --git a/app/static/js/summary.js b/app/static/js/summary.js index f9cadfa..b130bb3 100644 --- a/app/static/js/summary.js +++ b/app/static/js/summary.js @@ -43,39 +43,41 @@ function updateStats() { const totalBytes = files.reduce((sum, file) => sum + (file.file_size || 0), 0); elements.storageUsed.textContent = formatFileSize(totalBytes); - // Calculate average entropy and determine risk - const filesWithEntropy = files.filter(f => f.entropy_value); + // Calculate average risk score + const filesWithRisk = files.filter(f => f.risk_assessment && f.risk_assessment.score !== undefined); - if (filesWithEntropy.length > 0) { - const avgEntropy = filesWithEntropy.reduce((sum, file) => sum + file.entropy_value, 0) / filesWithEntropy.length; + if (filesWithRisk.length > 0) { + const avgRiskScore = filesWithRisk.reduce((sum, file) => + sum + file.risk_assessment.score, 0) / filesWithRisk.length; - // Determine risk level based on entropy value - let riskText; - let riskClass; + // Determine risk level based on risk score + let riskText, riskClass; - if (avgEntropy >= 7.2) { + if (avgRiskScore >= 75) { + riskText = 'Critical'; + riskClass = 'bg-red-900 text-white'; + } else if (avgRiskScore >= 50) { riskText = 'High'; riskClass = 'bg-red-500 text-white'; - } else if (avgEntropy >= 6.8) { + } else if (avgRiskScore >= 25) { riskText = 'Medium'; riskClass = 'bg-yellow-500 text-black'; } else { riskText = 'Low'; riskClass = 'bg-green-500 text-white'; } - // console.log(avgEntropy); - elements.averageRisk.textContent = riskText; + elements.averageRisk.textContent = `${riskText} Risk`; elements.averageRisk.className = 'px-2 py-1 text-sm rounded-lg inline-flex items-center justify-center font-medium ' + riskClass; - elements.averageEntropy.textContent = `Entropy: ${avgEntropy.toFixed(3)}`; + elements.averageEntropy.textContent = `Risk Score: ${avgRiskScore.toFixed(1)}%`; } else { elements.averageRisk.textContent = '-'; elements.averageRisk.className = 'px-2 py-1 text-sm rounded-lg inline-flex items-center justify-center font-medium bg-gray-500 text-white'; - elements.averageEntropy.textContent = 'Entropy: -'; + elements.averageEntropy.textContent = 'Risk Score: -'; } } - // Render file list +// Render file list - Update the risk display part function renderFiles() { const filteredFiles = filterFiles(files); const sortedFiles = sortFiles(filteredFiles); @@ -90,34 +92,33 @@ function renderFiles() { row.querySelector('[data-field="fileName"]').textContent = file.filename; row.querySelector('[data-field="fileHash"]').textContent = file.md5; - // Entropy and Risk - const entropyEl = row.querySelector('[data-field="fileEntropy"]'); + // Risk Assessment const riskEl = row.querySelector('[data-field="fileRisk"]'); + const entropyEl = row.querySelector('[data-field="fileEntropy"]'); - if (file.entropy_value) { - entropyEl.textContent = `Entropy: ${file.entropy_value.toFixed(2)}`; - } - - if (file.detection_risk) { - riskEl.textContent = file.detection_risk; + if (file.risk_assessment) { + const { level, score, factors } = file.risk_assessment; + riskEl.textContent = `${level} (${score}%)`; riskEl.className = 'px-3 py-1 text-xs rounded-lg inline-flex items-center justify-center font-medium'; - switch(file.detection_risk.toLowerCase()) { - case 'high': - // riskEl.className += ' bg-red-500/10 text-red-400 border border-red-900/20'; - riskEl.className += ' bg-red-500 text-white'; - break; - case 'medium': - // riskEl.className += ' bg-yellow-500/10 text-yellow-400 border border-yellow-900/20'; - riskEl.className += ' bg-yellow-500 text-black'; - break; - case 'low': - // riskEl.className += ' bg-green-500/10 text-green-400 border border-green-900/20'; - riskEl.className += ' bg-green-500 text-white'; - break; - default: - // riskEl.className += ' bg-gray-500/10 text-gray-400 border border-gray-900/20'; - riskEl.className += ' bg-gray-500 text-white'; + + if (score >= 75) { + riskEl.className += ' bg-red-900 text-white'; + } else if (score >= 50) { + riskEl.className += ' bg-red-500 text-white'; + } else if (score >= 25) { + riskEl.className += ' bg-yellow-500 text-black'; + } else { + riskEl.className += ' bg-green-500 text-white'; } + + // Show first risk factor if available + if (factors && factors.length > 0) { + entropyEl.textContent = factors[0]; + } + } else { + riskEl.textContent = 'Unknown'; + riskEl.className += ' bg-gray-500 text-white px-3 py-1 text-xs rounded-lg inline-flex items-center justify-center font-medium'; + entropyEl.textContent = ''; } // File type // const typeCell = row.querySelector('#fileType'); @@ -148,17 +149,18 @@ function renderFiles() { } // Filter files based on search and type +// Update filter function to use new risk levels function filterFiles(files) { const searchTerm = elements.searchFiles.value.toLowerCase(); const fileType = elements.filterType.value; - const riskLevel = elements.filterRisk.value; + const riskLevel = elements.filterRisk.value.toLowerCase(); return files.filter(file => { const matchesSearch = file.filename.toLowerCase().includes(searchTerm) || file.md5.toLowerCase().includes(searchTerm); const matchesType = fileType === 'all' || file.filename.toLowerCase().endsWith(fileType); const matchesRisk = riskLevel === 'all' || - (file.detection_risk && file.detection_risk.toLowerCase() === riskLevel); + (file.risk_assessment && file.risk_assessment.level.toLowerCase() === riskLevel); return matchesSearch && matchesType && matchesRisk; }); } @@ -177,8 +179,8 @@ function sortFiles(files) { return new Date(a.upload_time).getTime() - new Date(b.upload_time).getTime(); case 'size': return (b.file_size || 0) - (a.file_size || 0); - case 'entropy': - return (b.entropy_value || 0) - (a.entropy_value || 0); + case 'risk': + return ((b.risk_assessment?.score || 0) - (a.risk_assessment?.score || 0)); default: return 0; } @@ -204,6 +206,9 @@ function getAnalysisStatus(file) { }; } +// app/static/js/summery.js + + // View file details function viewFile(md5) { window.location.href = `/file/${md5}/info`; diff --git a/app/templates/dynamic_analysis.html b/app/templates/dynamic_analysis.html index 71621cf..2e38a24 100644 --- a/app/templates/dynamic_analysis.html +++ b/app/templates/dynamic_analysis.html @@ -48,7 +48,6 @@ -
@@ -70,8 +69,21 @@ - @@ -84,8 +96,17 @@ - @@ -98,8 +119,32 @@ - @@ -112,8 +157,18 @@ - @@ -126,14 +181,22 @@ -
{{ yara_detections }} - {{ yara_detections|string + ' rule matches found' if yara_detections else 'No threats detected' }} + + {% if yara_detections %} +
+ {% for match in analysis_results.yara.matches %} +
+ Rule: {{ match.rule }} + {% if match.metadata %} + (Severity: {{ match.metadata.severity }}) + {% endif %} +
+ {% endfor %} +
+ {% else %} + No threats detected + {% endif %}
{{ pesieve_detections }} - {{ pesieve_detections|string + ' suspicious modifications found' if pesieve_detections else 'No modifications detected' }} + + {% if pesieve_detections %} + {% set findings = analysis_results.pe_sieve.findings %} +
+
Implanted: {{ findings.implanted }}
+
Implanted PE: {{ findings.implanted_pe }}
+
Implanted shellcode: {{ findings.implanted_shc }}
+
+ {% else %} + No modifications detected + {% endif %}
{{ moneta_detections }} - {{ 'Memory anomalies found' if moneta_detections else 'No anomalies detected' }} + + {% if moneta_detections %} + {% set findings = analysis_results.moneta.findings %} +
+ {% if findings.total_private_rwx > 0 %} +
Private RWX: {{ findings.total_private_rwx }}
+ {% endif %} + {% if findings.total_private_rx > 0 %} +
Private RX: {{ findings.total_private_rx }}
+ {% endif %} + {% if findings.total_modified_code > 0 %} +
Modified Code: {{ findings.total_modified_code }}
+ {% endif %} + {% if findings.total_heap_executable > 0 %} +
Heap Executable: {{ findings.total_heap_executable }}
+ {% endif %} + {% if findings.total_missing_peb > 0 %} +
Missing PEB: {{ findings.total_missing_peb }}
+ {% endif %} + {% if findings.total_mismatching_peb > 0 %} +
Mismatching PEB: {{ findings.total_mismatching_peb }}
+ {% endif %} +
+ {% else %} + No anomalies detected + {% endif %}
{{ patriot_detections }} - {{ patriot_detections|string + ' suspicious activities found' if patriot_detections else 'No suspicious activities' }} + + {% if patriot_detections %} +
+ {% for finding in analysis_results.patriot.findings.findings %} +
+ {{ finding.type }} ({{ finding.level }}) +
+ {% endfor %} +
+ {% else %} + No suspicious activities + {% endif %}
{{ hsb_detections }} - {{ 'Suspicious behavior detected' if hsb_detections else 'No suspicious behavior' }} + + {% if hsb_detections %} +
+ {% for detection in analysis_results.hsb.findings.detections %} + {% for finding in detection.findings %} +
{{ finding.type }} ({{ finding.severity }})
+ {% endfor %} + {% endfor %} +
+ {% else %} + No suspicious behavior + {% endif %}
- - {% endblock %} \ No newline at end of file diff --git a/app/templates/file_info.html b/app/templates/file_info.html index 3fe1183..847f58b 100644 --- a/app/templates/file_info.html +++ b/app/templates/file_info.html @@ -2,41 +2,121 @@ {% block content %}
- +

{{ file_info.original_name }}

MD5: {{ file_info.md5 }}

- - {{ file_info.entropy_analysis.detection_risk }} Risk - +
+ +
+ Entropy + + {{ "%.2f"|format(file_info.entropy) }} + +
+ {# Replace the Risk Level Badge #} + + {{ file_info.risk_assessment.level }} Risk ({{ file_info.risk_assessment.score }}%) + +
- + + {# Add this right after the header div, before Navigation Buttons #} + +
+
+

Risk Assessment

+ + {{ file_info.risk_assessment.level }} Risk ({{ file_info.risk_assessment.score }}%) + +
+ + +
+
+
+
+ + + {% if file_info.risk_assessment.factors %} +
+

Risk Factors

+ {% for factor in file_info.risk_assessment.factors %} +
+ + + + {{ factor }} +
+ {% endfor %} +
+ {% endif %} +
+ +
- + + +
+

File Hashes

+
+
+

MD5

+

{{ file_info.md5 }}

+
+
+

SHA256

+

{{ file_info.sha256 }}

+
+
+
+ +

Basic Information

@@ -48,26 +128,31 @@

File Type

{{ file_info.mime_type }}

+
+

Extension

+

{{ file_info.extension }}

+

Upload Time

{{ file_info.upload_time }}

-
-

SHA256

-

{{ file_info.sha256 }}

-
-
-

Entropy

-

{{ "%.2f"|format(file_info.entropy) }}

-
{% if file_info.pe_info %} - +
-

PE Information

-
+
+

PE Information

+ {% if file_info.pe_info.detection_notes %} + + {{ file_info.pe_info.detection_notes|length }} Detection Notes + + {% endif %} +
+ + +

File Type

{{ file_info.pe_info.file_type }}

@@ -88,79 +173,115 @@

Entry Point

{{ file_info.pe_info.entry_point }}

+ {% if file_info.pe_info.checksum_info %} +
+

PE Checksum

+

+ {% if file_info.pe_info.checksum_info.is_valid %} + Valid + {% else %} + Invalid + {% endif %} +

+
+ {% endif %}
-
- -
-

Detection Notes

- -
- - -
-

PE Sections

-
- - - - - - - - - - - {% for section in file_info.pe_info.sections %} - - - - - - - {% endfor %} - -
NameSizeEntropyNotes
- - {{ section.name }} - - {{ section.size|filesizeformat }} - - {{ "%.2f"|format(section.entropy) }} - - -
- {% for note in section.detection_notes %} -
{{ note }}
- {% endfor %} -
-
-
-
- - - {% if file_info.pe_info.suspicious_imports %} -
-

Suspicious Imports

-
- {% for import in file_info.pe_info.suspicious_imports %} -
-

{{ import.dll }}!{{ import.function }}

-

{{ import.note }}

{% endfor %}
+ {% endif %} + + {% if file_info.pe_info.checksum_info and not file_info.pe_info.checksum_info.is_valid %} + +
+

Checksum Mismatch

+
+
+

Stored Checksum

+

{{ file_info.pe_info.checksum_info.stored_checksum }}

+
+
+

Calculated Checksum

+

{{ file_info.pe_info.checksum_info.calculated_checksum }}

+
+
+
+ {% endif %} + + +
+

PE Sections

+
+ + + + + + + + + + + {% for section in file_info.pe_info.sections %} + + + + + + + {% endfor %} + +
NameSizeEntropyNotes
+ + {{ section.name }} + + {{ section.size|filesizeformat }} + + {{ "%.2f"|format(section.entropy) }} + + +
+ {% for note in section.detection_notes %} +
{{ note }}
+ {% endfor %} +
+
+
+
+ + + {% if file_info.pe_info.suspicious_imports %} +
+

Suspicious Imports

+
+ {% for dll, imports in file_info.pe_info.grouped_suspicious_imports.items() %} +
+

{{ dll }}

+
+ {% for import in imports %} +
+

{{ import.function }}

+

{{ import.note }}

+
+ {% endfor %} +
+
+ {% endfor %} +
+
+ {% endif %}
{% endif %} - {% endif %}
{% endblock %} \ No newline at end of file diff --git a/app/templates/static_analysis.html b/app/templates/static_analysis.html index 39fac6e..9dfc188 100644 --- a/app/templates/static_analysis.html +++ b/app/templates/static_analysis.html @@ -66,8 +66,21 @@ {{ yara_detections }} - - {{ yara_detections|string + ' rule matches found' if yara_detections else 'No threats detected' }} + + {% if yara_detections %} +
+ {% for match in analysis_results.yara.matches %} +
+ Rule: {{ match.rule }} + {% if match.metadata %} + (Severity: {{ match.metadata.severity }}) + {% endif %} +
+ {% endfor %} +
+ {% else %} + No threats detected + {% endif %} diff --git a/app/templates/summary.html b/app/templates/summary.html index 63c6674..6296625 100644 --- a/app/templates/summary.html +++ b/app/templates/summary.html @@ -1,3 +1,5 @@ + + {% extends "base.html" %} {% block page_title %}File Management{% endblock %} @@ -83,16 +85,19 @@ - +