CVE-2020-15145: Incorrect Default Permissions (CWE-276)
value: In Composer-Setup for Windows before version 6.0.0, if the developer's computer is shared with other users, a local attacker may be able to exploit the following scenarios. 1. A local regular user may modify the existing `C:\ProgramData\ComposerSetup\bin\composer.bat` in order to get elevated command execution when composer is run by an administrator. 2. A local regular user may create a specially crafted dll in the `C:\ProgramData\ComposerSetup\bin` folder in order to get Local System privileges. See: https://itm4n.github.io/windows-server-netman-dll-hijacking. 3. If the directory of the php.exe selected by the user is not in the system path, it is added without checking that it is admin secured, as per Microsoft guidelines. See: https://msrc-blog.microsoft.com/2018/04/04/triaging-a-dll-planting-vulnerability.
CWE description: During installation, installed file permissions are set to allow anyone to modify those files.
CVSS v3 base score: 8.2
Published: 2020-08-14T17:15Z
Repository: https://github.com/composer/windows-setup
Language: Other
Files changed: src/composer.iss, src/scripts/acls.bat
Commit date: 2020-08-06 12:29:14 +0100
Commit message: Lock down bin folder for admin installs

Diff: diff --git a/src/composer.iss b/src/composer.iss
index 4020411..ed89ef9 100644
--- a/src/composer.iss
+++ b/src/composer.iss
@@ -10,12 +10,13 @@
 #define AppYear GetDateTimeString('yyyy', '', '');
 
 #define CmdPhp "php.exe"
 #define CmdBat "composer.bat"
 #define CmdShell "composer"
-#define RunPhp "runphp.exe"
 #define DllData "userdata.dll"
+#define PermsBat "acls.bat"
+#define RunPhp "runphp.exe"
 
 #define PhpCheck "check.php"
 #define PhpIniCheck "inicheck.php"
 #define PhpInstaller "installer.php"
 
@@ -86,25 +87,27 @@ WizardSizePercent=110,100
     OutputDir=..\builds\output
   #endif
   OutputBaseFilename=Composer-Setup.dev
 #endif
 
+
 [LangOptions]
 DialogFontSize=10
 
 
