T09 · Insecure Skill Coding Practices
- Location
scripts/waterfall.py:410- Finding
Predictable Shared Temporary File Enables Symlink-Based File Overwrite
- Content
View full analysis
Vulnerability Details
File Location:
scripts/waterfall.py, lines 410–438
Vulnerability Type: Predictable and insecure temporary file handling
Risk Level: MediumVulnerable Code
python n_samples = int(sample_rate * args.duration) iq_file = '/tmp/iq_waterfall_capture.raw' print(f"Capturing IQ: center={center_freq/1e6:.1f} MHz, " f"rate={sample_rate/1e6:.1f} MSPS, " f"duration={args.duration}s") result = subprocess.run( ['hackrf_transfer', '-f', str(int(center_freq)), '-s', str(int(sample_rate)), '-l', str(args.lna), '-g', str(args.vga), '-n', str(n_samples), '-r', iq_file], capture_output=True, text=True, timeout=300) if result.returncode != 0: print(f"ERROR: hackrf_transfer failed: {result.stderr}") sys.exit(1) freq = center_freq rate = sample_rate # ... if not args.input and iq_file.startswith('/tmp/'): try: os.remove(iq_file) except OSError: passTechnical Analysis
The script stores automatically captured IQ data at the fixed, globally predictable path
/tmp/iq_waterfall_capture.raw. It neither creates a private temporary directory nor safely reserves and validates the output file before passing its path tohackrf_transfer.On a multi-user system, another local user can pre-create this path as a symbolic link to a file writable by the victim. If
hackrf_transferfollows the link when opening its output, the linked target may be overwritten or corrupted with IQ data. The same fixed path also creates race conditions between concurrent instances: one process can overwrite, consume, or delete another process's capture.Arguments are passed to
subprocess.runas a list withoutshell=True, so this is not command injection. The issue is specifically unsafe temporary-file handling and a time-of-check/time-of-use exposure involving an external writer.Attack Path
- A local attacker determines that the script always uses `/tmp/iq_waterfall_capture.r ...[truncated 1422 chars]
- Remediation
View remediation
Remediation Suggestions
- Create a unique, mode-
0700temporary directory usingtempfile.TemporaryDirectory()and place the capture inside it. - Keep the temporary-directory object alive until capture processing is complete, and use a
try/finallyblock or context manager to guarantee cleanup. - Avoid a shared deterministic filename in
/tmp. - Before invoking the external writer, verify that the parent directory is private and that the destination is not a symbolic link.
- If supported by
hackrf_transfer, write through an already-secured file descriptor or use an option that refuses to follow symbolic links. - Prevent concurrent runs from sharing output state.
Example hardening pattern:
python import tempfile from pathlib import Path with tempfile.TemporaryDirectory(prefix='hackrf-waterfall-') as temp_dir: iq_file = str(Path(temp_dir) / 'capture.raw') result = subprocess.run( ['hackrf_transfer', '-f', str(int(center_freq)), '-s', str(int(sample_rate)), '-l', str(args.lna), '-g', str(args.vga), '-n', str(n_samples), '-r', iq_file], capture_output=True, text=True, timeout=300, check=False, ) if result.returncode != 0: raise RuntimeError( f"hackrf_transfer failed: {result.stderr}" ) generate_iq_waterfall( iq_file, center_freq, sample_rate, args.outdir, title, fft_size=args.fft, avg_factor=args.avg )- Create a unique, mode-
