将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 ()
答案 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()'调用之前,而不是被编译到静态构造函数的主体。修复 - 将测试模块放入单独的非最后文件或将其移动到库