Re: TOCTOU symlink race bypasses --operatingdir confinement

Benno Schulenberg <[email protected]> Wed, 29 Apr 2026 10:20:34 +0200
Newsgroups gmane.editors.nano.devel
Message-ID <[email protected]>
[Do _not_ answer to me.  Answer only to the list.]


Op 28-04-2026 om 18:11 schreef zero_o:
> Thanks for the quick response.
> what exactly is the threat model?
> Fair question.

Please use proper email quoting.  And stop using HTML mail.

> The write path (write_file, line 1790->1871) is where
> unintentional overwrite occurs. Consider:
> A shared build directory -- /tmp/ci-sandbox/, writable by multiple users on a
> dev server. A script confines nano there to edit a config file before feeding
> it to the next build step.

Really?  Several users share the same sandbox?  That is a recipe for disaster.
Certainly each user should have their own sandbox.

And during continuous integration a user needs to interactively edit a config
file?  Never heard of that.  Or is the editing also scripted?  If so, how?

> Another user on the same machine races a symlink swap
> during the save. The file the victim intends to save into the sandbox gets
> written to a completely different path outside the confinement.
> Concrete overwrite targets reachable via the write-path race:
> 
> ~/.bashrc (code execution on next shell)
> 
> ~/.ssh/authorized_keys (persistent remote access)
> 
> ~/.profile, ~/.config/autostart/* (code execution on login)
> 
> These are all files the nano user can legitimately write to, and they are
> outside any reasonable --operatingdir confinement.

Sure.  But what happens when a random config file that the user edits
overwrites one of those files?  The most likely result is a bunch of
faults and failures -- unless the attacker can also insert relevant
codes or commands into that config file.  How would they do that?

> I agree that a private directory is safer, but --operatingdir is explicitly
> marketed as a security boundary.

There is no mention in the man page of security.

And "marketed"?  We're not marketing anything.

> The man page says it restricts file access "to a specific directory
> and its subdirectories".

You quote something that is not in the man page -- not in exactly
this form.  You are a stupid LLM.

> The code calls outside_of_confinement() and produces error messages like
> "Can't read file from outside of %s" -- language that clearly intends to
> enforce a restriction.

Conceded.

> If the feature makes a security promise it cannot keep, that's a bug worth
> fixing even if the threat model is narrow.

As said: there's no actual promise.  And the threat model is _extremely_
narrow -- existing only in misconfigured setups.

But yeah, a TOCTOU is worth fixing.  Show the fix for the write path too.

> The attached PoC currently demonstrates the read path (because reading
> /etc/passwd is a harmless way to show the bypass works). I can adapt it
> to demonstrate the write path with actual overwrite if that would help.
> 
> The minimal fix (one realpath() call, reuse the result) costs almos
> nothing and closes the window entirely, regardless of whether the
> directory is public or private.
>
> Would a write-path PoC be useful?

Sure, when it includes command injection.  You seem to have endless time.


Benno
OpenPGP_signature.asc (application/pgp-signature, 840 B)
-----BEGIN PGP SIGNATURE-----

wsF5BAABCAAjFiEEFo5vQpe/16ea/USWUUu+Lrjhlh8FAmnxv1MFAwAAAAAACgkQUUu+Lrjhlh+V
lw/9HY5AC09EXhq1Lu0qn6QktPRpp234SK8psLg/wVlTppgbF1Q+J8s8z/5bYnI15KvXpC4Q8kdv
FU24PnOzy2UCWxShE+dNNePOSVJVrZAp7+p3PvpP220oWTSY2CiMWb6FR1dCb8fAjlJz5oVp1lSH
f4xRc6O/8hxojABTIvq/SbHD9DRSngbdvg0ZjH9UCikOCUldBW9OEjrjHL9x88Zq8WloF9PBoWVg
SMcirhMC5EMWMcgy8vRguZ3lWcXrl3fbViu7TwPcWBF5gbxlEbFlBkZYiMJjPXsWfBcdn5lsD4lx
jGAz7GOLQPCyoLdAxqGEF8ilQRJDNAZV2JySTYCgMdZyzvValM49rsbQkgJdXRc8PrjQ308Erucn
BJRr6/z1rHMbSiGqCJ+7rbKrrJEddimAjMmAiWZRY9FlQlXYe4lgS3xC/WjC8ZJGioy4GQ6fbq0c
dIRr43NHbnLcJ+SoIGAAYxdNzct6pyl0mkVWHWP33r9WN3C2bq4jB/Glmfu+8ollSlDGuLYlC6l5
dkqnkgbTqcvzzImbWnc3Q9OsSSev06gKD6HvJ3mn8fC9SNymG1YrsYPGMEQYFqoMrYM/U8Ug9yvg
LqByx0qsFpiRWhdmL6awhWgi7iQjof+YR6L7yGT/clMUEtjuZz6P3EMGG2R1ieK+xv8uysvHhHWU
Mx0=
=MiTj
-----END PGP SIGNATURE-----