| # PoC β arbitrary local file read when loading a NetCDF (.nc) file |
|
|
| A `.nc` file can make `netCDF4`/`xarray` read an arbitrary file off the machine that |
| opens it. NetCDF-4 is HDF5 underneath, and HDF5 lets a variable's raw data live in an |
| *external* file (`H5Pset_external`). libnetcdf follows that reference silently, so when |
| you open the file and read the variable, you get back the bytes of whatever file the |
| author pointed at β including absolute paths well outside the working directory. |
|
|
| Nothing here is malicious to run; the "secret" is a marker string I put next to the file. |
|
|
| ## Files |
| - `poc_secret.nc` β variable `leak` is backed by the relative path `secret.txt` (shipped here). Works on any OS. |
| - `secret.txt` β the stand-in secret so PoC 1 is self-contained. |
| - `poc_passwd.nc` β variable `leak` is backed by the absolute path `/etc/passwd`. On a Linux box this reads the real passwd file. |
| - `verify.py` β opens a `.nc` and prints the `leak` variable's bytes. |
| - `build_poc_hf.py` β how the two `.nc` files were built (with `h5py`). |
|
|
| ## Reproduce |
| ``` |
| pip install netCDF4 # or: pip install xarray netCDF4 |
| python verify.py poc_secret.nc # prints the contents of secret.txt |
| python verify.py poc_passwd.nc # on Linux, prints /etc/passwd |
| ``` |
|
|
| Or with xarray: |
| ```python |
| import xarray as xr |
| print(bytes(xr.open_dataset("poc_passwd.nc")["leak"].values.tobytes())) |
| ``` |
|
|
| ## Why it matters |
| Anywhere a service ingests a user-supplied `.nc` (model weights, scientific datasets, |
| data-conversion pipelines) and later reads the variable β or re-saves it β the targeted |
| file's bytes are pulled in and can end up in the output. It's a pure default-path read; |
| there's no safe-mode flag in `netCDF4`/`xarray` that turns external references off. |
|
|