TL; DR WindowsIdentity
的{{1}}属性(例如,Token
)中包含的用户令牌是否可以欺骗:
someIdentity.Token
...将返回一个声称代表用户的实例,该用户实际上未经过身份验证,但var validated = new WindowsIdentity(someIdentity.Token);
设置IsAuthenticated
,有效true
和{{ 1}}属性等?
下面我对此进行了一些界定; 完全欺骗可能是不可能的。
全文:
在this answer中,Damien_The_Unbeliever巧妙地证明了我的某些代码可能会被误认为它在.Name
实例中有一个有效的经过身份验证的用户。长话短说,我的代码假设如果.User
是WindowsIdentity
的实例且Thread.CurrentPrincipal.Identity
是WindowsIdentity
,那么它代表了经过身份验证的用户,我可以依赖于SID在IsAuthorized
:
true
(这个代码使用线程而不是.User
是有原因的。)
他的代码欺骗(稍加修改):
WindowsIdentity identity = Thread.CurrentPrincipal == null
? null
: Thread.CurrentPrincipal.Identity as WindowsIdentity;
if (identity != null && identity.IsAuthenticated && !identity.IsAnonymous) {
// ...use and trust the SID in identity.User, the
// username in identity.Name, etc....
}
果然,如果你这样做然后调用上面的代码,我的代码就会被愚弄。 Kudos Damien。
因此,在真正的军备竞赛方式中,这是我修改后的代码,它会抓住恶搞并否认它:
WindowsIdentity.GetCurrent()
如您所见,它从提供的标识中获取var ident = WindowsIdentity.GetCurrent();
Thread.CurrentPrincipal = new WindowsPrincipal(ident);
var fakeSid = new SecurityIdentifier("S-1-3-0"/* E.g., some SID you want to trick me into believing is the real user */);
typeof(WindowsIdentity).GetField("m_user", BindingFlags.Instance | BindingFlags.NonPublic)
.SetValue(ident, fakeSid);
,并使用该标记创建 new WindowsIdentity identity = Thread.CurrentPrincipal == null
? null
: Thread.CurrentPrincipal.Identity as WindowsIdentity;
if (identity != null && identity.IsAuthenticated && !identity.IsAnonymous) {
var validated = new WindowsIdentity(identity.Token);
if (!validated.User.Equals(identity.User) || !validated.IsAuthenticated || validated.IsAnonymous) {
// Something fishy is going on, don't trust it
} else {
// Good! Use the validated one
identity = validated;
// ...use and trust the SID in identity.User, the
// username in identity.Name, etc....
}
}
实例。如果身份的SID匹配,我们继续,信任经过验证的身份。 (documentation for WindowsIdentity(IntPtr token)
表示Token
的初始值为WindowsIdentity
,但在我的测试中这是错误的,假设我使用有效的用户令牌创建了它。)
我可以看到可以欺骗的唯一方法是使用欺骗性的用户令牌,但仍然通过Windows对其进行的验证。这对我来说似乎不太可能。但是,对我来说,这是一个无知的领域。
边界:
我应该注意到,我只是在这里拍摄合理程度的单点登录安全性,尽我所能。如果恶意应用程序已经成功开始拦截系统调用/受到威胁的Windows本身,那么,我将无法做很多事情。正如Damien在对其他问题的评论中指出的那样,他可能会构建一个完全忽略强命名的主机容器(因此可能会给我一个完全假的IsAuthenticated
类型)。很公平。完美杀死。我只是不想像Damien亲切地展示的那样打开门。如果我发布了一个系统,并且很容易在现场进行黑客入侵,那我就很尴尬。 : - )
答案 0 :(得分:7)
为了证明这是可行的,让我们使用名为" Microsoft Fakes"的一个很酷的Visual Studio插件。 (名称本身意味着很多......)。
伪造本身与Visual Studio的测试功能有关,但它将证明这一点。您可以按照标准教程设置项目,并为System添加一个fakes程序集(实际上是mscorlib + system)
这是您在图书馆项目中的代码(我在各地都使用了异常,因为它在测试条件方面更容易......)。
namespace ClassLibrary1
{
public class Class1
{
public static void MyCheck()
{
WindowsIdentity identity = Thread.CurrentPrincipal == null
? null
: Thread.CurrentPrincipal.Identity as WindowsIdentity;
if (identity != null && identity.IsAuthenticated && !identity.IsAnonymous)
{
var validated = new WindowsIdentity(identity.Token);
if (!validated.User.Equals(identity.User) || !validated.IsAuthenticated || validated.IsAnonymous)
throw new Exception("Something fishy is going on, don't trust it");
else
throw new Exception("Good! Use the validated one. name is:" + validated.Name);
}
else
throw new Exception("not in");
}
}
}
这是测试项目中的测试代码:
namespace UnitTestProject1
{
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
using (ShimsContext.Create())
{
System.Security.Principal.Fakes.ShimWindowsIdentity.AllInstances.NameGet = (i) =>
{
return "Simon the hacker";
};
WindowsIdentity wi = WindowsIdentity.GetCurrent(); // this is the real one "Simon".
Thread.CurrentPrincipal = new WindowsPrincipal(wi);
Class1.MyCheck();
}
}
}
}
这是Visual Studio中的项目布局:
还要确保修改自动生成的mscorlib.fakes文件:
<Fakes xmlns="http://schemas.microsoft.com/fakes/2011/" Diagnostic="true" TargetFrameworkVersion="v4.6">
<Assembly Name="mscorlib" />
<ShimGeneration>
<Clear />
<Add Namespace="System.Security.Principal" />
</ShimGeneration>
</Fakes>
这意味着我想要整个System.Security.Principal
命名空间,我建议您为两个项目使用框架4.6并添加相应的TargetFrameworkVersion属性。
现在,当你运行测试时,这就是你所看到的:
好的,在您的特定场景中,我可能无法使用假货,但它所依赖的基础技术只是重新路由所有API(它实际上低于.NET,它被称为{ {3}})我相信并允许所有这些hackery。
总结一下:如果它在我的机器上运行,我可以破解它(除非我没有物理访问我的机器)。