Groups keyboard shortcuts have been updated
Dismiss
See shortcuts

Ensuring Secure Directory Creation in ProgramData: Race Conditions & ACLs with Inno Setup

114 views
Skip to first unread message

Tom Acker

unread,
Mar 3, 2025, 8:55:03 AMMar 3
to inno...@googlegroups.com
Hello! I am writing to request some tips on a challenge I have encountered with Inno Setup when creating directories under the ProgramData (i.e., {commonappdata}) folder and applying custom permissions (ACLs). My goal is to ensure that standard users only have read and execute permissions on these folders, thus preventing them from creating subfolders or files before the ACLs are fully applied. Issue: - During testing, I have observed that a standard user can set an opportunistic lock (oplock) on one of the folders before the permissions are finalized. Once the oplock is triggered - it is possible to create subdirectories within the folder with unintended privileges and release the lock to finalize the changes.

My current script looks like this:

[Setup] AppName=... ... [Dirs] Name: "{commonappdata}\\TEST"; Permissions: users-readexec; Name: "{commonappdata}\\TEST\\Some-Folder"; Permissions: users-readexec; ... [Files] ...

Questions: 1. Is there a supported or recommended approach in Inno Setup to create a directory with a specified ACL in a single operation, ensuring no window exists where the directory is unprotected? 2. Or maybe I should combine [Dirs] and [Code] sections to achieve this secure environment? I would greatly appreciate any insights and recommended approaches. :)

Gavin Lambert

unread,
Mar 3, 2025, 5:18:36 PMMar 3
to innosetup
On Tuesday, March 4, 2025 at 2:55:03 AM UTC+13 Tom Acker wrote:
Hello! I am writing to request some tips on a challenge I have encountered with Inno Setup when creating directories under the ProgramData (i.e., {commonappdata}) folder and applying custom permissions (ACLs). My goal is to ensure that standard users only have read and execute permissions on these folders, thus preventing them from creating subfolders or files before the ACLs are fully applied. Issue: - During testing, I have observed that a standard user can set an opportunistic lock (oplock) on one of the folders before the permissions are finalized. Once the oplock is triggered - it is possible to create subdirectories within the folder with unintended privileges and release the lock to finalize the changes.

I think the fundamental problem is your choice of base directory.  ProgramData has general write permission for all users by default, and is intended for shared writable data.

If you want to have a directory where there's no chance for a standard user to mess with it, you should consider using a folder that lacks that permission -- in particular, using a subdir of {app} (where {app} is under {pf}) is probably the best idea, especially for a directory intended to be read-only to users.  {pf} being secure is precisely why apps are supposed to install there rather than other random paths.  There is then no need to mess with [Dirs] permissions at all because the default permissions are already correct.

Jordan Russell

unread,
Mar 4, 2025, 3:59:28 AMMar 4
to inno...@googlegroups.com
On 3/3/2025 7:54 AM, Tom Acker wrote:
> Name: "{commonappdata}\\TEST"; Permissions: users-readexec

The Permissions parameter can only be used to grant additional
permissions. So "users-readexec" means "give the Users group Read &
Execute access if they didn't already have it", not "limit the Users
group to only Read & Execute access". There's currently no built-in way
to do the latter.

Securely creating a subdirectory of an insecure parent directory is
difficult to pull off. An unprivileged user could have planted a
malicious reparse point (e.g. symlink or pseudo-symlink) with the name
of the directory you're trying to create. And checking if the name
exists first won't stop a really determined attacker (it's a TOCTOU race).

Program Files is the best place to store read-only data or config files
that should only be writable by admins, because as Gavin said, the
permissions are already secure by default.

And because of the risk of reparse point attacks (potentially leading to
privilege escalation, information disclosure, or DoS), I'm of the
opinion that the ProgramData folder ({commonappdata}) should be avoided
completely. Never access it in an installer, elevated or not, and don't
access it in regular apps either unless the user explicitly requests it
(in which case it's their fault if they get exploited!).

-JR

Gavin Lambert

unread,
Mar 4, 2025, 5:22:42 PMMar 4
to innosetup
On Tuesday, March 4, 2025 at 9:59:28 PM UTC+13 Jordan Russell wrote:
And because of the risk of reparse point attacks (potentially leading to
privilege escalation, information disclosure, or DoS), I'm of the
opinion that the ProgramData folder ({commonappdata}) should be avoided
completely. Never access it in an installer, elevated or not, and don't
access it in regular apps either unless the user explicitly requests it
(in which case it's their fault if they get exploited!).

I don't think I'd go that far -- just treat it with the caution it deserves, as a local data cache shared between all users of a PC, including potentially untrustworthy ones.  But yes, that does include best avoiding it entirely in the installer or other elevated contexts, just in case.

Jordan Russell

unread,
Mar 5, 2025, 4:07:48 AMMar 5
to inno...@googlegroups.com
On 3/4/2025 4:22 PM, Gavin Lambert wrote:
> I don't think I'd go that far -- just treat it with the caution it
> deserves, as a local data cache shared between all users of a PC, including
> potentially untrustworthy ones.

While it's obvious that users can freely modify and delete files under
ProgramData and break the associated app (assuming the permissions
aren't tightened), what's not as widely known is that reparse points can
be employed there to gain access to files in other users' profiles. Also
not widely known is that even though Windows' default security policy
only allows Administrators to create symbolic links, there's another
type of link known as a "pseudo-symlink" that can be created by
unprivileged users.

For example, if an app sends email as part of its operation and is known
to append the contents of "C:\ProgramData\MyApp\signature.txt" to the
messages it sends, a user could replace that file with a
(pseudo-)symlink that points to a confidential file within another
user's profile, e.g. "C:\Users\Joe\Documents\Secret.txt". When Joe logs
in and runs the app, the contents of Secret.txt will be divulged in the
emails that are sent.

If an app *really* must read or write files in a world-writable
directory like ProgramData without the user explicitly requesting it,
steps should be taken to ensure reparse points are not followed.

The simplest mitigation may be to enable "RedirectionGuard" for the
process using the SetProcessMitigationPolicy API, which should prevent
any non-admin-created reparse points from being followed. (To do: Add an
option to enable this in Inno Setup installers.) However, that might be
unsuitable for apps that need to follow legitimate symlinks a user has
created in their own profile.

Another option is to check for the FILE_ATTRIBUTE_REPARSE_POINT
attribute on files that are opened in the insecure location
(ProgramData), and on any parent directories that aren't securely ACLed.
Also pass FILE_FLAG_OPEN_REPARSE_POINT when opening the file if
possible. This method isn't perfect, though; there's a TOCTOU problem.

I still say it's best to avoid ProgramData whenever possible. If an app
has a file that needs to be editable by any user, install it under {app}
but use "Permissions: users-modify" on the file. All users will be able
to modify the file's contents, but they shouldn't be able to swap it out
with a reparse point because they don't have write access to the
directory (assuming {app} is under Program Files). So no special
precautions are needed when opening the file.

-JR

Reply all
Reply to author
Forward
0 new messages