Implementing SandBox Support

Having problems with building or using CEF's C/C++ APIs? This forum is here to help. Please do not post bug reports or feature requests here.

Implementing SandBox Support

Postby amaitland » Mon Jan 11, 2016 3:36 am

Any thoughts on if it would be possible to implement sandboxing in a `.Net` application? I'm asking in the context of `CefSharp`.

It appears the only valid workaround for the `Pepper Flash` issue listed http://magpcss.org/ceforum/viewtopic.php?f=6&t=13094

Code: Select all
// To enable the sandbox on Windows the following requirements must be met:
// 1. Use the same executable for the browser process and all sub-processes.
// 2. Link the executable with the cef_sandbox static library.
// 3. Call the cef_sandbox_info_create() function from within the executable
//    (not from a separate DLL) and pass the resulting pointer into both the
//    CefExecutProcess() and CefInitialize() functions via the
//    |windows_sandbox_info| parameter.


1. Little bit of work and this should be possible.
2. We can link the cef_sandbox static library against the VC++ wrapper that we use, how strict a requirement is this?
3. We wrap all the CEF calls in a VC++ wrapper library, which I guess equates to being called from a separate dll? Only thing I can think of is to P/Invoke the call from C#. What are the implications of calling this from a separate dll?

Similar question was asked in https://bitbucket.org/chromiumembedded/cef/issues/524/cef3-enable-sandbox-support#comment-16410750.

Currently we're using the 2526 branch.
Maintainer of the CefSharp project.
amaitland
Virtuoso
 
Posts: 1292
Joined: Wed Jan 14, 2015 2:35 am

Re: Implementing SandBox Support

Postby magreenblatt » Mon Jan 11, 2016 12:28 pm

The cef_sandbox library must be statically linked to the exe and the cef_sandbox_info_create() function must be called from the exe.
magreenblatt
Site Admin
 
Posts: 12409
Joined: Fri May 29, 2009 6:57 pm

Re: Implementing SandBox Support

Postby amaitland » Mon Jan 11, 2016 6:29 pm

Thanks for the prompt reply!

I guess we have to hope that the Pepper Flash issue is resolved.
Maintainer of the CefSharp project.
amaitland
Virtuoso
 
Posts: 1292
Joined: Wed Jan 14, 2015 2:35 am

Re: Implementing SandBox Support

Postby akjana » Mon Feb 22, 2016 7:29 pm

Hi

The cef_sandbox library must be statically linked to the exe and the cef_sandbox_info_create() function must be called from the exe.


Wondering how/why this requirement came about to be?
I was searching Chromium code base while referring to the Cef implemention in https://chromiumembedded.googlecode.com/svn/trunk/cef3/libcef_dll/sandbox/sandbox_win.cc and spotted this https://code.google.com/p/chromium/codesearch#chromium/src/content/public/app/sandbox_helper_win.h&q=InitializeSandboxInfo&sq=package:chromium&type=cs&l=18 but it doesn't have any useful explanation.

Thank you
akjana
Techie
 
Posts: 39
Joined: Wed Feb 03, 2016 2:15 am

Re: Implementing SandBox Support

Postby magreenblatt » Mon Feb 22, 2016 7:46 pm

It's a requirement of the Windows sandbox implementation. Basically, the main process identifies certain code offsets in the main executable and then hooks those offset locations in the child process after spawning with reduced OS permissions. If the subprocess uses a different executable then it doesn't work. See the Chromium sandbox architecture docs and code for more information.
magreenblatt
Site Admin
 
Posts: 12409
Joined: Fri May 29, 2009 6:57 pm

Re: Implementing SandBox Support

Postby amaitland » Mon Feb 22, 2016 7:46 pm

For reference CEF moved to bitbucket quite some time ago, any code on the old googlecode site is potentially outdated.

https://bitbucket.org/chromiumembedded/cef/overview
Maintainer of the CefSharp project.
amaitland
Virtuoso
 
Posts: 1292
Joined: Wed Jan 14, 2015 2:35 am

Re: Implementing SandBox Support

Postby sreenivas003 » Tue Feb 23, 2016 2:19 am

hi,
I am also facing a similar issue(Pepper flash)(unwanted console is shown when launching flash content in cef). i cannot enable sandbox as my main executable and subprocess names are different(aplication requirement). Is there any way i can prevent the console window opening or any ways to enable sandbox even if the executable and subprocess names are different.

thanks & regards,
Sreenivas
sreenivas003
Newbie
 
