LitterBox v1.6.1
This commit is contained in:
+1
-1
@@ -13,7 +13,7 @@ All notable changes to this project will be documented in this file.
|
|||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Resolved missing IOC issue in Moneta.
|
- Resolved missing IOC issue in Moneta.
|
||||||
|
- Multiple bug fixes in summary section
|
||||||
|
|
||||||
## [v1.6.0] - 2025-01-26
|
## [v1.6.0] - 2025-01-26
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
@@ -291,9 +291,6 @@ class AnalysisManager:
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Cleanup after capturing output
|
# Cleanup after capturing output
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# 6. Get RedEdr results if it was started
|
# 6. Get RedEdr results if it was started
|
||||||
if rededr:
|
if rededr:
|
||||||
|
|||||||
+13
-15
@@ -266,7 +266,7 @@ def register_routes(app):
|
|||||||
app.logger.debug(f"Extracted detection counts: {detections}")
|
app.logger.debug(f"Extracted detection counts: {detections}")
|
||||||
|
|
||||||
return render_template(
|
return render_template(
|
||||||
'dynamic_results.html',
|
'dynamic_info.html',
|
||||||
file_info=None,
|
file_info=None,
|
||||||
analysis_results=dynamic_results,
|
analysis_results=dynamic_results,
|
||||||
yara_detections=detections['yara'],
|
yara_detections=detections['yara'],
|
||||||
@@ -411,9 +411,9 @@ def register_routes(app):
|
|||||||
app.logger.error(f"Error formatting scan duration: {e}")
|
app.logger.error(f"Error formatting scan duration: {e}")
|
||||||
app.logger.debug(f"Checkplz results structure: {analysis_results.get('checkplz', {})}")
|
app.logger.debug(f"Checkplz results structure: {analysis_results.get('checkplz', {})}")
|
||||||
|
|
||||||
app.logger.debug("Rendering static_results.html template")
|
app.logger.debug("Rendering static_info.html template")
|
||||||
return render_template(
|
return render_template(
|
||||||
'static_results.html',
|
'static_info.html',
|
||||||
file_info=file_info,
|
file_info=file_info,
|
||||||
analysis_results=analysis_results,
|
analysis_results=analysis_results,
|
||||||
yara_detections=yara_detections,
|
yara_detections=yara_detections,
|
||||||
@@ -426,9 +426,9 @@ def register_routes(app):
|
|||||||
detections = utils.extract_detection_counts(analysis_results)
|
detections = utils.extract_detection_counts(analysis_results)
|
||||||
app.logger.debug(f"Extracted dynamic analysis detections: {detections}")
|
app.logger.debug(f"Extracted dynamic analysis detections: {detections}")
|
||||||
|
|
||||||
app.logger.debug("Rendering dynamic_results.html template")
|
app.logger.debug("Rendering dynamic_info.html template")
|
||||||
return render_template(
|
return render_template(
|
||||||
'dynamic_results.html',
|
'dynamic_info.html',
|
||||||
file_info=file_info,
|
file_info=file_info,
|
||||||
analysis_results=analysis_results,
|
analysis_results=analysis_results,
|
||||||
yara_detections=detections['yara'],
|
yara_detections=detections['yara'],
|
||||||
@@ -518,22 +518,21 @@ def register_routes(app):
|
|||||||
},
|
},
|
||||||
'analysis_summary': {
|
'analysis_summary': {
|
||||||
'yara': {
|
'yara': {
|
||||||
'match_count': len(yara_matches),
|
'total_findings': len(yara_matches),
|
||||||
'critical_rules': sum(1 for match in yara_matches if match.get('metadata', {}).get('severity', 0) >= 90)
|
'findings': yara_matches # Store complete YARA findings
|
||||||
},
|
},
|
||||||
'pe_sieve': {
|
'pe_sieve': {
|
||||||
'total_suspicious': pe_sieve_findings.get('total_suspicious', 0),
|
'total_findings': pe_sieve_findings.get('total_suspicious', 0),
|
||||||
'implanted': pe_sieve_findings.get('implanted', 0),
|
'findings': pe_sieve_findings # Store complete PE-sieve findings
|
||||||
'hooked': pe_sieve_findings.get('hooked', 0)
|
|
||||||
},
|
},
|
||||||
'moneta': {
|
'moneta': {
|
||||||
'abnormal_exec': moneta_findings.get('total_abnormal_private_exec', 0),
|
'total_findings': sum(1 for key, value in moneta_findings.items()
|
||||||
'unsigned_modules': moneta_findings.get('total_unsigned_modules', 0),
|
if key.startswith('total_') and isinstance(value, (int, float)) and value > 0),
|
||||||
'rwx_regions': moneta_findings.get('total_private_rwx', 0)
|
'findings': moneta_findings # Store complete Moneta findings
|
||||||
},
|
},
|
||||||
'hsb': {
|
'hsb': {
|
||||||
'total_findings': sum(len(det.get('findings', [])) for det in hsb_detections if det.get('pid') == int(pid)),
|
'total_findings': sum(len(det.get('findings', [])) for det in hsb_detections if det.get('pid') == int(pid)),
|
||||||
'max_severity': max((det.get('max_severity', 0) for det in hsb_detections if det.get('pid') == int(pid)), default=0)
|
'findings': [det for det in hsb_detections if det.get('pid') == int(pid)] # Store complete HSB findings for this PID
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -626,7 +625,6 @@ def register_routes(app):
|
|||||||
'error': str(e)
|
'error': str(e)
|
||||||
}), 500
|
}), 500
|
||||||
|
|
||||||
|
|
||||||
@app.route('/cleanup', methods=['POST'])
|
@app.route('/cleanup', methods=['POST'])
|
||||||
def cleanup():
|
def cleanup():
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -167,33 +167,42 @@
|
|||||||
<td class="px-6 py-4 text-base {{ 'text-red-500' if moneta_detections else 'text-gray-400' }}">{{ moneta_detections }}</td>
|
<td class="px-6 py-4 text-base {{ 'text-red-500' if moneta_detections else 'text-gray-400' }}">{{ moneta_detections }}</td>
|
||||||
<td class="px-6 py-4">
|
<td class="px-6 py-4">
|
||||||
{% if moneta_detections %}
|
{% if moneta_detections %}
|
||||||
{% set findings = analysis_results.moneta.findings %}
|
|
||||||
<div class="text-base text-gray-400">
|
<div class="text-base text-gray-400">
|
||||||
{% if findings.total_private_rwx > 0 %}
|
{% for key, value in analysis_results.moneta.findings.items() %}
|
||||||
<div>Private RWX: {{ findings.total_private_rwx }}</div>
|
{% if value is number and value > 0 and key != 'scan_duration' %}
|
||||||
{% endif %}
|
<div class="mb-1">
|
||||||
{% if findings.total_private_rx > 0 %}
|
{% if key == 'total_regions' %}
|
||||||
<div>Private RX: {{ findings.total_private_rx }}</div>
|
Total Regions: {{ value }}
|
||||||
{% endif %}
|
{% elif key == 'total_private_rx' %}
|
||||||
{% if findings.total_modified_code > 0 %}
|
Private RX: {{ value }}
|
||||||
<div>Modified Code: {{ findings.total_modified_code }}</div>
|
{% elif key == 'total_private_rwx' %}
|
||||||
{% endif %}
|
Private RWX: {{ value }}
|
||||||
{% if findings.total_heap_executable > 0 %}
|
{% elif key == 'total_abnormal_private_exec' %}
|
||||||
<div>Heap Executable: {{ findings.total_heap_executable }}</div>
|
Abnormal Private Executable: {{ value }}
|
||||||
{% endif %}
|
{% elif key == 'total_heap_executable' %}
|
||||||
{% if findings.total_missing_peb > 0 %}
|
Heap Executable: {{ value }}
|
||||||
<div>Missing PEB: {{ findings.total_missing_peb }}</div>
|
{% elif key == 'total_modified_code' %}
|
||||||
{% endif %}
|
Modified Code: {{ value }}
|
||||||
{% if findings.total_mismatching_peb > 0 %}
|
{% elif key == 'total_modified_pe_header' %}
|
||||||
<div>Mismatching PEB: {{ findings.total_mismatching_peb }}</div>
|
Modified PE Headers: {{ value }}
|
||||||
{% endif %}
|
{% elif key == 'total_inconsistent_x' %}
|
||||||
|
Inconsistent Execute Flags: {{ value }}
|
||||||
|
{% elif key == 'total_missing_peb' %}
|
||||||
|
Missing PEB: {{ value }}
|
||||||
|
{% elif key == 'total_mismatching_peb' %}
|
||||||
|
Mismatching PEB: {{ value }}
|
||||||
|
{% elif key == 'total_threads_non_image' %}
|
||||||
|
Threads in Non-Image Memory: {{ value }}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<span class="text-base text-gray-400">No anomalies detected</span>
|
<span class="text-base text-gray-400">No anomalies detected</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<!-- Patriot Results Row -->
|
<!-- Patriot Results Row -->
|
||||||
<tr>
|
<tr>
|
||||||
<td class="px-6 py-4 text-base text-gray-300">Patriot</td>
|
<td class="px-6 py-4 text-base text-gray-300">Patriot</td>
|
||||||
@@ -353,8 +362,8 @@
|
|||||||
</svg>
|
</svg>
|
||||||
<span class="text-base font-medium text-gray-300">Captured Output</span>
|
<span class="text-base font-medium text-gray-300">Captured Output</span>
|
||||||
</div>
|
</div>
|
||||||
<span class="text-sm px-2 py-1 rounded-full {{ 'bg-green-500/10 text-green-500' if analysis_results.process_output.stdout or analysis_results.process_output.stderr else 'bg-gray-800 text-gray-400' }}">
|
<span class="text-sm px-2 py-1 rounded-full {{ 'bg-green-500/10 text-green-500' if analysis_results.get('process_output', {}).get('stdout') or analysis_results.get('process_output', {}).get('stderr') else 'bg-gray-800 text-gray-400' }}">
|
||||||
{{ 'Output Available' if analysis_results.process_output.stdout or analysis_results.process_output.stderr else 'No Output' }}
|
{{ 'Output Available' if analysis_results.get('process_output', {}).get('stdout') or analysis_results.get('process_output', {}).get('stderr') else 'No Output' }}
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -364,37 +373,34 @@
|
|||||||
class="hidden border-t border-gray-800">
|
class="hidden border-t border-gray-800">
|
||||||
<div class="p-4 space-y-4">
|
<div class="p-4 space-y-4">
|
||||||
<!-- STDOUT Section -->
|
<!-- STDOUT Section -->
|
||||||
{% if analysis_results.process_output.stdout %}
|
{% if analysis_results.get('process_output', {}).get('stdout') %}
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<h5 class="text-sm font-medium text-gray-400">Standard Output</h5>
|
<h5 class="text-sm font-medium text-gray-400">Standard Output</h5>
|
||||||
<div class="bg-black/30 rounded p-4 font-mono text-sm">
|
<div class="bg-black/30 rounded p-4 font-mono text-sm">
|
||||||
<pre class="text-gray-300 whitespace-pre-wrap">{{ analysis_results.process_output.stdout }}</pre>
|
<pre class="text-gray-300 whitespace-pre-wrap">{{ analysis_results.get('process_output', {}).get('stdout') }}</pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<!-- STDERR Section -->
|
<!-- STDERR Section -->
|
||||||
{% if analysis_results.process_output.stderr %}
|
{% if analysis_results.get('process_output', {}).get('stderr') %}
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<h5 class="text-sm font-medium text-gray-400">Standard Error</h5>
|
<h5 class="text-sm font-medium text-gray-400">Standard Error</h5>
|
||||||
<div class="bg-black/30 rounded p-4 font-mono text-sm">
|
<div class="bg-black/30 rounded p-4 font-mono text-sm">
|
||||||
<pre class="text-red-300 whitespace-pre-wrap">{{ analysis_results.process_output.stderr }}</pre>
|
<pre class="text-red-300 whitespace-pre-wrap">{{ analysis_results.get('process_output', {}).get('stderr') }}</pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<!-- Additional Info -->
|
<!-- Additional Info -->
|
||||||
{% if analysis_results.process_output.output_truncated or analysis_results.process_output.exit_code is not none %}
|
{% if analysis_results.get('process_output', {}) %}
|
||||||
<!-- Additional Info -->
|
|
||||||
<div class="text-sm text-gray-500 italic">
|
<div class="text-sm text-gray-500 italic">
|
||||||
{% if analysis_results.process_output %}
|
{% if analysis_results.get('process_output', {}).get('output_truncated') %}
|
||||||
{% if analysis_results.process_output.output_truncated is defined and analysis_results.process_output.output_truncated %}
|
Output was truncated due to size limitations
|
||||||
Output was truncated due to size limitations
|
{% if analysis_results.get('process_output', {}).get('exit_code') is not none %} • {% endif %}
|
||||||
{% if analysis_results.process_output.exit_code is defined and analysis_results.process_output.exit_code is not none %} • {% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% if analysis_results.get('process_output', {}).get('exit_code') is not none %}
|
||||||
{% if analysis_results.process_output.exit_code is defined and analysis_results.process_output.exit_code is not none %}
|
Process exit code: {{ analysis_results.get('process_output', {}).get('exit_code') }}
|
||||||
Process exit code: {{ analysis_results.process_output.exit_code }}
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
+24
-17
@@ -770,45 +770,52 @@ class Utils:
|
|||||||
'patriot': 0,
|
'patriot': 0,
|
||||||
'hsb': 0
|
'hsb': 0
|
||||||
}
|
}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
# YARA - Get total matches
|
||||||
yara_matches = results.get('yara', {}).get('matches', [])
|
yara_matches = results.get('yara', {}).get('matches', [])
|
||||||
counts['yara'] = len({match.get('rule') for match in yara_matches if match.get('rule')}) if isinstance(yara_matches, list) else 0
|
counts['yara'] = len(yara_matches) if isinstance(yara_matches, list) else 0
|
||||||
except (TypeError, ValueError):
|
except (TypeError, ValueError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
# PE-sieve - Count all findings
|
||||||
pesieve_findings = results.get('pe_sieve', {}).get('findings', {})
|
pesieve_findings = results.get('pe_sieve', {}).get('findings', {})
|
||||||
counts['pesieve'] = int(pesieve_findings.get('total_suspicious', 0) or 0)
|
total_findings = sum(
|
||||||
|
value for key, value in pesieve_findings.items()
|
||||||
|
if isinstance(value, (int, float)) and key != 'total_scanned'
|
||||||
|
)
|
||||||
|
counts['pesieve'] = total_findings
|
||||||
except (TypeError, ValueError):
|
except (TypeError, ValueError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
# Moneta - Count all findings
|
||||||
moneta_findings = results.get('moneta', {}).get('findings', {})
|
moneta_findings = results.get('moneta', {}).get('findings', {})
|
||||||
counts['moneta'] = sum([
|
total_findings = sum(
|
||||||
int(moneta_findings.get('total_private_rwx', 0) or 0),
|
value for key, value in moneta_findings.items()
|
||||||
int(moneta_findings.get('total_private_rx', 0) or 0),
|
if isinstance(value, (int, float)) and key.startswith('total_') and key != 'total_regions'
|
||||||
int(moneta_findings.get('total_modified_code', 0) or 0),
|
)
|
||||||
int(moneta_findings.get('total_heap_executable', 0) or 0),
|
counts['moneta'] = total_findings
|
||||||
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):
|
except (TypeError, ValueError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
# Patriot - Get all findings
|
||||||
patriot_findings = results.get('patriot', {}).get('findings', {}).get('findings', [])
|
patriot_findings = results.get('patriot', {}).get('findings', {}).get('findings', [])
|
||||||
counts['patriot'] = len(patriot_findings) if isinstance(patriot_findings, list) else 0
|
counts['patriot'] = len(patriot_findings) if isinstance(patriot_findings, list) else 0
|
||||||
except (TypeError, ValueError):
|
except (TypeError, ValueError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
try:
|
try:
|
||||||
hsb_findings = results.get('hsb', {}).get('findings', {})
|
# HSB - Get all findings from all detections
|
||||||
if hsb_findings and hsb_findings.get('detections'):
|
hsb_findings = results.get('hsb', {}).get('findings', {}).get('detections', [])
|
||||||
counts['hsb'] = len(hsb_findings['detections'][0].get('findings', []))
|
total_findings = sum(
|
||||||
except (TypeError, ValueError, IndexError):
|
len(detection.get('findings', []))
|
||||||
|
for detection in hsb_findings
|
||||||
|
if isinstance(detection, dict)
|
||||||
|
)
|
||||||
|
counts['hsb'] = total_findings
|
||||||
|
except (TypeError, ValueError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
return counts
|
return counts
|
||||||
Reference in New Issue
Block a user