T07 · Tool Hijacking and Spoofing
Error
- Location
- scripts/office/soffice.py:24
- Finding
- Predictable Shared Library Path Enables LD_PRELOAD Hijacking<![CDATA[ ## Vulnerability Details **File Location**: `scripts/office/soffice.py:24-31, 41-64` **Vulnerability Type**: Unsafe temporary file reuse and dynamic-loader hijacking **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 _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 helper stores its native shim at the predictable shared path `/tmp/lo_socket_shim.so`. If that path already exists, `_ensure_shim()` accepts it without checking its owner, permissions, file type, content, or cryptographic digest. `get_soffice_env()` subsequently places the accepted path in `LD_PRELOAD`. The dynamic loader therefore loads the shared object into the LibreOffice process before normal libraries. A malicious shared object can use a constructor function to execute arbitrary native code immediately when LibreOffice starts. The same predictable path also creates a time-of-check/time-of-use risk. Even if the legitimate script initially determines that the file does not exist, another local process may attempt to replace or redirect the output while compilation is occurring. The predictable C source path is similarly unsafe, although compromise of the resulting shared object is the primary risk. ### Attack Path 1. The attacker has access as another local user or through another process sharing the same temporary directory. 2. Before the Skill invokes LibreOffice, the attacker cre ...[truncated 1016 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions - Create a private temporary directory with permissions limited to the current user, such as mode `0700`. - Generate both the C source and compiled library inside that private directory using unpredictable names. - Create files atomically and reject symbolic links. - Do not reuse an existing shared object merely because it exists. - If caching is necessary, verify the file owner, mode, regular-file status, expected hash, and containing-directory permissions before use. - Compile to a temporary output and atomically rename it only after successful compilation. - Remove the library after the LibreOffice process exits. - Prefer redesigning the integration so that `LD_PRELOAD` is unnecessary. - Invoke the compiler by a trusted absolute path or execute it in a tightly controlled environment. A safer structure is: ```python with tempfile.TemporaryDirectory(prefix="lo-shim-") as temp_dir: private_dir = Path(temp_dir) private_dir.chmod(0o700) src = private_dir / "shim.c" shim = private_dir / "shim.so" src.write_text(_SHIM_SOURCE, encoding="utf-8") subprocess.run( ["/usr/bin/gcc", "-shared", "-fPIC", "-o", str(shim), str(src), "-ldl"], check=True, capture_output=True, ) env["LD_PRELOAD"] = str(shim) # Start and wait for LibreOffice before leaving this context. ``` ]]>
