VirtualBox

Ignore:
Timestamp:
Oct 22, 2015 12:23:07 AM (9 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
103578
Message:

SUPR3HardenedMain.cpp: Started on a @page about the code, still a lot of work left on windows.

Location:
trunk/src/VBox/HostDrivers/Support
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/HostDrivers/Support/SUPR3HardenedMain.cpp

    r58340 r58363  
    2323 * You may elect to license modified versions of this file under the
    2424 * terms and conditions of either the GPL or the CDDL or both.
     25 */
     26
     27/** @page pg_hardening      VirtualBox VM Process Hardening
     28 *
     29 * The VM process hardening is to prevent malicious software from using
     30 * VirtualBox as a vehicle to obtain kernel level access.
     31 *
     32 * The VirtualBox VMM requires supervisor (kernel) level access to the CPU.  For
     33 * both practical and historical reasons part of the VMM is still implemented in
     34 * ring-3 and has a rich interface to the kernel part.  While the device
     35 * emulations can be run all in ring-3, we have performance optimizations that
     36 * loads device emulation code into ring-0 and our special raw-mode execution
     37 * context (non-VT-x/AMD-V mode) for handling frequent operations.  These share
     38 * data between all three context (ring-3, ring-0 and raw-mode).  All this poses
     39 * a rather broad attack surface, which the hardening protects.
     40 *
     41 * The hardening primarily focuses on restricting access to the support driver,
     42 * VBoxDrv or vboxdrv depending on the OS, as it is ultimately the link and
     43 * instigator of the communication between ring-3 and the ring-0 and raw-mode
     44 * contexts. A secondary focus is to make sure malicious code cannot be loaded
     45 * and executed in the VM process.  Exactly how we go about this depends a lot
     46 * on the host OS.
     47 *
     48 *
     49 * @section sec_hardening_unix  Hardening on UNIX-like OSes
     50 *
     51 * On UNIX-like systems (Solaris, Linux, darwin, freebsd, ...) only allow root
     52 * to get full unrestricted access to the support driver.  The device node
     53 * corresponding to unrestricted access is own by root and has a 0600 access
     54 * mode (i.e. only accessible to the owner, root).  In addition to this file
     55 * system level restriction, the support driver also checks that the effective
     56 * user ID (EUID) is root when it is being opened.
     57 *
     58 * The VM processes temporarily assume root privileges using the set-uid-bit on
     59 * the executable with root as owner.  In fact, all the executable files, shared
     60 * objects and the other files and directories we install are owned by root and
     61 * the wheel (or equivalent gid = 0) group.
     62 *
     63 * The executable with the set-uid-to-root-bit set is a stub binary that has no
     64 * unnecessary library dependencies (only libc) and simply calls
     65 * #SUPR3HardenedMain.  SUPR3HardenedMain does the following:
     66 *
     67 *      -# Validate installation (supR3HardenedVerifyAll):
     68 *          - Check that the executable file of the process is one of the known
     69 *            VirtualBox executables.
     70 *          - Check that all mandatory files are present.
     71 *          - Check that all installed files and directories (both optional and
     72 *            mandatory ones) are owned by root:wheel and are not writable by
     73 *            anyone except root.
     74 *          - Check that all the parent directories, all the way up to the root
     75 *            if possible, only permits root (or system admin) to change them -
     76 *            in order to exclude directory renaming races.
     77 *          - On systems where it is possible, we may also valiadate signatures.
     78 *
     79 *      -# Open a file descriptor for the support device driver
     80 *         (supR3HardenedMainOpenDevice).
     81 *
     82 *      -# Grab ICMP capabilites, if needed (supR3HardenedMainGrabCapabilites).
     83 *
     84 *      -# Correctly drop the root privileges (supR3HardenedMainDropPrivileges).
     85 *
     86 *      -# Load the VBoxRT dynamic link library and hand over the file
     87 *         descriptor to the SUPLib code in it (supR3HardenedMainInitRuntime).
     88 *
     89 *      -# Load a dynamic library containing the VM frontend code and run it
     90 *         (tail of SUPR3HardenedMain).  The set-uid-to-root stub executable is
     91 *         paired with a dynamic link library which export one TrustedMain
     92 *         entrypoint (see FNSUPTRUSTEDMAIN) that we call.
     93 *
     94 *         In case of error reporting, the library may also export a
     95 *         TrustedError function (FNSUPTRUSTEDERROR).
     96 *
     97 *  That a process was started with a set-uid-to-root-bit applied is something
     98 *  that sticks with the process even if after dropping the root privileges and
     99 *  becoming the original user.  The dynamic linkers take special care when
     100 *  dealing with processes of this kind to not allow the user to load arbitrary
     101 *  code into a root process and causing a privilege escalation issue.  This is
     102 *  of course exactly the kind of behavior we're looking for.
     103 *
     104 *  In addition to what the dynamic linker does for us, we will not directly
     105 *  call either RTLdrLoad or dlopen to load dynamic link libraries into the
     106 *  process.  Instead we will call SUPR3HardenedLdrLoad,
     107 *  SUPR3HardenedLdrLoadAppPriv or SUPR3HardenedLdrLoadPlugIn to do the loading.
     108 *  These functions will perform the validations on the file being loaded as
     109 *  SUPR3HardenedMain did in its validation step.  So, anything we load must be
     110 *  installed owned by root:wheel, the directory we load it from must also be
     111 *  owned by root:wheel and now allow for renaming the file.  Similar ownership
     112 *  restricts applies to all the parent directories (except on darwin).
     113 *
     114 *  So, we leave the responsibility of not installing malicious software on the
     115 *  root user on UNIX-like systems.  Which is fair enough, in our opinion.
     116 *
     117 *
     118 * @section sec_hardening_win   Hardening on Windows
     119 *
     120 * On Windows things are a lot more complicated, unforunately.  This is mainly
     121 * because on windows you cannot trust the Administrators users.  Some or the
     122 * blame for this is that Windows is a decentant/replacement for a set of single
     123 * user systems: DOS, Windows 1.0-3.11 Windows 95-ME, and OS/2.  Users of NT
     124 * 3.51 and later was inclied to want to always run it with full
     125 * root/administrator privileges like they had done on the predecessors, while
     126 * Microsoft made doing some very simple and didn't help with the alternative.
     127 * Bad idea, security wise, which is good for the security software industry.
     128 * For this reason using a set-uid-to-root approach is pointless, even if
     129 * windows had one, which is doesn't.
     130 *
     131 * So, in order to protect access to the support driver and protect the
     132 * VM process while it's running we have to do a lot more work.  A keystone in
     133 * the defences is code signing.  The short version is this:
     134 *
     135 *      - Minimal stub executable, signed with the same certificate as the
     136 *        kernel driver.
     137 *
     138 *      - The stub executable respawns itself twice, hooking the NTDLL init
     139 *        routine to perform protection tasks as early as possible.  The parent
     140 *        stub helps keep in the child clean for verification as does the
     141 *        support driver.
     142 *
     143 *      - In order to protect against loading unwanted code into the process,
     144 *        the stub processes installs DLL load hooks with NTDLL as well as
     145 *        directly intercepting the LdrLoadDll and NtCreateSection APIs.
     146 *
     147 *      - The support driver will verify all but the initial process very
     148 *        thoroughly before allowing them protection and in the final case full
     149 *        unrestricted access.
     150 *
     151 * What makes our life REALLY difficult on Windows is this 3rd party "security"
     152 * software which is more or less required to keep a Windows system safe for
     153 * normal users and all corporate IT departments righly insists on installing.
     154 * After the kernel patching clampdown in Vista, AV software have to do a lot
     155 * more mucking about in user mode to get their job (kind of) done.  So, it
     156 * common practice to patch a lot of NTDLL, KERNEL32, the executable import
     157 * table, load extra DLLS into the process, allocate executable memory in the
     158 * process and worse.  The BIG problem with all this is that it is
     159 * indistiguishable from what malicious software would be doing in order to
     160 * intercept process acctivity (network sniffing, maybe password snooping) or
     161 * gain a level of kernel access via the the support driver.
     162 *
     163 * We share the stub executable approach with the UNIX-like systems, so there's
     164 * the SUPR3HardenedMain and a paried DLL with TrustedMain and TrustedError.
     165 * However, the stub executable is pushed a bit further here.
     166 *      - It has no CRT (libc) because we don't need one and we need full
     167 *        control over the code in the stub.
     168 *      - It does not statically import anything to avoid having a import table
     169 *        that can be patched or extended to either intercept our calls or load
     170 *        additional DLLs.
     171 *      - System calls normally going thru NTDLL are done directly because there
     172 *        is so much software out there which wants to patch known NTDLL entry
     173 *        points to control our software (either for good or malicious reasons).
     174 *
     175 * The initial stub process is not really to be trusted, though we try our best
     176 * to limit potential harm (user mode debugger checks, disable thread creation).
     177 * So, when it enters SUPR3HardenedMain we only call supR3HardenedVerifyAll to
     178 * verify the installation (known executables and dlls, checking their code
     179 * signing signatures, keeping them all open to deny deletion and replacing) and
     180 * does a respawn via supR3HardenedWinReSpawn.
     181 *
     182 * The second stub process will be created in suspended state (the thread hasn't
     183 * executed a single instruction) and with less generous ACLs associated with it
     184 * (skin deep protection only).  In order for SUPR3TrustedMain to figure it's
     185 * the second stub process, the zero'th command line argument has been replaced
     186 * by a known magic string (UUID).   Now, before the process starts executing,
     187 * the parent will patch the LdrInitializeThunk entrypoint in NTDLL to call
     188 * supR3HardenedEarlyProcessInit via supR3HardenedEarlyProcessInitThunk.  The
     189 * parent will also plant some synchronization stuff via SUPR3WINPROCPARAMS
     190 * (NTDLL location, inherited event handles and associated ping-pong equipment).
     191 *
     192 * The LdrInitializeThunk entrypoint of NTDLL is where the kernel sets up
     193 * process execution to start executing (via a user alert, so not subject to
     194 * SetThreadContext).  LdrInitializeThunk performs process, NTDLL and
     195 * sub-system client (kernel32) initialization.  A lot of "protection" software
     196 * uses triggers in this initialization sequence (like the KERNEL32.DLL load
     197 * event), so we avoid quite a bit problems by getting our stuff done early on.
     198 *
     199 * However, there is also those that uses events that triggers immediately when
     200 * the process is created or/and starts executing the first instruction, we have
     201 * a well know process state we can restore.  The first thing that
     202 * supR3HardenedEarlyProcessInit does is to signal the parent to do perform a
     203 * child purification to exorcise potentially evil influences.
     204 *
     205 * What the parent does during the purification is very similar to what the
     206 * kernel driver will do later on when verifying the second stub and the VM
     207 * processes, except that instead of failing when encountering an issue it will
     208 * take corrective actions:
     209 *      - Executable memory regions not belonging to a DLL mapping will be
     210 *        attempted freed, and we'll only fail if we cann evict it.
     211 *      - All pages in the executable images in the process (should be just the
     212 *        stub executable and NTDLL) will be compared to the pristine fixed-up
     213 *        copy prepared by the IPRT PE loader code, restoring any bytes which
     214 *        appears differently in the child.  (g_ProcParams (SUPR3WINPROCPARAMS)
     215 *        is exempted, LdrInitializeThunk is set to call NtTerminateThread.)
     216 *      - Unwanted DLLs will be unloaded (we have a set of DLLs we like).
     217 *
     218 * Before signalling the second stub process that it has been purified and shoud
     219 * get on with it, the parent will close all handles with unrestricted access to
     220 * the process and thread so that the initial stub process no longer can
     221 * influence the child in any really harmful way.  (The caller of CreateProcess
     222 * usually receives handles with unrestricted access to the child process and
     223 * main thread.  These could in theory be used with DuplicateHandle or
     224 * WriteProcessMemory to get at the VM process if we're not careful.)
     225 *
     226 * supR3HardenedEarlyProcessInit will continue with opening the log file
     227 * (require command line parsing).  It will continue to initialize a bunch of
     228 * globals, syscalls and trustworthy/harmless NTDLL imports.
     229 * supR3HardenedWinInit is then called to setup image verification, that is:
     230 *      - Hook (insert jump instruction) the NtCreateSection entrypoint in NTDLL
     231 *        so we can check all executable mappings before they're created and can
     232 *        be mapped.
     233 *      - Hook (ditto) the LdrLoadDll entrypoint in NTDLL so we can prevalidate
     234 *        all images that gets loaded the normal way (partly because the
     235 *        NtCreateSection context is restrictive because the NTDLL loader lock
     236 *        is usally held, which prevents us from safely calling WinVerityTrust).
     237 * The image/dll verification hooks are at this point able to verify DLLs
     238 * containing code signing signatures, and will restrict the locations from
     239 * which DLLs will be loaded.  When SUPR3HardenedMain gets going later one, they
     240 * will start insiting on everything having valid signatures in the DLL or in an
     241 * installer catalog file.
     242 *
     243 * The function also irrevocably disables debug notifications related to teh
     244 * current thread, just to make attaching a debugging that much more difficult.
     245 *
     246 * Now, the second stub process will open the so called stub device, that is a
     247 * special support driver device node that tells the support driver to:
     248 *      - Protect the process against the OpenProcess and OpenThread attack
     249 *        vectors by stripping risky access rights.
     250 *      - Check that the process isn't being debugged.
     251 *      - Check that the process contains exactly one thread.
     252 *      - Check that the process doesn't have any unknown DLLs loaded into it.
     253 *      - Check that the process doesn't have any executable memory (other that
     254 *        DLL sections) in it.
     255 *      - Check that the process executable is a known VBox executable which may
     256 *        access the support driver.
     257 *      - Check that the process executable is signed with the same code signing
     258 *        certificate as the driver and that the on disk image is valid
     259 *        according to its embedded signature.
     260 *      - Check all the signature of all DLLs in the process (NTDLL) if they are
     261 *        signed, and only accept unsigned ones in versions where they are known
     262 *        not to be signed.
     263 *
     264 *
     265 *
    25266 */
    26267
  • trunk/src/VBox/HostDrivers/Support/win/SUPR3HardenedMain-win.cpp

    r58339 r58363  
    55885588/**
    55895589 * Routine called by the supR3HardenedEarlyProcessInitThunk assembly routine
    5590  * when LdrInitializeThunk is executed in during process initialization.
     5590 * when LdrInitializeThunk is executed during process initialization.
    55915591 *
    55925592 * This initializes the Stub and VM processes, hooking NTDLL APIs and opening
  • trunk/src/VBox/HostDrivers/Support/win/SUPR3HardenedMainImports-win.cpp

    r58339 r58363  
    701701
    702702    /*
    703      * Pointer the other imports at the early init stubs.
     703     * Point the other imports at the early init stubs.
    704704     */
    705705    for (uint32_t iDll = 1; iDll < RT_ELEMENTS(g_aSupNtImpDlls); iDll++)
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette