T07 · Tool Hijacking and Spoofing
Error
- Location
- scripts/office/soffice.py:24
- Finding
- Predictable Temporary Shared Library Enables LD_PRELOAD Tool Hijacking<![CDATA[ ## Vulnerability Details **File Location**: `scripts/office/soffice.py:24-31, 41-67` **Vulnerability Type**: Insecure predictable temporary file and dynamic-library preloading **Risk Level**: High ### Vulnerable Code ```python def get_soffice_env() -> dict: env = os.environ.copy() env["SAL_USE_VCLPLUGIN"] = "svp" if _needs_shim(): shim = _ensure_shim() env["LD_PRELOAD"] = str(shim) return env _SHIM_SO = Path(tempfile.gettempdir()) / "lo_socket_shim.so" def _needs_shim() -> bool: try: s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) s.close() return False except OSError: return True def _ensure_shim() -> Path: if _SHIM_SO.exists(): return _SHIM_SO src = Path(tempfile.gettempdir()) / "lo_socket_shim.c" src.write_text(_SHIM_SOURCE) subprocess.run( ["gcc", "-shared", "-fPIC", "-o", str(_SHIM_SO), str(src), "-ldl"], check=True, capture_output=True, ) src.unlink() return _SHIM_SO ``` ### Technical Analysis The generated shared library uses the fixed path `/tmp/lo_socket_shim.so`. If that path already exists, `_ensure_shim()` accepts it without validating: - File ownership - File permissions - Whether it is a regular file or symbolic link - Its cryptographic digest or expected contents - Whether it was produced by the current process When UNIX-domain socket creation fails, `get_soffice_env()` places this unverified file in `LD_PRELOAD`. The dynamic loader will load the library before starting LibreOffice, giving its initialization routines and intercepted functions native code execution inside the `soffice` process. The subprocess call itself does not use a shell and is not vulnerable to shell command injection. The vulnerability instead arises from trusting a predictable, cross-process temporary artifact and explicitly preloading it. ### Attack Path 1. An attacker with access to the same host or shared t ...[truncated 1252 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Create a private temporary directory for every execution: ```python with tempfile.TemporaryDirectory(prefix="lo-shim-") as temp_dir: private_dir = Path(temp_dir) private_dir.chmod(0o700) ``` 2. Generate both the C source and shared library inside that private directory. 3. Create source and output files atomically and reject symbolic links, using protections such as `O_CREAT | O_EXCL | O_NOFOLLOW`. 4. Never accept a pre-existing shared object solely because it exists. 5. Verify that temporary artifacts are regular files owned by the current effective user and are not group- or world-writable. 6. Prefer avoiding `LD_PRELOAD` entirely. If the shim is necessary, ship a reviewed binary as a protected package resource and verify its digest before use. 7. Pass the preload environment only to the exact LibreOffice subprocess that requires it. 8. Consider clearing any inherited `LD_PRELOAD` value before constructing the subprocess environment. ]]>
