[Bug 49334] onefile applications created with PyInstaller 3.6 fail, reporting 'INTERNAL ERROR: cannot create temporary directory' (improper handling of owner rights SID '{S-1-3-4}')

WineHQ Bugzilla wine-bugs at winehq.org
Tue Jun 9 06:52:46 CDT 2020


https://bugs.winehq.org/show_bug.cgi?id=49334

Anastasius Focht <focht at gmx.net> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
          Component|-unknown                    |wineserver
           Severity|major                       |normal
           Keywords|                            |download, source
     Ever confirmed|0                           |1
                URL|                            |https://github.com/pyinstal
                   |                            |ler/pyinstaller
                 CC|                            |focht at gmx.net
            Summary|Binaries compiled with      |onefile applications
                   |PyInstaller on Wine crash   |created with PyInstaller
                   |with error, affects         |3.6 fail, reporting
                   |Anaconda installation, too. |'INTERNAL ERROR: cannot
                   |                            |create temporary directory'
                   |                            |(improper handling of owner
                   |                            |rights SID '{S-1-3-4}')
             Status|UNCONFIRMED                 |NEW
            Version|5.0.1                       |5.0

--- Comment #1 from Anastasius Focht <focht at gmx.net> ---
Hello folks,

confirming.

The upstream change that caused this breakage is here:

https://github.com/pyinstaller/pyinstaller/commit/42a67148b3bdf9211fda8499fdc5b63acdd7e6cc
(master branch, included in 3.6 release)

--- quote ---
 [SECURITY] Bootloader: Fix insecure directory permissions of sys._MEI…

…PATH.

Important: This security fix effects *all Windows software frozen by
PyInstaller* in "onefile" mode.

This fix prevents a Local Privilege Escalation vulnerability that is
present only on Windows and in this particular case:
If a software frozen by PyInstaller in "onefile" mode is launched
by a (privileged) user who has his/her "TempPath" resolving to a world
writable directory. This is the case e.g. if the software is
launched as a service or as a scheduled task using a system account
(in which case TempPath will default to `C:\Windows\Temp`).

All Windows version have been vulnerable, since _wmkdir() does not
enforce restricted permissions. On Posix-systems mkdtemp() is used,
which already enforce permissions, so they have not been affected.

The fix is done by implementing a new pyi_win32_mkdir() that enforces
proper permissions for the created directory.

While PyInstaller itself was not vulnerable, all Windows software frozen
by PyInstaller in "onefile" mode is vulnerable. If you are using
PyInstaller to freeze Windows software using "onefile" mode, you should
upgrade PyInstaller and rebuild your software.

Fixes CVE-2019-16784.

Co-authored-by: Hartmut Goebel <h.goebel at crazy-compilers.com>
--- quote ---

The code:

--- snip ---
/* Create a directory at path with restricted permissions.
 *  The directory owner will be the only one with permissions on the created
 *  dir. Calling this function is equivalent to callin chmod(path, 0700) on
 *  Posix.
 *  Returns 0 on success, -1 on error.
 */
int
pyi_win32_mkdir(const wchar_t *path)
{
    wchar_t stringSecurityDesc[] = // ACE String :
        L"D:" // DACL (D) :
        L"(A;" // Authorize (A)
        L";FA;" // FILE_ALL_ACCESS (FA)
        L";;S-1-3-4)"; // For the current directory owner (SID: S-1-3-4)
        // no other permissions are granted

    SECURITY_ATTRIBUTES securityAttr;
    PSECURITY_DESCRIPTOR *lpSecurityDesc;
    securityAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
    securityAttr.bInheritHandle = FALSE;
    lpSecurityDesc = &securityAttr.lpSecurityDescriptor;

    if (!ConvertStringSecurityDescriptorToSecurityDescriptorW(
             stringSecurityDesc,
             SDDL_REVISION_1,
             lpSecurityDesc,
             NULL)) {
        return -1;
    }
    if (!CreateDirectoryW(path, &securityAttr)) {
        return -1;
    };
    return 0;
}
--- snip ---

Recipe for reproduction:

https://github.com/pyinstaller/pyinstaller/issues/4628#issuecomment-586806948

--- snip ---
$ wget https://www.python.org/ftp/python/3.8.1/python-3.8.1-amd64.exe
$ wine ./python-3.8.1-amd64.exe /passive
$ wine py -m pip install --upgrade pip
$ wine py -m pip install pyinstaller

$ wget https://github.com/pinobatch/lorom-template/raw/master/tools/wav2brr.py
$ wine \path\to\pyinstaller.exe --onefile -d bootloader wav2brr.py
--- snip ---

Relevant part of trace log running the onefile app:

