使用Type.GetType调用不受信任的类型名称是否安全?

28

我在代码审查中发现了以下内容:

Type type = Type.GetType(typeName);
if (type == typeof(SomeKnownType))
    DoSomething(...); // does not use type or typeName

typeName 来自 AJAX 请求并且没有经过验证。 这会有任何潜在的安全问题吗? 例如,是否可能由于从任意程序集中加载任意类型而执行意外代码或导致整个应用程序崩溃(拒绝服务)?

(我想一些开玩笑的人可能会试图通过加载 GAC 中每个程序集中的每个类型来耗尽可用内存。有更糟糕的情况吗?)

注:

  • 这是在完全信任下运行的 ASP.NET 应用程序。
  • 生成的type仅用于上述显示。不会尝试实例化该类型。

1
你的内存使用量可能会急剧增加。你能否提供一个从字符串到“Type”类型的白名单映射? - Daniel A. White
1
+1 非常有趣的问题。我不知道 GetType 是否有任何副作用(如实例化),但一个规范的答案会非常有趣!顺便说一句,我希望所有的问题都像这个问题一样清晰,并在第一次提问时包含所有相关信息。真是令人耳目一新! - BradleyDotNET
6
对安全性不发表评论,但这并不需要,因为它应该等同于“typeName == typeof(SomeKnownType).AssemblyQualifiedName”。 - nmclean
我看到的一个理论潜在风险是某人可以通过其他漏洞或文件上传功能等方式将程序集放置到服务器上,然后使用该代码来加载该程序集。不确定是否有可能执行任何操作,但可以查看Reflection-Only Assembly Loading以进行加载而不启用执行。 - LB2
@LB2 我的答案里也有这个。 - Daniel A. White
@DanielA.White 是的,我后来看到了(关于静态构造函数调用,并且发现它没有被执行)。因此,我编辑了评论,只保留了反射加载选项,以防对OP有所帮助。 - LB2
3个回答

20
不,这并不安全。如果之前没有加载过程序集,Type.GetType 将会加载它:

GetType 会导致加载 typeName 参数指定的程序集。

那么加载程序集有什么问题呢?除了像 Daniel 指出的使用额外内存外,.NET 程序集可以在加载时执行代码,尽管这种功能没有暴露给像 C# 和 VB.NET 这样的普通编译器。这些被称为 模块初始化器

模块初始化器方法在访问模块中定义的任何类型、方法或数据之前或之时执行。

只要加载程序集并检查其类型就足以使模块初始化器运行。

如果有人使用ilasm并编写原始MSIL,那么只要加载程序集并检查类型,就可以执行代码。这就是为什么我们需要Assembly.ReflectionOnlyLoad,以便在非可执行环境中安全地加载程序集。


我对此进行了更多思考并想到了几个案例。

考虑您的应用程序池设置为运行64位。现在想象一下,您的攻击者使用AJAX服务尝试加载一个仅适用于x86架构的程序集。例如,我的GAC中有一个叫做Microsoft.SqlServer.Replication的程序集仅适用于x86,没有AMD64的对应项。如果我要求您的服务加载该程序集,您会收到一个BadImageFormatException。根据您在加载程序集周围放置的守卫子句,未处理的异常可能会完全使您的AppPool崩溃


1
你不能用C#添加的代码,太好了!这是放置病毒的好方法,因为没有人能找到它 :( - BradleyDotNET
1
@BradleyDotNET - 这是一个令人困惑的陈述。您可以通过反汇编或反编译找到代码,这就是您在程序集中找到任何其他代码的方式。这与嵌入PE格式的古老PE病毒几乎没有什么不同。 - Erik Funkenbusch
但是您是否会在生产服务器上“拥有”这样的恶意汇编,如果是的话,任何将其带到那里的安全漏洞都会使这个安全漏洞毫无意义吧? - Servy
@Servy 我不会冒险。模块初始化程序很可能存在于任何使用托管C++编写的内容中(这也是CLR支持此功能的主要原因),它们可以很容易地进入GAC(例如,支持托管C++的.NET Framework程序集)。尽管它们可能做更无害的事情,但这可能足以使应用程序崩溃以拒绝服务。考虑到通过首先在反射上下文中加载程序集就可以相对容易地避免这个问题。 - vcsjones

4

如果库文件不在内存中,这可能会导致内存被占用。

我会使用一个 Dictionary<string, Type> 作为允许列表。

var whitelist = new Dictionary<string, Type>;
whitelist.Add("MyType", typeof(MyType));

使用您的字典仍需要基于用户定义的字符串创建“Type”对象,因此所有相同的问题仍然存在。 - Servy
@Daniel:你确定 Type.GetType 会调用静态构造函数吗? - Michael Liu
@MichaelLiu 我非常确定。 - Daniel A. White
3
@MichaelLiu,它没有起作用。我们刚刚测试过了。 - Servy

0

引用类型本身并没有固有的危险。在.NET中,信任是在程序集级别上进行的。如果没有可用的包含指定类型的程序集,调用将只返回null。因此,有人必须使该程序集对代码可用-程序集不会凭空出现。


在加载已经存在于磁盘上的程序集时是否存在危险?(假设攻击者无法上传任意程序集,但现有程序集可能存在安全漏洞。) - Michael Liu
1
@MichaelLiu 我无法想象为什么加载程序集会固有地触发代码运行。它只是将程序集加载到内存中。这不会执行任何操作。 - Servy
@Servy:我知道加载本地DLL将会执行DllMain中的代码。托管的程序集是否有所不同?Type.GetType是否会拒绝加载本地DLL? - Michael Liu
1
迈克尔,考虑一下。也许在ajax请求中暴露你的实现(类型名称)并不明智。也许更聪明的做法是传递一个枚举值,并让你的代码静态地评估它以找到类型,而不是动态地评估。然后你的问题就变成了一个非问题。 - Xavier J
1
@codenoire:确实。我正在审核别人的代码。 - Michael Liu
@Servy 加载程序集确实可以像vcsjones所示执行代码。因此,这肯定是一个可能的弱点。现在它本身可能不是安全问题,但允许攻击者写一些文件到磁盘(例如有界大小以避免DDOS问题)的弱点也不是。但是,将两者结合起来肯定是有问题的。最好从一开始就避免它。 - Voo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接