ApplicationBase应该与沙箱AppDomain不同吗?

时间:2012-11-07 23:45:41

标签: .net f# clr sandbox appdomain

将slave sandbox域的ApplicationBase设置为与托管域相同的路径有哪些确切的安全隐患?

我发现MSDN指南声明ApplicationBase对于从属域应该是不同的“如果ApplicationBase设置相同,则部分信任应用程序可以使宿主应用程序加载(完全信任)它定义的异常,从而利用它“(第3页):

http://msdn.microsoft.com/en-us/library/bb763046.aspx

这种漏洞利用究竟是如何运作的?

在我的场景中,我愿意完全信任地运行位于ApplicationBase下的所有程序集。我专门为从属AppDomain沙箱化,以限制该域内动态生成的程序集的权限。我尝试遵循这些准则但是更改ApplicationBase属性似乎打破了域之间的双向通信桥,因为程序集加载到LoadFrom上下文中,所以我想避免它。

示例F#代码演示了不同ApplicationBase值的问题:

module Main =

    open System
    open System.Diagnostics
    open System.IO
    open System.Reflection
    open System.Security
    open System.Security.Permissions
    open System.Security.Policy

    /// Change this switch to observe the problem.
    let useSameApplicationBase = true

    let getStrongName (a: Assembly) =
        match a.Evidence.GetHostEvidence<StrongName>() with
        | null -> None
        | sn -> Some sn

    let getAssemblies () =
        [|
            Assembly.GetExecutingAssembly()
        |]

    let buildAppDomain () =
        let fullTrust =
            getAssemblies ()
            |> Array.choose getStrongName
        let evidence = null
        let appBase =
            if useSameApplicationBase then
                AppDomain.CurrentDomain.BaseDirectory
            else
                Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Untrusted")
        let setup = AppDomainSetup(ApplicationBase = appBase)
        let perms = PermissionSet(PermissionState.None)
        AppDomain.CreateDomain("SLAVE", null, setup, perms, fullTrust)

    [<Sealed>]
    type Backer() =
        inherit MarshalByRefObject()
        member this.Pong() =
            Console.WriteLine("PONG IN DOMAIN = {0}", AppDomain.CurrentDomain.FriendlyName)

    [<Sealed>]
    type Sandbox() =
        inherit MarshalByRefObject()
        member this.Start(backer: obj) =
            Console.WriteLine("RUN IN SLAVE DOMAIN = {0}", AppDomain.CurrentDomain.FriendlyName)
            (backer :?> Backer).Pong()

    let test () =
        let dom = buildAppDomain ()
        try
            let handle =
                Activator.CreateInstanceFrom(dom,
                    typeof<Sandbox>.Assembly.Location,
                    typeof<Sandbox>.FullName)
            let sandbox = handle.Unwrap() :?> Sandbox
            sandbox.Start(Backer())
        finally
            AppDomain.Unload(dom)

    test ()

1 个答案:

答案 0 :(得分:3)

module Main =

    open System
    open System.Diagnostics
    open System.IO
    open System.Reflection
    open System.Security
    open System.Security.Permissions
    open System.Security.Policy

    /// Change this switch to observe the problem.
    let useSameApplicationBase = false

    let getStrongName (a: Assembly) =
        match a.Evidence.GetHostEvidence<StrongName>() with
        | null -> None
        | sn -> Some sn

    let getAssemblies () =
        [|
            Assembly.GetExecutingAssembly()
        |]

    let buildAppDomain () =
        let fullTrust =
            getAssemblies ()
            |> Array.choose getStrongName
        let evidence = null
        let appBase =
            if useSameApplicationBase then
                AppDomain.CurrentDomain.BaseDirectory
            else
                Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Untrusted")
        let setup = AppDomainSetup(ApplicationBase = appBase)
        let perms = PermissionSet(PermissionState.None)
        AppDomain.CreateDomain("SLAVE", null, setup, perms, fullTrust)

    module AssemblyResolveSetup = 
        let install() = 
            let resolveHandler = 
                    ResolveEventHandler(
                        fun _ args ->
                            // try to find requested assembly in current domain
                            let name = AssemblyName(args.Name)
                            let asmOpt =
                                AppDomain.CurrentDomain.GetAssemblies()
                                |> Array.tryFind(fun asm -> AssemblyName.ReferenceMatchesDefinition(AssemblyName(asm.FullName), name))
                            defaultArg asmOpt null
                    )        
            AppDomain.CurrentDomain.add_AssemblyResolve(resolveHandler)

    [<Sealed>]
    type Backer() =
        inherit MarshalByRefObject()
        member this.Pong() =
            Console.WriteLine("PONG IN DOMAIN = {0}", AppDomain.CurrentDomain.FriendlyName)

    [<Sealed>]
    type Sandbox() =
        inherit MarshalByRefObject()
        do AssemblyResolveSetup.install()
        member this.Start(backer: obj) =
            Console.WriteLine("RUN IN SLAVE DOMAIN = {0}", AppDomain.CurrentDomain.FriendlyName)
            (backer :?> Backer).Pong()

    let test () =
        let dom = buildAppDomain ()
        try
            let handle =
                Activator.CreateInstanceFrom(dom,
                    typeof<Sandbox>.Assembly.Location,
                    typeof<Sandbox>.FullName)
            let sandbox = handle.Unwrap() :?> Sandbox
            sandbox.Start(Backer())
        finally
            AppDomain.Unload(dom)

    test ()

UPDATE(假设测试代码包含在程序集Sandbox.exe中)

:分辨率如何通过查看SLAVE(CurrentDomain)在SLAVE中找到程序集,听起来像一个恶性循环

SLAVE域已包含Sandbox.exe但它在LoadFrom上下文中加载,因此在解析Load上下文(Choosing a binding context)的依赖关系时不会自动探测它。

:为什么它会因为asm.GetName()而不是AssemblyName(asm.FullName)而中断

Assembly.GetName需要FileIOPermission,Assembly.FullName - 不要这样我想如果你替换

AssemblyName(asm.FullName)

let name = AssemblyName(args.Name)
let p = new FileIOPermission(PermissionState.Unrestricted)
p.Assert()
try
    let asmOpt =
        AppDomain.CurrentDomain.GetAssemblies()
        |> Array.tryFind(fun asm -> AssemblyName.ReferenceMatchesDefinition(asm.GetName(), name))
    defaultArg asmOpt null
finally
    CodeAccessPermission.RevertAssert()

这也应该有效(未尝试过)

:为什么要为静态做刹车的事做AssemblyResolveSetup.install()

这是这里唯一的F#特定问题。我猜您的测试项目是编译为exe的单个文件项目。根据F#规范:

对于具有隐式入口点的可执行文件,显示在上一个文件的静态初始化程序 命令行是隐式入口点函数的主体。

因此,'static do'块中的代码将放在隐式入口点的'test()'调用之前,而不是被编译到静态构造函数的主体。修复 - 将测试模块放入单独的非最后文件或将其移动到库