--- snip ---
$ WINEDEBUG=+seh,+relay,+server,+advapi,+security wine ./wav2brr.exe >>log.txt
2>&1
...
00c0:Call
advapi32.ConvertStringSecurityDescriptorToSecurityDescriptorW(002198b8
L"D:(A;;FA;;;S-1-3-4)",00000001,002198a8,00000000) ret=140005964
00c0:Call sechost.ConvertStringSecurityDescriptorToSecurityDescriptorW(002198b8
L"D:(A;;FA;;;S-1-3-4)",00000001,002198a8,00000000) ret=7bca06ef 
...
00c0:trace:security:parse_acl L"(A;;FA;;;S-1-3-4)" 
...
00c0:Ret  sechost.ConvertStringSecurityDescriptorToSecurityDescriptorW()
retval=00000001 ret=7bca06ef
00c0:Ret  advapi32.ConvertStringSecurityDescriptorToSecurityDescriptorW()
retval=00000001 ret=140005964
00c0:Call KERNEL32.CreateDirectoryW(0011f710
L"C:\\users\\focht\\Temp\\_MEI1882",002198a0) ret=14000598c
00c0:Call kernelbase.CreateDirectoryW(0011f710
L"C:\\users\\focht\\Temp\\_MEI1882",002198a0) ret=7bca06ef
00c0:Call ntdll.RtlDosPathNameToNtPathName_U(0011f710
L"C:\\users\\focht\\Temp\\_MEI1882",00219728,00000000,00000000) ret=7b01447e
00c0:Ret  ntdll.RtlDosPathNameToNtPathName_U() retval=00000001 ret=7b01447e
00c0:Call
ntdll.NtCreateFile(00219720,80100000,00219738,00219768,00000000,00000080,00000001,00000002,00000021,00000000,00000000)
ret=7b014536
00c0: create_file( access=80100000, sharing=00000001, create=2,
options=00000021, attrs=00000080,
objattr={rootdir=0000,attributes=00000040,sd={control=00000004,owner=<not
present>,group=<not
present>,sacl={},dacl={{AceType=ACCESS_ALLOWED_ACE_TYPE,Mask=1f01ff,AceFlags=0,Sid={S-1-3-4}}}},name=L""},
filename="/home/focht/.wine/dosdevices/c:/users/focht/Temp/_MEI1882" )
00c0: create_file() = ACCESS_DENIED { handle=0000 }
00c0:Ret  ntdll.NtCreateFile() retval=c0000022 ret=7b014536
...
00c0:Call ntdll.RtlNtStatusToDosError(c0000022) ret=7b01454f
00c0:Ret  ntdll.RtlNtStatusToDosError() retval=00000005 ret=7b01454f
00c0:Ret  kernelbase.CreateDirectoryW() retval=00000000 ret=7bca06ef
00c0:Ret  KERNEL32.CreateDirectoryW() retval=00000000 ret=14000598c 
--- snip ---

No SD owner is present so the owner derived from the process token. This
obviously can't work together with specified owner rights SID '{S-1-3-4}'.

sid = '{S-1-3-4}' (from SD)
user = '{S-1-5-21-0-0-0-1000}' (derived from process token)
owner = '{S-1-5-21-0-0-0-1000}' (derived from process token)

https://source.winehq.org/git/wine.git/blob/HEAD:/server/file.c#l533

--- snip ---
...
 533                 case ACCESS_ALLOWED_ACE_TYPE:
 534                     aa_ace = (const ACCESS_ALLOWED_ACE *)ace;
 535                     sid = (const SID *)&aa_ace->SidStart;
 536                     mode = file_access_to_mode( aa_ace->Mask );
 537                     if (security_equal_sid( sid, security_world_sid ))
 538                     {
 539                         mode = (mode << 6) | (mode << 3) | mode;  /* all
*/
 540                         new_mode |= mode & bits_to_set;
 541                         bits_to_set &= ~mode;
 542                     }
 543                     else if ((security_equal_sid( user, owner ) &&
 544                               token_sid_present( current->process->token,
sid, FALSE )))
 545                     {
 546                         mode = (mode << 6) | (mode << 3);  /* user + group
*/
 547                         new_mode |= mode & bits_to_set;
 548                         bits_to_set &= ~mode;
 549                     }
 550                     else if (security_equal_sid( sid, owner ))
 551                     {
 552                         mode = (mode << 6);  /* user only */
 553                         new_mode |= mode & bits_to_set;
 554                         bits_to_set &= ~mode;
 555                     }
 556                     break;
...
--- snip ---

---

I've changed the reported version field back to Wine 5.0 because that's the
earliest version the people reported the problem with. See referenced links to
Github PRs.

$ wine --version
wine-5.10-39-g1752958240

Regards

-- 
Do not reply to this email, post in Bugzilla using the
above URL to reply.
You are receiving this mail because:
You are watching all bug changes.


More information about the wine-bugs mailing list