lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Date: Wed, 17 Oct 2007 18:43:12 +0200
From: "KJK::Hyperion" <hackbunny@...tpj.org>
To: full-disclosure@...ts.grok.org.uk
Cc: bugtraq@...urityfocus.com
Subject: Re: URI handling woes in Acrobat Reader, Netscape,
 Miranda, Skype

KJK::Hyperion ha scritto:
> ShellExecute is not called ExecuteUri [...]

This function isn't, either, but it should be close enough:

/* --- 8< ------ 8< -SNIP- 8< ------ 8< ------ 8< -SNIP- 8< --- */

/*
 * Helper functions to unambiguously execute URLs with ShellExecute(Ex).
 * Author: KJK::Hyperion <hackbunny@...tpj.org>
 */

/*
 * Copyright (c) 2007 KJK::Hyperion
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#define WIN32_LEAN_AND_MEAN
#include <windows.h>

#include <shellapi.h>
#include <shlwapi.h>
#include <wininet.h>

#include <strsafe.h>

BOOL ShellExecuteURLExInternal(LPSHELLEXECUTEINFO lpExecInfo)
{
    BOOL bRet;
    DWORD dwErr;
    HRESULT hr;
    PARSEDURL pu;
    TCHAR szSchemeBuffer[INTERNET_MAX_SCHEME_LENGTH + 1];
    HKEY hkeyClass;

    /* Default error codes */
    bRet = FALSE;
    dwErr = ERROR_INVALID_PARAMETER;
    hr = S_OK;

    lpExecInfo->hInstApp =
        (HINSTANCE)UlongToHandle(SE_ERR_ACCESSDENIED);

    /* Validate parameters */
    if
    (
        lpExecInfo->cbSize == sizeof(*lpExecInfo) &&
        lpExecInfo->lpFile != NULL &&
        (lpExecInfo->fMask & SEE_MASK_INVOKEIDLIST) == 0 &&
        (lpExecInfo->fMask & SEE_MASK_CLASSNAME) == 0 &&
        (lpExecInfo->fMask & 0x00400000) == 0 /* SEE_MASK_FILEANDURL */
    )
    {
        /* Extract the scheme out of the URL */
        pu.cbSize = sizeof(pu);
        hr = ParseURL(lpExecInfo->lpFile, &pu);

        /* Is the URL really, unambiguously an URL? */
        if
        (
            SUCCEEDED(hr) &&
            pu.pszProtocol == lpExecInfo->lpFile &&
            pu.pszProtocol[pu.cchProtocol] == TEXT(':')
        )
        {
            /* We need the scheme name NUL-terminated, so we copy it */
            hr = StringCbCopyN
            (
                szSchemeBuffer,
                sizeof(szSchemeBuffer),
                pu.pszProtocol,
                pu.cchProtocol * sizeof(TCHAR)
            );

            if(SUCCEEDED(hr))
            {
                /* Is the URL scheme a registered ProgId? */
                hr = AssocQueryKey
                (
                    ASSOCF_INIT_IGNOREUNKNOWN,
                    ASSOCKEY_CLASS,
                    szSchemeBuffer,
                    NULL,
                    &hkeyClass
                );

                if(SUCCEEDED(hr))
                {
                    /* Is the ProgId really an URL scheme? */
                    dwErr = RegQueryValueEx
                    (
                        hkeyClass,
                        TEXT("URL Protocol"),
                        NULL,
                        NULL,
                        NULL,
                        NULL
                    );

                    /* All clear! */
                    if(dwErr == NO_ERROR || dwErr == ERROR_MORE_DATA)
                    {
                        /* Don't let ShellExecuteEx guess */
                        lpExecInfo->fMask |= SEE_MASK_CLASSKEY;
                        lpExecInfo->lpClass = NULL;
                        lpExecInfo->hkeyClass = hkeyClass;

                        /* Finally, execute the damn URL */
                        bRet = ShellExecuteEx(lpExecInfo);

                        /* To preserve ShellExecuteEx's last error */
                        dwErr = NO_ERROR;
                    }

                    RegCloseKey(hkeyClass);
                }
            }
        }
    }

    /* Last error was a HRESULT */
    if(FAILED(hr))
    {
        /* Try to dissect it */
        if(HRESULT_FACILITY(hr) == FACILITY_WIN32)
            dwErr = HRESULT_CODE(hr);
        else
            dwErr = hr;
    }

    /* We have a last error to set */
    if(dwErr)
        SetLastError(dwErr);

    return bRet;
}

BOOL ShellExecuteURLEx(LPSHELLEXECUTEINFO lpExecInfo)
{
    BOOL bRet;
    SHELLEXECUTEINFO ExecInfo;

    /* We use a copy of the parameters, because you never know */
    CopyMemory(&ExecInfo, lpExecInfo, sizeof(ExecInfo));

    /* Do the magic */
    bRet = ShellExecuteURLExInternal(&ExecInfo);

    /* These need to be copied back */
    lpExecInfo->hInstApp = ExecInfo.hInstApp;
    lpExecInfo->hProcess = ExecInfo.hProcess;
    return bRet;
}

HINSTANCE ShellExecuteURL
(
    HWND hwnd,
    LPCTSTR lpOperation,
    LPCTSTR lpFile,
    LPCTSTR lpParameters,
    LPCTSTR lpDirectory,
    INT nShowCmd
)
{
    SHELLEXECUTEINFO ExecuteInfo;

    ExecuteInfo.fMask = SEE_MASK_FLAG_NO_UI; /* Odd but true */
    ExecuteInfo.hwnd = hwnd;
    ExecuteInfo.cbSize = sizeof(ExecuteInfo);
    ExecuteInfo.lpVerb = lpOperation;
    ExecuteInfo.lpFile = lpFile;
    ExecuteInfo.lpParameters = lpParameters;
    ExecuteInfo.lpDirectory = lpDirectory;
    ExecuteInfo.nShow = nShowCmd;

    ShellExecuteURLExInternal(&ExecuteInfo);

    return ExecuteInfo.hInstApp;
}

/* --- >8 -SNIP- >8 ------ >8 ------ >8 -SNIP- >8 ------ >8 --- */

Developers: when you know you are going to execute an URL, consider
using these safe wrappers instead of calling ShellExecute(Ex) directly

In case you are curious, they work by looking up the program associated
to the URL scheme (and verifying that the scheme is actually registered
as such) before calling ShellExecuteEx. This prevents ShellExecuteEx
from guessing (wrong), and causes any URL, however broken and mangled,
to be passed verbatim to the application that registered to handle it.
Using the original PoC as an example, using ShellExecuteURLEx instead of
ShellExecuteEx will cause your e-mail program to start composing a mail
addressed to Mr. "test%../../../../windows/system32/calc.exe.cmd" (or
"test%../../../../windows/system32/calc.exe".cmd", depending on the
program. But I think you get the idea)

This code will not solve all issues, but it will prevent any dangerous
treatment of ambiguous or invalid URLs, and it will ultimately shift the
blame for any input validation failure to the external application that
receives the URL

Even if you won't use my code directly, it will, hopefully, help you
understand what happens inside ShellExecuteEx

_______________________________________________
Full-Disclosure - We believe in it.
Charter: http://lists.grok.org.uk/full-disclosure-charter.html
Hosted and sponsored by Secunia - http://secunia.com/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