Posts: 3
Joined: Tue Feb 23, 2016 2:12 am

Re: Implementing SandBox Support

Postby magreenblatt » Tue Feb 23, 2016 11:20 am

sreenivas003 wrote:Is there any way i can prevent the console window opening or any ways to enable sandbox even if the executable and subprocess names are different.

No to both. The flash console window is discussed in viewtopic.php?f=6&t=13094
magreenblatt
Site Admin
 
Posts: 12409
Joined: Fri May 29, 2009 6:57 pm

Re: Implementing SandBox Support

Postby ndesktop » Fri Aug 10, 2018 1:12 am

sreenivas003 wrote:Is there any way i can prevent the console window opening or any ways to enable sandbox even if the executable and subprocess names are different.

Actually, it is possible. Not an easy or a pleasant thing to do.
Short story: the sandbox conflicts with our own AV product and cannot be used (not easily, at least).

Use the following examples and ideas on your own risk.

* * *
Inside pepper Flash it is code that spawns a console with something like "cmd /c echo NOT SANDBOXED".
The API call for this is CreateProcessA.

What I did is this:
- self patching CreateProcessA in the host executable (I didn't read the whole thread, but I saw it is suggested to hook CreateProcess)
- look at the arguments of CreateProcessA
- get the path of cmd.exe
- if command line ends up being the one we're looking for (LPSTARTUPINFOA StartupInfo is the penultimate argument of CreateProcessA), hide the console
Code: Select all
  WORD wShowWindow = StartupInfo->wShowWindow;
  StartupInfo->wShowWindow = SW_HIDE;

- let CreateProcessA hook continue
- restore flags
Code: Select all
  StartupInfo->wShowWindow = wShowWindow;


I won't go in the details on how to hook the API calls.
The patching needs to go thru 2 stages:
- patch LoadLibraryW/LoadLibraryA/LoadLibraryExW/LoadLibraryExA for intercepting when pepflashplayer is loaded.
My application is Unicode so libcef!LoadLibraryW will do, but is always possible that your dll might use LoadLibraryExA for some reason etc.
Code: Select all
#if !defined(ENABLE_CEF_SANDBOX)
        //  libcef!LoadLibraryW
    IATPatch::PatchImport(
        "libcef.dll",
        "kernel32.dll", "LoadLibraryW",
        (FARPROC)&Jmp_LoadLibraryW);
        //  libcef!LoadLibraryExW
    IATPatch::PatchImport(
        "libcef.dll",
        "kernel32.dll", "LoadLibraryExW",
        (FARPROC)&Jmp_LoadLibraryExW);
#endif // ENABLE_CEF_SANDBOX

We'll consider Jmp_LoadLibraryW to be the target here.

- the patched Jmp_LoadLibraryW needs to intercept when pepflashplayer is loaded, so it can patch also inside pepflashplayer the call to CreateProcessA:
Code: Select all
HMODULE WINAPI patch_handler_t::Jmp_LoadLibraryW(
    LPCWSTR lpFileName
) {
...
// let original function call
                HMODULE result = __C__Jmp_LoadLibraryW(
                    pfnOriginalFunction,
                    lpFileName
                );
...
                // save last error to restore after; we can use a secondary thread and wait, but why bother
                DWORD last_error = GetLastError();

#if !defined(ENABLE_CEF_SANDBOX)
                if(result != NULL) {
                    if(lpFileName != NULL && lstrlenW(lpFileName) != 0) {
                        WCHAR szDllName[MAX_PATH] = L"";
                        if(wcsncpy_s(szDllName, _countof(szDllName),
                            lpFileName, _TRUNCATE) != STRUNCATE) {
                            PathStripPathW(szDllName);
                            _wcslwr_s(szDllName, _countof(szDllName));

                            LPWSTR lpExt = PathFindExtensionW(szDllName);
                            if(lpExt != NULL && _wcsicmp(lpExt, L".dll") == 0) {
                                const wchar_t* ptrpep_32 = wcsstr(szDllName, L"pepflashplayer32");
                                const wchar_t* ptrpep_64 = wcsstr(szDllName, L"pepflashplayer64");

                                bool fIsPepFlashPlayer32 = ptrpep_32 == szDllName;
                                bool fIsPepFlashPlayer64 = ptrpep_64 == szDllName;
                                if(fIsPepFlashPlayer32 || fIsPepFlashPlayer64) {
                                    CHAR szDllName_A[MAX_PATH] = "";
                                    if(WideCharToMultiByte(CP_ACP, 0,
                                        szDllName, -1, szDllName_A, _countof(szDllName_A),
                                        NULL, NULL) != 0) {

                                        IATPatch::PatchImport(
                                            szDllName_A,
                                            "kernel32.dll", "CreateProcessA",
                                            (FARPROC)&Jmp_CreateProcessA);
                                    }
                                }
                            }
                        }
                    }
                }
#endif // ENABLE_CEF_SANDBOX

                // restore last error
                if(result == NULL) {
                    SetLastError(last_error);
                }
...

Now we patched CreateProcessA inside pepflashplayer32.dll/pepflashplayer3264.dll.

- inside the patched CreateProcessA do the processing described above to detect cmd /c echo NOT SANDBOXED
Code: Select all
BOOL WINAPI patch_handler_t::Jmp_CreateProcessA(
    LPCSTR lpApplicationName,
    LPSTR lpCommandLine,
    LPSECURITY_ATTRIBUTES lpProcessAttributes,
    LPSECURITY_ATTRIBUTES lpThreadAttributes,
    BOOL bInheritHandles,
    DWORD dwCreationFlags,
    LPVOID lpEnvironment,
    LPCSTR lpCurrentDirectory,
    LPSTARTUPINFOA lpStartupInfo,
    LPPROCESS_INFORMATION lpProcessInformation
) {
...
#if !defined(ENABLE_CEF_SANDBOX)
                // is our command prompt launched from pepflashplayer?
                // if so, modify it
                // we don't have a context to know "CreateProcessA was launched from
                // pepflashplayer and not from libcef.dll or our exe" because all functions
                // are patched thru the same entry Jmp_CreateProcessA
                // so we're using detection/patching in place using whatever arguments
                // recognition we have locally.
                bool fIsPepflashplayerCmdNotSandboxed = false;
                {
                    CHAR szCmd_A[MAX_PATH] = "";
                    if(lpCommandLine != NULL &&
                        lstrlenA(lpCommandLine) != 0) {
                        LPITEMIDLIST pidl = NULL;
                        HRESULT hr = SHGetKnownFolderIDList(
                            FOLDERID_System, 0, NULL, &pidl);
                        if(SUCCEEDED(hr)) {
                            CHAR szPath_A[MAX_PATH] = "";
                            if(SHGetPathFromIDListA(pidl, szPath_A)) {
                                static const char* kCmdExe = "cmd.exe";
                                static const char* kArgNotSandboxed = "/c echo NOT SANDBOXED";
                                if(::PathAppendA(szPath_A, kCmdExe)) {
                                    CHAR szApp_A[MAX_PATH] = "";
                                    strncpy_s(szApp_A, _countof(szApp_A),
                                        szPath_A, _TRUNCATE);

                                    strncpy_s(szCmd_A, _countof(szCmd_A),
                                        szApp_A, _TRUNCATE);
                                    strncat_s(szCmd_A, _countof(szCmd_A),
                                        " ", _TRUNCATE);
                                    strncat_s(szCmd_A, _countof(szCmd_A),
                                        kArgNotSandboxed, _TRUNCATE);
                                }
                            }
                        }
                        if(pidl != NULL) {
                            ILFree(pidl);
                        }
                    }

                    if(lstrlenA(szCmd_A) != 0) {
                        __trackVW(
                            L"[IAT-PATCH] lpCommandLine=[%hs] szCmd_A=[%hs]",
                            LOG_LEVEL_TRACE,
                            lpCommandLine, szCmd_A);

                        if(lstrcmpiA(szCmd_A, lpCommandLine) == 0) {
                            fIsPepflashplayerCmdNotSandboxed = true;
                        }
                    }
                }
#endif // ENABLE_CEF_SANDBOX

                do {
#if !defined(ENABLE_CEF_SANDBOX)
                    __trackVW(
                        L"[IAT-PATCH] fIsPepflashplayerCmdNotSandboxed=%s",
                        LOG_LEVEL_TRACE,
                        fIsPepflashplayerCmdNotSandboxed ? L"true" : L"false");

                    DWORD dwFlags = lpStartupInfo->dwFlags;
                    WORD wShowWindow = lpStartupInfo->wShowWindow;
                    if(fIsPepflashplayerCmdNotSandboxed) {
                        // let the process run to avoid tampering caller,
                        // but modify its windowing to be invisible by changing
                        // flags and window visibility
                        if(lpStartupInfo != NULL) {
                            if((lpStartupInfo->dwFlags & STARTF_USESHOWWINDOW) == 0) {
                                lpStartupInfo->dwFlags |= STARTF_USESHOWWINDOW;
                            }
                            lpStartupInfo->wShowWindow = SW_HIDE;
                        }
                    }
#endif // ENABLE_CEF_SANDBOX

                    result = __C__Jmp_CreateProcessA(
                        pfnOriginalFunction,
                        lpApplicationName,
                        lpCommandLine,
                        lpProcessAttributes,
                        lpThreadAttributes,
                        bInheritHandles,
                        dwCreationFlags,
                        lpEnvironment,
                        lpCurrentDirectory,
                        lpStartupInfo,
                        lpProcessInformation
                    );

#if !defined(ENABLE_CEF_SANDBOX)
                    // restore STARTUPINFOA if changed
                    // to play nice with the caller
                    if(lpStartupInfo->dwFlags != dwFlags)
                        lpStartupInfo->dwFlags = dwFlags;
                    if(lpStartupInfo->wShowWindow != wShowWindow)
                        lpStartupInfo->wShowWindow = wShowWindow;
#endif // ENABLE_CEF_SANDBOX

#pragma warning(disable: 4127)
                } while(0);
#pragma warning(default: 4127)

...


Notes.
1. __C__Jmp_CreateProcessA is the "C" version of C++ called Jmp_CreateProcessA (basically the __C__XXX functions are extern "C" and uses __try/__except or __try/__finally, a custom SEH handler etc.) to avoid C++ inside calls.
2. Patching is different between 32 and 64 bit. But VirtualProtect is the call in both.
A short example:
Code: Select all
#ifdef WIN64
namespace IATPatch64 {
    struct iat_patch_t {
        void*            original;
        char             patched[256];
        unsigned __int64 patchSize;
        void*            original_function;
    };
...
};
...


namespace IATPatchMemory64 {
    void _memwrite(void* dest,
        const void* source,
        unsigned __int64 size)
    {
        DWORD access = 0;
        VirtualProtect(dest, size,
            PAGE_EXECUTE_READWRITE,
            &access);
        __try {
            memcpy_s(dest, size, source, size);
        }
        __except(EXCEPTION_EXECUTE_HANDLER) {
        }
        VirtualProtect(dest, size,
            access, &access);
        return;
    }
    void _memwrite(unsigned __int64 dest,
        const void* source,
        unsigned __int64 size)
    {
        _memwrite((void*)dest, source, size);
        return;
    }
    void memwrite(void* dest,
        const void* source,
        unsigned __int64 size)
    {
        critical_section_lock_t lock(
            &IATPatch64::s__csPatches);

        IATPatch64::iat_patch_t patch;
        patch.original = dest;
        patch.patchSize = size;
        memcpy_s(patch.patched,
            _countof(patch.patched),
            source, size);
        patch.original_function = *(FARPROC*)dest;

        _memwrite(dest, source, size);
        IATPatch64::getPatches()->push_back(patch);
        return;
    }
};

FARPROC
PatchImportModule(
    const char* sourceModule,
    const char* importModule,
    const char* name,
    void* patchFunction)
{
...
                FARPROC oldFxn = (FARPROC)f_import[i];
                IATPatchMemory64::memwrite(
                    &f_import[i],
                    &patchFunction,
                    sizeof(patchFunction));
                return oldFxn;
...


The above excerpts are obviously incomplete and I won't pretend it will be easy for reader to fill the gaps.
But as a general idea for those wanting to adventure in places like these it might help.
ndesktop
Master
 
Posts: 756
Joined: Thu Dec 03, 2015 10:10 am

Re: Implementing SandBox Support

Postby ndesktop » Fri Aug 10, 2018 2:35 am

A note about the original question that asked how to *prevent*: one can return FALSE and set ERROR_ACCESS_DENIED, or simply can return TRUE and skip calling the original CreateProcessA.
While these can be done, one cannot know what execution path/side effect occurs in pepflashplayer.dll if returning FALSE/SetLastError, or returning TRUE and not creating the process.

This is why I choose to let the console be invoked and just hide it using STARTUPINFO::wShowWindow.
ndesktop
Master
 
Posts: 756
Joined: Thu Dec 03, 2015 10:10 am


Return to Support Forum

Who is online

Users browsing this forum: Google [Bot] and 51 guests