-[Dirs]
-; we need to make all-users directory writeable so composer.phar can update
-Name: {code:GetBinDir}; Permissions: users-modify; Check: CheckPermisions;
+; remove the directory so existing permissions are not preserved
+[InstallDelete]
+Type: filesandordirs; Name: {code:GetBinDir}; Check: CheckPermisions;
 
 
 [Files]
 ; files to extract must be first
 Source: php\{#PhpCheck}; Flags: dontcopy;
 Source: php\{#PhpInstaller}; Flags: dontcopy;
 Source: php\{#PhpIniCheck}; Flags: dontcopy;
 Source: runphp\{#RunPhp}; Flags: dontcopy signonce;
+Source: scripts\{#PermsBat}; Flags: dontcopy;
 Source: shims\{#CmdShell}; Flags: dontcopy;
 
 ; app files
 Source: userdata\{#DllData}; DestDir: "{app}"; Flags: ignoreversion signonce; Check: IncludeUninstaller;
 
@@ -119,10 +122,11 @@ Source: {tmp}\composer.phar; DestDir: {code:GetBinDir}; Flags: external ignoreve
 [UninstallDelete]
 Type: filesandordirs; Name: {code:GetBinDir};
 
 
 [Run]
+Filename: {tmp}\{#PermsBat}; Flags: runhidden; Check: CheckPermisions;
 Filename: "https://{#AppUrl}"; Description: "View online documentation"; Flags: postinstall shellexec unchecked;
 
 
 [Messages]
 SetupWindowTitle=%1 Setup
@@ -355,10 +359,11 @@ const
   SEP_PATH = ';';
   LF = #13#10;
   LF2 = LF + LF;
   TAB = #32#32#32#32#32#32;
 
+  PERMS_BAT = '{#PermsBat}';
   RUN_PHP = '{#RunPhp}';
   PHP_CHECK = '{#PhpCheck}';
   PHP_CHECK_ID = '{#PHP_CHECK_ID}';
   PHP_INICHECK = '{#PhpIniCheck}';
   PHP_INSTALLER = '{#PhpInstaller}';
@@ -619,10 +624,11 @@ begin
 
   GCmdExe := ExpandConstant('{cmd}');
   GTmpDir := RemoveBackslash(ExpandConstant('{tmp}'));
 
   {Extract our temp files to installer directory}
+  ExtractTemporaryFile(PERMS_BAT);
   ExtractTemporaryFile(RUN_PHP);
   ExtractTemporaryFile(PHP_CHECK);
   ExtractTemporaryFile(PHP_INICHECK);
   ExtractTemporaryFile(PHP_INSTALLER);
   ExtractTemporaryFile(CMD_SHELL);
@@ -1689,11 +1695,14 @@ var
 begin
 
   Current := (StrToVer(VerCurrent) shr 24) and $ff;
   Existing := (StrToVer(VerExisting) shr 24) and $ff;
 
-  if (Current = 5) and (Existing = 4) then
+  if (Current = 6) and (Existing = 5) then
+    {Version 6 locks down admin bin directory}
+    Result := False
+  else if (Current = 5) and (Existing = 4) then
     {Version 5 only drops support for XP}
     Result := False
   else
     Result := Current <> Existing;
 
@@ -6053,11 +6062,11 @@ begin
   case GPaths.Php.Status of
     PATH_OK:
     begin
 
       if SameText(GSettingsPage.Combo.Text, GPaths.Php.Data.Cmd) then
-        Caption := 'This is the PHP in your path. Click Next to use it.'
+        Info := 'This is the PHP in your path. Click Next to use it.'
       else
       begin
         Info := 'This will replace the PHP entry in your path. Click Next if you want to do this.';
         {Fix resizing issue by making the caption word wrap}
         Info := Info + StringOfChar(#32, 20);
@@ -6067,11 +6076,11 @@ begin
     end;
 
     PATH_NONE:
     begin
 
-      if (GSettingsPage.Combo.Text <> '') then
+      if NotEmpty(GSettingsPage.Combo.Text) then
       begin
         Info := 'This will add PHP to your path. Click Next to use it.';
         Confirm := 'Add this PHP to your path?';
       end;
 
@@ -6160,11 +6169,11 @@ begin
   GSettingsPage.Warning.Height := ScaleY(30);
   GSettingsPage.Warning.Anchors := [akLeft, akTop, akRight];
   GSettingsPage.Warning.AutoSize := True;
   GSettingsPage.Warning.WordWrap := True;
 
-  S := 'Please confirm to continue. The security of this location is your responsibility';
+  S := 'Confirm to continue. The access control of this location is your responsibility';
   S := S + ' and is particularly important if other people use this computer.';
 
   GSettingsPage.Warning.Caption := S;
   GSettingsPage.Warning.Visible := False;
 
diff --git a/src/scripts/acls.bat b/src/scripts/acls.bat
new file mode 100644
index 0000000..b491b3c
--- /dev/null
+++ b/src/scripts/acls.bat
@@ -0,0 +1,13 @@
+@echo off
+
+:: remove inheritance
+icacls %ALLUSERSPROFILE%\ComposerSetup /inheritance:d /Q
+
+:: remove creator owner
+icacls %ALLUSERSPROFILE%\ComposerSetup /remove:g *S-1-3-0 /Q
+
+:: remove builtin\users
+icacls %ALLUSERSPROFILE%\ComposerSetup /remove:g *S-1-5-32-545 /Q
+
+:: grant read-execute to builtin\users 
+icacls %ALLUSERSPROFILE%\ComposerSetup /grant *S-1-5-32-545:(OI)(CI)(RX) /Q

Vulnerable code: #define RunPhp "runphp.exe"
[Dirs]
; we need to make all-users directory writeable so composer.phar can update
Name: {code:GetBinDir}; Permissions: users-modify; Check: CheckPermisions;
  if (Current = 5) and (Existing = 4) then
        Caption := 'This is the PHP in your path. Click Next to use it.'
      if (GSettingsPage.Combo.Text <> '') then
  S := 'Please confirm to continue. The security of this location is your responsibility';
Fixed code: #define PermsBat "acls.bat"
#define RunPhp "runphp.exe"

; remove the directory so existing permissions are not preserved
[InstallDelete]
Type: filesandordirs; Name: {code:GetBinDir}; Check: CheckPermisions;
Source: scripts\{#PermsBat}; Flags: dontcopy;
Filename: {tmp}\{#PermsBat}; Flags: runhidden; Check: CheckPermisions;
  PERMS_BAT = '{#PermsBat}';
  ExtractTemporaryFile(PERMS_BAT);
  if (Current = 6) and (Existing = 5) then
    {Version 6 locks down admin bin directory}
    Result := False
  else if (Current = 5) and (Existing = 4) then
        Info := 'This is the PHP in your path. Click Next to use it.'
      if NotEmpty(GSettingsPage.Combo.Text) then
  S := 'Confirm to continue. The access control of this location is your responsibility';
@echo off

:: remove inheritance
icacls %ALLUSERSPROFILE%\ComposerSetup /inheritance:d /Q

:: remove creator owner
icacls %ALLUSERSPROFILE%\ComposerSetup /remove:g *S-1-3-0 /Q

:: remove builtin\users
icacls %ALLUSERSPROFILE%\ComposerSetup /remove:g *S-1-5-32-545 /Q

:: grant read-execute to builtin\users 
icacls %ALLUSERSPROFILE%\ComposerSetup /grant *S-1-5-32-545:(OI)(CI)(RX) /Q